/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include #include #include int smb_open_dsize_check = 0; /* * Client Request Description * ================================== ================================= * * UCHAR WordCount; Count of parameter words = 15 * UCHAR AndXCommand; Secondary (X) command; 0xFF = * none * UCHAR AndXReserved; Reserved (must be 0) * USHORT AndXOffset; Offset to next command WordCount * USHORT Flags; Additional information: bit set- * 0 - return additional info * 1 - exclusive oplock requested * 2 - batch oplock requested * USHORT DesiredAccess; File open mode * USHORT SearchAttributes; * USHORT FileAttributes; * UTIME CreationTime; Creation timestamp for file if it * gets created * USHORT OpenFunction; Action to take if file exists * ULONG AllocationSize; Bytes to reserve on create or * truncate * ULONG Reserved[2]; Must be 0 * USHORT ByteCount; Count of data bytes; min = 1 * UCHAR BufferFormat 0x04 * STRING FileName; * * Server Response Description * ================================== ================================= * * UCHAR WordCount; Count of parameter words = 15 * UCHAR AndXCommand; Secondary (X) command; 0xFF = * none * UCHAR AndXReserved; Reserved (must be 0) * USHORT AndXOffset; Offset to next command WordCount * USHORT Fid; File handle * USHORT FileAttributes; * UTIME LastWriteTime; * ULONG DataSize; Current file size * USHORT GrantedAccess; Access permissions actually * allowed * USHORT FileType; Type of file opened * USHORT DeviceState; State of the named pipe * USHORT Action; Action taken * ULONG ServerFid; Server unique file id * USHORT Reserved; Reserved (must be 0) * USHORT ByteCount; Count of data bytes = 0 * * DesiredAccess describes the access the client desires for the file (see * section 3.6 - Access Mode Encoding). * * OpenFunction specifies the action to be taken depending on whether or * not the file exists (see section 3.8 - Open Function Encoding). Action * * in the response specifies the action as a result of the Open request * (see section 3.9 - Open Action Encoding). * * SearchAttributes indicates the attributes that the file must have to be * found while searching to see if it exists. The encoding of this field * is described in the "File Attribute Encoding" section elsewhere in this * document. If SearchAttributes is zero then only normal files are * returned. If the system file, hidden or directory attributes are * specified then the search is inclusive -- both the specified type(s) of * files and normal files are returned. * * FileType returns the kind of resource actually opened: * * Name Value Description * ========================== ====== ================================== * * FileTypeDisk 0 Disk file or directory as defined * in the attribute field * FileTypeByteModePipe 1 Named pipe in byte mode * FileTypeMessageModePipe 2 Named pipe in message mode * FileTypePrinter 3 Spooled printer * FileTypeUnknown 0xFFFF Unrecognized resource type * * If bit0 of Flags is clear, the FileAttributes, LastWriteTime, DataSize, * FileType, and DeviceState have indeterminate values in the response. * * This SMB can request an oplock on the opened file. Oplocks are fully * described in the "Oplocks" section elsewhere in this document, and there * is also discussion of oplocks in the SMB_COM_LOCKING_ANDX SMB * description. Bit1 and bit2 of the Flags field are used to request * oplocks during open. * * The following SMBs may follow SMB_COM_OPEN_ANDX: * * SMB_COM_READ SMB_COM_READ_ANDX * SMB_COM_IOCTL */ /* * This message is sent to obtain a file handle for a data file. This * returned Fid is used in subsequent client requests such as read, write, * close, etc. * * Client Request Description * ================================== ================================= * * UCHAR WordCount; Count of parameter words = 2 * USHORT DesiredAccess; Mode - read/write/share * USHORT SearchAttributes; * USHORT ByteCount; Count of data bytes; min = 2 * UCHAR BufferFormat; 0x04 * STRING FileName[]; File name * * FileName is the fully qualified file name, relative to the root of the * share specified in the Tid field of the SMB header. If Tid in the SMB * header refers to a print share, this SMB creates a new file which will * be spooled to the printer when closed. In this case, FileName is * ignored. * * SearchAttributes specifies the type of file desired. The encoding is * described in the "File Attribute Encoding" section. * * DesiredAccess controls the mode under which the file is opened, and the * file will be opened only if the client has the appropriate permissions. * The encoding of DesiredAccess is discussed in the section entitled * "Access Mode Encoding". * * Server Response Description * ================================== ================================= * * UCHAR WordCount; Count of parameter words = 7 * USHORT Fid; File handle * USHORT FileAttributes; Attributes of opened file * UTIME LastWriteTime; Time file was last written * ULONG DataSize; File size * USHORT GrantedAccess; Access allowed * USHORT ByteCount; Count of data bytes = 0 * * Fid is the handle value which should be used for subsequent file * operations. * * FileAttributes specifies the type of file obtained. The encoding is * described in the "File Attribute Encoding" section. * * GrantedAccess indicates the access permissions actually allowed, and may * have one of the following values: * * 0 read-only * 1 write-only * 2 read/write * * File Handles (Fids) are scoped per client. A Pid may reference any Fid * established by itself or any other Pid on the client (so far as the * server is concerned). The actual accesses allowed through the Fid * depends on the open and deny modes specified when the file was opened * (see below). * * The MS-DOS compatibility mode of file open provides exclusion at the * client level. A file open in compatibility mode may be opened (also in * compatibility mode) any number of times for any combination of reading * and writing (subject to the user's permissions) by any Pid on the same * client. If the first client has the file open for writing, then the * file may not be opened in any way by any other client. If the first * client has the file open only for reading, then other clients may open * the file, in compatibility mode, for reading.. The above * notwithstanding, if the filename has an extension of .EXE, .DLL, .SYM, * or .COM other clients are permitted to open the file regardless of * read/write open modes of other compatibility mode opens. However, once * multiple clients have the file open for reading, no client is permitted * to open the file for writing and no other client may open the file in * any mode other than compatibility mode. * * The other file exclusion modes (Deny read/write, Deny write, Deny read, * Deny none) provide exclusion at the file level. A file opened in any * "Deny" mode may be opened again only for the accesses allowed by the * Deny mode (subject to the user's permissions). This is true regardless * of the identity of the second opener -a different client, a Pid from the * same client, or the Pid that already has the file open. For example, if * a file is open in "Deny write" mode a second open may only obtain read * permission to the file. * * Although Fids are available to all Pids on a client, Pids other than the * owner may not have the full access rights specified in the open mode by * the Fid's creator. If the open creating the Fid specified a deny mode, * then any Pid using the Fid, other than the creating Pid, will have only * those access rights determined by "anding" the open mode rights and the * deny mode rights, i.e., the deny mode is checked on all file accesses. * For example, if a file is opened for Read/Write in Deny write mode, then * other clients may only read the file and cannot write; if a file is * opened for Read in Deny read mode, then the other clients can neither * read nor write the file. */ smb_sdrc_t smb_pre_open(smb_request_t *sr) { struct open_param *op = &sr->arg.open; int rc; bzero(op, sizeof (sr->arg.open)); rc = smbsr_decode_vwv(sr, "ww", &op->omode, &op->fqi.fq_sattr); if (rc == 0) rc = smbsr_decode_data(sr, "%S", sr, &op->fqi.fq_path.pn_path); DTRACE_SMB_START(op__Open, smb_request_t *, sr); /* arg.open */ return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); } void smb_post_open(smb_request_t *sr) { DTRACE_SMB_DONE(op__Open, smb_request_t *, sr); } smb_sdrc_t smb_com_open(smb_request_t *sr) { struct open_param *op = &sr->arg.open; smb_ofile_t *of; uint32_t mtime_sec; uint32_t status; uint16_t file_attr; int rc; op->desired_access = smb_omode_to_amask(op->omode); op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.fq_path.pn_path); op->crtime.tv_sec = op->crtime.tv_nsec = 0; op->create_disposition = FILE_OPEN; op->create_options = FILE_NON_DIRECTORY_FILE; if (op->omode & SMB_DA_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (sr->smb_flg & SMB_FLAGS_OPLOCK) { if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY) op->op_oplock_level = SMB_OPLOCK_BATCH; else op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; } else { op->op_oplock_level = SMB_OPLOCK_NONE; } if (smb_open_dsize_check && op->dsize > UINT_MAX) { smbsr_error(sr, 0, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } /* * The real open call. Note: this gets attributes into * op->fqi.fq_fattr (SMB_AT_ALL). We need those below. */ status = smb_common_open(sr); if (status != NT_STATUS_SUCCESS) { smbsr_status(sr, status, 0, 0); return (SDRC_ERROR); } if (op->op_oplock_level != SMB_OPLOCK_NONE) { /* Oplock req. in op->op_oplock_level etc. */ smb1_oplock_acquire(sr, B_FALSE); } /* * NB: after the above smb_common_open() success, * we have a handle allocated (sr->fid_ofile). * If we don't return success, we must close it. */ of = sr->fid_ofile; if (op->op_oplock_level == SMB_OPLOCK_NONE) { sr->smb_flg &= ~(SMB_FLAGS_OPLOCK | SMB_FLAGS_OPLOCK_NOTIFY_ANY); } file_attr = op->dattr & FILE_ATTRIBUTE_MASK; mtime_sec = smb_time_gmt_to_local(sr, op->fqi.fq_fattr.sa_vattr.va_mtime.tv_sec); rc = smbsr_encode_result(sr, 7, 0, "bwwllww", 7, sr->smb_fid, file_attr, mtime_sec, (uint32_t)op->dsize, op->omode, (uint16_t)0); /* bcc */ if (rc == 0) return (SDRC_SUCCESS); smb_ofile_close(of, 0); return (SDRC_ERROR); } int smb_openx_enable_extended_response = 1; /* * smb_pre_open_andx * For compatibility with windows servers, the search attributes * specified in the request are ignored. */ smb_sdrc_t smb_pre_open_andx(smb_request_t *sr) { struct open_param *op = &sr->arg.open; uint16_t openx_flags; uint32_t alloc_size; uint32_t creation_time; uint16_t file_attr, sattr; int rc; bzero(op, sizeof (sr->arg.open)); rc = smbsr_decode_vwv(sr, "b.wwwwwlwll4.", &sr->andx_com, &sr->andx_off, &openx_flags, &op->omode, &sattr, &file_attr, &creation_time, &op->ofun, &alloc_size, &op->timeo); if (rc == 0) { rc = smbsr_decode_data(sr, "%u", sr, &op->fqi.fq_path.pn_path); op->dattr = file_attr; op->dsize = alloc_size; /* * The openx_flags use some "extended" flags that * happen to match some of the NtCreateX flags. */ if (openx_flags & NT_CREATE_FLAG_REQUEST_OPBATCH) op->op_oplock_level = SMB_OPLOCK_BATCH; else if (openx_flags & NT_CREATE_FLAG_REQUEST_OPLOCK) op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; else op->op_oplock_level = SMB_OPLOCK_NONE; if (openx_flags & NT_CREATE_FLAG_EXTENDED_RESPONSE) op->nt_flags |= NT_CREATE_FLAG_EXTENDED_RESPONSE; if ((creation_time != 0) && (creation_time != UINT_MAX)) op->crtime.tv_sec = smb_time_local_to_gmt(sr, creation_time); op->crtime.tv_nsec = 0; op->create_disposition = smb_ofun_to_crdisposition(op->ofun); } DTRACE_SMB_START(op__OpenX, smb_request_t *, sr); /* arg.open */ return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); } void smb_post_open_andx(smb_request_t *sr) { DTRACE_SMB_DONE(op__OpenX, smb_request_t *, sr); } smb_sdrc_t smb_com_open_andx(smb_request_t *sr) { struct open_param *op = &sr->arg.open; smb_attr_t *ap = &op->fqi.fq_fattr; smb_ofile_t *of; uint32_t status; uint32_t mtime_sec; uint16_t file_attr; int rc; op->desired_access = smb_omode_to_amask(op->omode); op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.fq_path.pn_path); if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) { smbsr_error(sr, 0, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } op->create_options = FILE_NON_DIRECTORY_FILE; if (op->omode & SMB_DA_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (smb_open_dsize_check && op->dsize > UINT_MAX) { smbsr_error(sr, 0, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } status = smb_common_open(sr); if (status != NT_STATUS_SUCCESS) { smbsr_status(sr, status, 0, 0); return (SDRC_ERROR); } if (op->op_oplock_level != SMB_OPLOCK_NONE) { /* Oplock req. in op->op_oplock_level etc. */ smb1_oplock_acquire(sr, B_FALSE); } /* * NB: after the above smb_common_open() success, * we have a handle allocated (sr->fid_ofile). * If we don't return success, we must close it. */ of = sr->fid_ofile; if (op->op_oplock_level != SMB_OPLOCK_NONE) op->action_taken |= SMB_OACT_OPLOCK; else op->action_taken &= ~SMB_OACT_OPLOCK; file_attr = op->dattr & FILE_ATTRIBUTE_MASK; mtime_sec = smb_time_gmt_to_local(sr, ap->sa_vattr.va_mtime.tv_sec); switch (sr->tid_tree->t_res_type & STYPE_MASK) { case STYPE_DISKTREE: case STYPE_PRINTQ: break; case STYPE_IPC: mtime_sec = 0; break; default: smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); goto errout; } if ((op->nt_flags & NT_CREATE_FLAG_EXTENDED_RESPONSE) != 0 && smb_openx_enable_extended_response != 0) { uint32_t MaxAccess = 0; if (of->f_node != NULL) { smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess); } MaxAccess |= of->f_granted_access; rc = smbsr_encode_result( sr, 19, 0, "bb.wwwllwwwwl2.llw", 19, /* word count (b) */ sr->andx_com, /* (b.) */ VAR_BCC, /* andx offset (w) */ sr->smb_fid, /* (w) */ file_attr, /* (w) */ mtime_sec, /* (l) */ (uint32_t)op->dsize, /* (l) */ op->omode, /* (w) */ op->ftype, /* (w) */ op->devstate, /* (w) */ op->action_taken, /* (w) */ 0, /* legacy fileid (l) */ /* reserved (2.) */ MaxAccess, /* (l) */ 0, /* guest access (l) */ 0); /* byte count (w) */ } else { rc = smbsr_encode_result( sr, 15, 0, "bb.wwwllwwwwl2.w", 15, /* word count (b) */ sr->andx_com, /* (b.) */ VAR_BCC, /* andx offset (w) */ sr->smb_fid, /* (w) */ file_attr, /* (w) */ mtime_sec, /* (l) */ (uint32_t)op->dsize, /* (l) */ op->omode, /* (w) */ op->ftype, /* (w) */ op->devstate, /* (w) */ op->action_taken, /* (w) */ 0, /* legacy fileid (l) */ /* reserved (2.) */ 0); /* byte count (w) */ } if (rc == 0) return (SDRC_SUCCESS); errout: smb_ofile_close(of, 0); return (SDRC_ERROR); } smb_sdrc_t smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa) { struct open_param *op = &sr->arg.open; uint32_t creation_time; uint32_t alloc_size; uint32_t ea_list_size; uint16_t flags; uint16_t file_attr; uint32_t status; int rc; bzero(op, sizeof (sr->arg.open)); rc = smb_mbc_decodef(&xa->req_param_mb, "%wwwwlwl10.u", sr, &flags, &op->omode, &op->fqi.fq_sattr, &file_attr, &creation_time, &op->ofun, &alloc_size, &op->fqi.fq_path.pn_path); if (rc != 0) return (SDRC_ERROR); /* * The data part of this transaction may contain an EA list. * See: SMB_FEA_LIST ExtendedAttributeList * * If we find a non-empty EA list payload, return the special * error that tells the caller this FS does not suport EAs. * * Note: the first word is the size of the whole data segment, * INCLUDING the size of that length word. That means if * the length word specifies a size less than four, it's * invalid (and probably a client trying something fishy). */ rc = smb_mbc_decodef(&xa->req_data_mb, "l", &ea_list_size); if (rc == 0 && ea_list_size > 4) { smbsr_status(sr, NT_STATUS_EAS_NOT_SUPPORTED, 0, 0); return (SDRC_ERROR); } if ((creation_time != 0) && (creation_time != UINT_MAX)) op->crtime.tv_sec = smb_time_local_to_gmt(sr, creation_time); op->crtime.tv_nsec = 0; op->dattr = file_attr; op->dsize = alloc_size; op->create_options = FILE_NON_DIRECTORY_FILE; op->desired_access = smb_omode_to_amask(op->omode); op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.fq_path.pn_path); op->create_disposition = smb_ofun_to_crdisposition(op->ofun); if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) op->create_disposition = FILE_CREATE; if (op->omode & SMB_DA_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (sr->smb_flg & SMB_FLAGS_OPLOCK) { if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY) op->op_oplock_level = SMB_OPLOCK_BATCH; else op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; } else { op->op_oplock_level = SMB_OPLOCK_NONE; } status = smb_common_open(sr); if (status != NT_STATUS_SUCCESS) { smbsr_status(sr, status, 0, 0); return (SDRC_ERROR); } if (op->op_oplock_level != SMB_OPLOCK_NONE) { /* Oplock req. in op->op_oplock_level etc. */ smb1_oplock_acquire(sr, B_FALSE); } if (op->op_oplock_level != SMB_OPLOCK_NONE) op->action_taken |= SMB_OACT_OPLOCK; else op->action_taken &= ~SMB_OACT_OPLOCK; file_attr = op->dattr & FILE_ATTRIBUTE_MASK; if (STYPE_ISIPC(sr->tid_tree->t_res_type)) op->dsize = 0; (void) smb_mbc_encodef(&xa->rep_param_mb, "wwllwwwwlwl", sr->smb_fid, file_attr, (uint32_t)0, /* creation time */ (uint32_t)op->dsize, op->omode, op->ftype, op->devstate, op->action_taken, op->fileid, (uint16_t)0, /* EA error offset */ (uint32_t)0); /* EA list length */ return (SDRC_SUCCESS); }