/* * Build an SMB2 error response. [MS-SMB2] 2.2.2 */ void smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc) { DWORD len; /* * The common dispatch code writes this when it * updates the SMB2 header before sending. */ sr->smb2_status = status; /* Rewind to the end of the SMB header. */ sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE; /* * NB: Must provide at least one byte of error data, * per [MS-SMB2] 2.2.2 */ if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) { (void) smb_mbc_encodef( &sr->reply, "wwlC", 9, /* StructSize */ /* w */ 0, /* reserved */ /* w */ len, /* l */ mbc); /* C */ } else { (void) smb_mbc_encodef( &sr->reply, "wwl.", 9, /* StructSize */ /* w */ 0, /* reserved */ /* w */ 0); /* l. */ } }
/* * FileFsVolumeInformation */ uint32_t smb2_qfs_volume(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; smb_node_t *snode; fsid_t fsid; uint32_t LabelLength; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); snode = tree->t_snode; fsid = SMB_NODE_FSID(snode); LabelLength = smb_wcequiv_strlen(tree->t_volume); /* * NT has the "supports objects" flag set to 1. */ (void) smb_mbc_encodef( &sr->raw_data, "qllb.U", 0LL, /* Volume creation time (q) */ fsid.val[0], /* serial no. (l) */ LabelLength, /* (l) */ 0, /* Supports objects (b) */ /* reserved (.) */ tree->t_volume); /* (U) */ return (0); }
/* * FilePipeInformation */ static uint32_t smb2_qif_pipe(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; uint32_t pipe_mode; uint32_t nonblock; switch (of->f_ftype) { case SMB_FTYPE_BYTE_PIPE: pipe_mode = 0; /* FILE_PIPE_BYTE_STREAM_MODE */ break; case SMB_FTYPE_MESG_PIPE: pipe_mode = 1; /* FILE_PIPE_MESSAGE_MODE */ break; case SMB_FTYPE_DISK: case SMB_FTYPE_PRINTER: default: return (NT_STATUS_INVALID_PARAMETER); } nonblock = 0; /* XXX todo: Get this from the pipe handle. */ (void) smb_mbc_encodef( &sr->raw_data, "ll", pipe_mode, nonblock); return (0); }
/* * FileFsControlInformation */ uint32_t smb2_qfs_control(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) { /* * Strange error per. [MS-FSCC 2.5.2] * which means quotas not supported. */ return (NT_STATUS_VOLUME_NOT_UPGRADED); } (void) smb_mbc_encodef( &sr->raw_data, "qqqqqll", 0, /* free space start filtering - MUST be 0 */ 0, /* free space threshold - MUST be 0 */ 0, /* free space stop filtering - MUST be 0 */ SMB_QUOTA_UNLIMITED, /* default quota threshold */ SMB_QUOTA_UNLIMITED, /* default quota limit */ FILE_VC_QUOTA_ENFORCE, /* fs control flag */ 0); /* pad bytes */ return (0); }
/* * Prepare a response with V3/V4 referral format. * * For more details, see comments for smb_dfs_encode_refv2() or see * MS-DFSC specification. */ static uint32_t smb_dfs_encode_refv3x(smb_request_t *sr, mbuf_chain_t *mbc, dfs_info_t *referrals, uint16_t ver) { _NOTE(ARGUNUSED(sr)) uint16_t entsize, rep_bufsize, hdrsize; uint16_t server_type; uint16_t flags = 0; uint16_t path_offs, altpath_offs, netpath_offs; uint16_t targetsz, total_targetsz = 0; uint16_t dfs_pathsz; uint16_t r; hdrsize = (ver == DFS_REFERRAL_V3) ? DFS_REFV3_ENTSZ : DFS_REFV4_ENTSZ; rep_bufsize = MBC_MAXBYTES(mbc); dfs_pathsz = smb_wcequiv_strlen(referrals->i_uncpath) + 2; entsize = hdrsize + dfs_pathsz + dfs_pathsz + smb_dfs_referrals_unclen(referrals, 0); if (entsize > rep_bufsize) { /* need room for at least one referral */ return (NT_STATUS_BUFFER_OVERFLOW); } server_type = (referrals->i_type == DFS_OBJECT_ROOT) ? DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT; rep_bufsize -= entsize; for (r = 0; r < referrals->i_ntargets; r++) { path_offs = (referrals->i_ntargets - r) * hdrsize; altpath_offs = path_offs + dfs_pathsz; netpath_offs = altpath_offs + dfs_pathsz + total_targetsz; targetsz = smb_dfs_referrals_unclen(referrals, r); if (r != 0) { entsize = hdrsize + targetsz; if (entsize > rep_bufsize) /* silently drop targets that do not fit */ break; rep_bufsize -= entsize; flags = 0; } else if (ver == DFS_REFERRAL_V4) { flags = DFS_ENTFLG_T; } (void) smb_mbc_encodef(mbc, "wwwwlwww16.", ver, hdrsize, server_type, flags, referrals->i_timeout, path_offs, altpath_offs, netpath_offs); total_targetsz += targetsz; } smb_dfs_encode_targets(mbc, referrals); return (NT_STATUS_SUCCESS); }
smb_sdrc_t smb_com_echo(struct smb_request *sr) { unsigned short necho; unsigned short nbytes; unsigned short i; struct mbuf_chain reply; char *data; if (smbsr_decode_vwv(sr, "w", &necho) != 0) return (SDRC_ERROR); nbytes = sr->smb_bcc; data = smb_srm_zalloc(sr, nbytes); if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data)) return (SDRC_ERROR); for (i = 1; i <= necho; ++i) { MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); (void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, sr->smb_pid_high, sr->smb_sig, sr->smb_tid, sr->smb_pid, sr->smb_uid, sr->smb_mid); (void) smb_mbc_encodef(&reply, "bww#c", 1, i, nbytes, nbytes, data); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, &reply); (void) smb_session_send(sr->session, 0, &reply); } return (SDRC_NO_REPLY); }
/* * FileAttributeTagInformation * * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the * second dword should be the reparse tag. Otherwise * the tag value should be set to zero. * We don't support reparse points, so we set the tag * to zero. */ static uint32_t smb2_qif_tags(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) (void) smb_mbc_encodef( &sr->raw_data, "ll", 0, 0); return (0); }
/* * Encodes DFS path, and target strings which come after fixed header * entries. * * Windows 2000 and earlier set the DFSAlternatePathOffset to point to * an 8.3 string representation of the string pointed to by * DFSPathOffset if it is not a legal 8.3 string. Otherwise, if * DFSPathOffset points to a legal 8.3 string, DFSAlternatePathOffset * points to a separate copy of the same string. Windows Server 2003, * Windows Server 2008 and Windows Server 2008 R2 set the * DFSPathOffset and DFSAlternatePathOffset fields to point to separate * copies of the identical string. * * Following Windows 2003 and later here. */ static void smb_dfs_encode_targets(mbuf_chain_t *mbc, dfs_info_t *referrals) { char *target; int r; (void) smb_mbc_encodef(mbc, "UU", referrals->i_uncpath, referrals->i_uncpath); target = kmem_alloc(MAXPATHLEN, KM_SLEEP); for (r = 0; r < referrals->i_ntargets; r++) { (void) snprintf(target, MAXPATHLEN, "\\%s\\%s", referrals->i_targets[r].t_server, referrals->i_targets[r].t_share); (void) smb_mbc_encodef(mbc, "U", target); } kmem_free(target, MAXPATHLEN); }
/* * FileAccessInformation */ static uint32_t smb2_qif_access(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; (void) smb_mbc_encodef( &sr->raw_data, "l", of->f_granted_access); return (0); }
/* * FileInternalInformation * See also: * SMB_FILE_INTERNAL_INFORMATION */ static uint32_t smb2_qif_internal(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; ASSERT((sa->sa_mask & SMB_AT_NODEID) == SMB_AT_NODEID); (void) smb_mbc_encodef( &sr->raw_data, "q", sa->sa_vattr.va_nodeid); /* q */ return (0); }
/* * FileFsAttributeInformation */ uint32_t smb2_qfs_attr(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; char *fsname; uint32_t namelen; uint32_t FsAttr; /* This call is OK on all tree types. */ switch (tree->t_res_type & STYPE_MASK) { case STYPE_IPC: fsname = "PIPE"; break; case STYPE_DISKTREE: fsname = "NTFS"; /* A lie, but compatible... */ break; case STYPE_PRINTQ: case STYPE_DEVICE: default: /* gcc -Wuninitialized */ return (NT_STATUS_INVALID_PARAMETER); } namelen = smb_wcequiv_strlen(fsname); /* * Todo: Store the FsAttributes in the tree object, * then just return that directly here. */ FsAttr = FILE_CASE_PRESERVED_NAMES; if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK) FsAttr |= FILE_UNICODE_ON_DISK; if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS) FsAttr |= FILE_PERSISTENT_ACLS; if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0) FsAttr |= FILE_CASE_SENSITIVE_SEARCH; if (tree->t_flags & SMB_TREE_STREAMS) FsAttr |= FILE_NAMED_STREAMS; if (tree->t_flags & SMB_TREE_QUOTA) FsAttr |= FILE_VOLUME_QUOTAS; if (tree->t_flags & SMB_TREE_SPARSE) FsAttr |= FILE_SUPPORTS_SPARSE_FILES; (void) smb_mbc_encodef( &sr->raw_data, "lllU", FsAttr, MAXNAMELEN-1, namelen, fsname); return (0); }
/* * FileNameInformation * See also: * SMB_QUERY_FILE_NAME_INFO * SMB_FILE_NAME_INFORMATION */ static uint32_t smb2_qif_name(smb_request_t *sr, smb_queryinfo_t *qi) { ASSERT(qi->qi_namelen > 0); (void) smb_mbc_encodef( &sr->raw_data, "llU", 0, /* FileIndex (l) */ qi->qi_namelen, /* l */ qi->qi_name); /* U */ return (0); }
/* * FileCompressionInformation * XXX: For now, just say "not compressed". */ static uint32_t smb2_qif_compr(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; uint16_t CompressionFormat = 0; /* COMPRESSION_FORMAT_NONE */ ASSERT(sa->sa_mask & SMB_AT_SIZE); (void) smb_mbc_encodef( &sr->raw_data, "qw6.", sa->sa_vattr.va_size, /* q */ CompressionFormat); /* w */ return (0); }
static void smb_dfs_encode_hdr(mbuf_chain_t *mbc, dfs_info_t *referrals) { uint16_t path_consumed; uint32_t flags; path_consumed = smb_wcequiv_strlen(referrals->i_uncpath); flags = DFS_HDRFLG_S; if (referrals->i_type == DFS_OBJECT_ROOT) flags |= DFS_HDRFLG_R; /* Fill rep_param_mb in SMB1 caller. */ (void) smb_mbc_encodef(mbc, "wwl", path_consumed, referrals->i_ntargets, flags); }
/* * FilePositionInformation */ static uint32_t smb2_qif_position(smb_request_t *sr, smb_queryinfo_t *qi) { _NOTE(ARGUNUSED(qi)) smb_ofile_t *of = sr->fid_ofile; uint64_t pos; mutex_enter(&of->f_mutex); pos = of->f_seek_pos; mutex_exit(&of->f_mutex); (void) smb_mbc_encodef( &sr->raw_data, "q", pos); return (0); }
int smb2_encode_header(smb_request_t *sr, boolean_t overwrite) { uint64_t ssnid = sr->smb_uid; uint64_t pid_tid_aid; /* pid+tid, or async id */ int rc; if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { pid_tid_aid = sr->smb2_async_id; } else { pid_tid_aid = sr->smb_pid | ((uint64_t)sr->smb_tid) << 32; } if (overwrite) { rc = smb_mbc_poke(&sr->reply, sr->smb2_reply_hdr, "Nwwlwwllqqq16c", SMB2_HDR_SIZE, /* w */ sr->smb2_credit_charge, /* w */ sr->smb2_status, /* l */ sr->smb2_cmd_code, /* w */ sr->smb2_credit_response, /* w */ sr->smb2_hdr_flags, /* l */ sr->smb2_next_reply, /* l */ sr->smb2_messageid, /* q */ pid_tid_aid, /* q */ ssnid, /* q */ sr->smb2_sig); /* 16c */ } else { rc = smb_mbc_encodef(&sr->reply, "Nwwlwwllqqq16c", SMB2_HDR_SIZE, /* w */ sr->smb2_credit_charge, /* w */ sr->smb2_status, /* l */ sr->smb2_cmd_code, /* w */ sr->smb2_credit_response, /* w */ sr->smb2_hdr_flags, /* l */ sr->smb2_next_reply, /* l */ sr->smb2_messageid, /* q */ pid_tid_aid, /* q */ ssnid, /* q */ sr->smb2_sig); /* 16c */ } return (rc); }
/* * FileStreamInformation */ static uint32_t smb2_qif_stream(smb_request_t *sr, smb_queryinfo_t *qi) { smb_ofile_t *of = sr->fid_ofile; smb_attr_t *attr = &qi->qi_attr; uint32_t status; ASSERT((attr->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD); if (of->f_ftype != SMB_FTYPE_DISK) { (void) smb_mbc_encodef( &sr->raw_data, "l", 0); return (0); } status = smb_query_stream_info(sr, &sr->raw_data, qi); return (status); }
static uint32_t smb_dfs_encode_refv1(smb_request_t *sr, mbuf_chain_t *mbc, dfs_info_t *referrals) { _NOTE(ARGUNUSED(sr)) uint16_t entsize, rep_bufsize; uint16_t server_type; uint16_t flags = 0; uint16_t r; char *target; rep_bufsize = MBC_MAXBYTES(mbc); server_type = (referrals->i_type == DFS_OBJECT_ROOT) ? DFS_SRVTYPE_ROOT : DFS_SRVTYPE_NONROOT; target = kmem_alloc(MAXPATHLEN, KM_SLEEP); for (r = 0; r < referrals->i_ntargets; r++) { (void) snprintf(target, MAXPATHLEN, "\\%s\\%s", referrals->i_targets[r].t_server, referrals->i_targets[r].t_share); entsize = DFS_REFV1_ENTSZ + smb_wcequiv_strlen(target) + 2; if (entsize > rep_bufsize) break; (void) smb_mbc_encodef(mbc, "wwwwU", DFS_REFERRAL_V1, entsize, server_type, flags, target); rep_bufsize -= entsize; } kmem_free(target, MAXPATHLEN); /* * Need room for at least one entry. * Windows will silently drop targets that do not fit in * the response buffer. */ if (r == 0) { return (NT_STATUS_BUFFER_OVERFLOW); } return (NT_STATUS_SUCCESS); }
/* * FileStandardInformation * See also: * SMB_QUERY_FILE_STANDARD_INFO * SMB_FILE_STANDARD_INFORMATION */ static uint32_t smb2_qif_standard(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; ASSERT((sa->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD); (void) smb_mbc_encodef( &sr->raw_data, "qqlbbw", sa->sa_allocsz, /* q */ sa->sa_vattr.va_size, /* q */ sa->sa_vattr.va_nlink, /* l */ qi->qi_delete_on_close, /* b */ qi->qi_isdir, /* b */ 0); /* reserved */ /* w */ return (0); }
/* * FileBasicInformation * See also: * case SMB_QUERY_FILE_BASIC_INFO: * case SMB_FILE_BASIC_INFORMATION: */ static uint32_t smb2_qif_basic(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; ASSERT((sa->sa_mask & SMB_AT_BASIC) == SMB_AT_BASIC); (void) smb_mbc_encodef( &sr->raw_data, "TTTTll", &sa->sa_crtime, /* T */ &sa->sa_vattr.va_atime, /* T */ &sa->sa_vattr.va_mtime, /* T */ &sa->sa_vattr.va_ctime, /* T */ sa->sa_dosattr, /* l */ 0); /* reserved */ /* l */ return (0); }
/* * FileNetworkOpenInformation */ static uint32_t smb2_qif_opens(smb_request_t *sr, smb_queryinfo_t *qi) { smb_attr_t *sa = &qi->qi_attr; (void) smb_mbc_encodef( &sr->raw_data, "TTTTqqll", &sa->sa_crtime, /* T */ &sa->sa_vattr.va_atime, /* T */ &sa->sa_vattr.va_mtime, /* T */ &sa->sa_vattr.va_ctime, /* T */ sa->sa_allocsz, /* q */ sa->sa_vattr.va_size, /* q */ sa->sa_dosattr, /* l */ 0); /* reserved */ /* l */ return (0); }
/* * Compose an SMB1 Oplock Break Notification packet, including * the SMB1 header and everything, in sr->reply. * The caller will send it and free the request. */ void smb1_oplock_break_notification(smb_request_t *sr, uint8_t brk) { smb_ofile_t *ofile = sr->fid_ofile; uint16_t fid; uint8_t lock_type; uint8_t oplock_level; switch (brk) { default: ASSERT(0); /* FALLTHROUGH */ case SMB_OPLOCK_BREAK_TO_NONE: oplock_level = 0; break; case SMB_OPLOCK_BREAK_TO_LEVEL_II: oplock_level = 1; break; } sr->smb_com = SMB_COM_LOCKING_ANDX; sr->smb_tid = ofile->f_tree->t_tid; sr->smb_pid = 0xFFFF; sr->smb_uid = 0; sr->smb_mid = 0xFFFF; fid = ofile->f_fid; lock_type = LOCKING_ANDX_OPLOCK_RELEASE; (void) smb_mbc_encodef( &sr->reply, "Mb19.wwwwbb3.wbb10.", /* "\xffSMB" M */ sr->smb_com, /* b */ /* status, flags, signature 19. */ sr->smb_tid, /* w */ sr->smb_pid, /* w */ sr->smb_uid, /* w */ sr->smb_mid, /* w */ 8, /* word count b */ 0xFF, /* AndX cmd b */ /* AndX reserved, offset 3. */ fid, lock_type, oplock_level); }
/* * FileFsDeviceInformation */ uint32_t smb2_qfs_device(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; uint32_t DeviceType; uint32_t Characteristics; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); DeviceType = FILE_DEVICE_DISK; Characteristics = FILE_DEVICE_IS_MOUNTED; (void) smb_mbc_encodef( &sr->raw_data, "ll", DeviceType, Characteristics); return (0); }
/* * This is to respond to the nt_transact_ioctl to either respond with the * number of snapshots, or to respond with the list. It needs to be sorted * before the reply. If the the max data bytes to return is * SMB_VSS_COUNT_SIZE, then all that is requested is the count, otherwise * return the count and the list of @GMT tokens (one token for each * snapshot). */ uint32_t smb_vss_enum_snapshots(smb_request_t *sr, smb_fsctl_t *fsctl) { uint32_t count = 0; char *root_path; uint32_t status = NT_STATUS_SUCCESS; smb_node_t *tnode; smb_gmttoken_response_t snaps; ASSERT(sr->tid_tree); ASSERT(sr->tid_tree->t_snode); if (fsctl->MaxOutputResp < SMB_VSS_COUNT_SIZE) return (NT_STATUS_INVALID_PARAMETER); tnode = sr->tid_tree->t_snode; root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0) return (NT_STATUS_INVALID_PARAMETER); if (fsctl->MaxOutputResp == SMB_VSS_COUNT_SIZE) { count = smb_vss_get_count(sr->tid_tree, root_path); if (smb_mbc_encodef(fsctl->out_mbc, "lllw", count, 0, (count * SMB_VSS_GMT_NET_SIZE(sr) + smb_ascii_or_unicode_null_len(sr)), 0) != 0) { status = NT_STATUS_INVALID_PARAMETER; } } else { count = fsctl->MaxOutputResp / SMB_VSS_GMT_NET_SIZE(sr); smb_vss_get_snapshots(sr->tid_tree, root_path, count, &snaps); status = smb_vss_encode_gmttokens(sr, fsctl, count, &snaps); smb_vss_get_snapshots_free(&snaps); } kmem_free(root_path, MAXPATHLEN); return (status); }
/* * FileFsSizeInformation */ uint32_t smb2_qfs_size(smb_request_t *sr) { smb_fssize_t fssize; smb_tree_t *tree = sr->tid_tree; int rc; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); rc = smb_fssize(sr, &fssize); if (rc) return (smb_errno2status(rc)); (void) smb_mbc_encodef( &sr->raw_data, "qqll", fssize.fs_caller_units, fssize.fs_caller_avail, fssize.fs_sectors_per_unit, fssize.fs_bytes_per_sector); return (0); }
/* * FileAlternateNameInformation * See also: * SMB_QUERY_FILE_ALT_NAME_INFO * SMB_FILE_ALT_NAME_INFORMATION */ static uint32_t smb2_qif_altname(smb_request_t *sr, smb_queryinfo_t *qi) { smb_ofile_t *of = sr->fid_ofile; ASSERT(qi->qi_namelen > 0); ASSERT(qi->qi_attr.sa_mask & SMB_AT_NODEID); if (of->f_ftype != SMB_FTYPE_DISK) return (NT_STATUS_OBJECT_NAME_NOT_FOUND); if ((of->f_tree->t_flags & SMB_TREE_SHORTNAMES) == 0) return (NT_STATUS_OBJECT_NAME_NOT_FOUND); /* fill in qi->qi_shortname */ smb_query_shortname(of->f_node, qi); (void) smb_mbc_encodef( &sr->raw_data, "%lU", sr, smb_wcequiv_strlen(qi->qi_shortname), qi->qi_shortname); return (0); }
/* * See [MS-DFSC] for details about this command */ smb_sdrc_t smb_com_trans2_get_dfs_referral(smb_request_t *sr, smb_xa_t *xa) { smb_fsctl_t fsctl; uint32_t status; uint16_t doserr; /* This request is only valid over IPC connections */ if (!STYPE_ISIPC(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } fsctl.CtlCode = FSCTL_DFS_GET_REFERRALS; fsctl.InputCount = xa->smb_tpscnt; fsctl.OutputCount = 0; fsctl.MaxOutputResp = xa->smb_mdrcnt; fsctl.in_mbc = &xa->req_param_mb; fsctl.out_mbc = &xa->rep_data_mb; status = smb_dfs_get_referrals(sr, &fsctl); /* Out param is the API-level return code. */ doserr = smb_status2doserr(status); (void) smb_mbc_encodef(&xa->rep_param_mb, "w", doserr); #if 0 /* XXX - Is API-level return code enough? */ if (status) { smbsr_error(sr, NT_STATUS_NO_SUCH_DEVICE, 0, 0); return (SDRC_ERROR); } #endif return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_echo(struct smb_request *sr) { unsigned short necho; unsigned short nbytes; unsigned short i; struct mbuf_chain reply; char *data; uint16_t pid_hi, pid_lo; pid_hi = sr->smb_pid >> 16; pid_lo = (uint16_t)sr->smb_pid; if (smbsr_decode_vwv(sr, "w", &necho) != 0) return (SDRC_ERROR); /* * Don't let the client fool us into doing * more work than is "reasonable". */ if (necho > smb_max_echo) necho = smb_max_echo; nbytes = sr->smb_bcc; data = smb_srm_zalloc(sr, nbytes); if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data)) return (SDRC_ERROR); for (i = 1; i <= necho; ++i) { /* * According to [MS-CIFS] 3.3.5.32 echo is * subject to cancellation. */ if (sr->sr_state != SMB_REQ_STATE_ACTIVE) break; MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); (void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, pid_hi, sr->smb_sig, sr->smb_tid, pid_lo, sr->smb_uid, sr->smb_mid); (void) smb_mbc_encodef(&reply, "bww#c", 1, i, nbytes, nbytes, data); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, &reply); (void) smb_session_send(sr->session, 0, &reply); delay(MSEC_TO_TICK(100)); } return (SDRC_NO_REPLY); }
void smb_reply_notify_change_request(smb_request_t *sr) { smb_node_t *node; smb_srqueue_t *srq; int total_bytes, n_setup, n_param, n_data; int param_off, param_pad, data_off, data_pad; struct smb_xa *xa; smb_error_t err; SMB_REQ_VALID(sr); srq = sr->session->s_srqueue; smb_srqueue_waitq_to_runq(srq); xa = sr->r_xa; node = sr->sr_ncr.nc_node; if (--node->waiting_event == 0) { node->flags &= ~(NODE_FLAGS_NOTIFY_CHANGE | NODE_FLAGS_CHANGED); smb_fem_fcn_uninstall(node); } mutex_enter(&sr->sr_mutex); switch (sr->sr_state) { case SMB_REQ_STATE_EVENT_OCCURRED: sr->sr_state = SMB_REQ_STATE_ACTIVE; /* many things changed */ (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0L); /* setup the NT transact reply */ n_setup = MBC_LENGTH(&xa->rep_setup_mb); n_param = MBC_LENGTH(&xa->rep_param_mb); n_data = MBC_LENGTH(&xa->rep_data_mb); n_setup = (n_setup + 1) / 2; /* Convert to setup words */ param_pad = 1; /* must be one */ param_off = param_pad + 32 + 37 + (n_setup << 1) + 2; /* Pad to 4 bytes */ data_pad = (4 - ((param_off + n_param) & 3)) % 4; /* Param off from hdr */ data_off = param_off + n_param + data_pad; total_bytes = param_pad + n_param + data_pad + n_data; (void) smbsr_encode_result(sr, 18+n_setup, total_bytes, "b3.llllllllbCw#.C#.C", 18 + n_setup, /* wct */ n_param, /* Total Parameter Bytes */ n_data, /* Total Data Bytes */ n_param, /* Total Parameter Bytes this buffer */ param_off, /* Param offset from header start */ 0, /* Param displacement */ n_data, /* Total Data Bytes this buffer */ data_off, /* Data offset from header start */ 0, /* Data displacement */ n_setup, /* suwcnt */ &xa->rep_setup_mb, /* setup[] */ total_bytes, /* Total data bytes */ param_pad, &xa->rep_param_mb, data_pad, &xa->rep_data_mb); break; case SMB_REQ_STATE_CANCELED: err.status = NT_STATUS_CANCELLED; err.errcls = ERRDOS; err.errcode = ERROR_OPERATION_ABORTED; smbsr_set_error(sr, &err); (void) smb_mbc_encodef(&sr->reply, "bwbw", (short)0, 0L, (short)0, 0L); sr->smb_wct = 0; sr->smb_bcc = 0; break; default: ASSERT(0); } mutex_exit(&sr->sr_mutex); /* Setup the header */ (void) smb_mbc_poke(&sr->reply, 0, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, sr->smb_pid_high, sr->smb_sig, sr->smb_tid, sr->smb_pid, sr->smb_uid, sr->smb_mid); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, NULL); /* send the reply */ DTRACE_PROBE1(ncr__reply, struct smb_request *, sr) (void) smb_session_send(sr->session, 0, &sr->reply); smbsr_cleanup(sr); mutex_enter(&sr->sr_mutex); sr->sr_state = SMB_REQ_STATE_COMPLETED; mutex_exit(&sr->sr_mutex); smb_srqueue_runq_exit(srq); smb_request_free(sr); }
smb_sdrc_t smb2_read(smb_request_t *sr) { smb_ofile_t *of = NULL; smb_vdb_t *vdb = NULL; struct mbuf *m = NULL; uint16_t StructSize; uint8_t Padding; uint8_t DataOff; uint32_t Length; uint64_t Offset; smb2fid_t smb2fid; uint32_t MinCount; uint32_t Channel; uint32_t Remaining; uint16_t ChanInfoOffset; uint16_t ChanInfoLength; uint32_t XferCount; uint32_t status; int rc = 0; /* * SMB2 Read request */ rc = smb_mbc_decodef( &sr->smb_data, "wb.lqqqlllww", &StructSize, /* w */ &Padding, /* b. */ &Length, /* l */ &Offset, /* q */ &smb2fid.persistent, /* q */ &smb2fid.temporal, /* q */ &MinCount, /* l */ &Channel, /* l */ &Remaining, /* l */ &ChanInfoOffset, /* w */ &ChanInfoLength); /* w */ if (rc) return (SDRC_ERROR); if (StructSize != 49) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); if (status) { smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } of = sr->fid_ofile; if (Length > smb2_max_rwsize) { smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER); return (SDRC_SUCCESS); } if (MinCount > Length) MinCount = Length; /* This is automatically free'd. */ vdb = smb_srm_zalloc(sr, sizeof (*vdb)); vdb->vdb_tag = 0; vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0]; vdb->vdb_uio.uio_iovcnt = MAX_IOVEC; vdb->vdb_uio.uio_resid = Length; vdb->vdb_uio.uio_loffset = (offset_t)Offset; vdb->vdb_uio.uio_segflg = UIO_SYSSPACE; vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT; sr->raw_data.max_bytes = Length; m = smb_mbuf_allocate(&vdb->vdb_uio); switch (of->f_tree->t_res_type & STYPE_MASK) { case STYPE_DISKTREE: if (!smb_node_is_dir(of->f_node)) { /* Check for conflicting locks. */ rc = smb_lock_range_access(sr, of->f_node, Offset, Length, B_FALSE); if (rc) { rc = ERANGE; break; } } rc = smb_fsop_read(sr, of->f_cr, of->f_node, &vdb->vdb_uio); break; case STYPE_IPC: rc = smb_opipe_read(sr, &vdb->vdb_uio); break; default: case STYPE_PRINTQ: rc = EACCES; break; } /* How much data we moved. */ XferCount = Length - vdb->vdb_uio.uio_resid; sr->raw_data.max_bytes = XferCount; smb_mbuf_trim(m, XferCount); MBC_ATTACH_MBUF(&sr->raw_data, m); /* * Checking the error return _after_ dealing with * the returned data so that if m was allocated, * it will be free'd via sr->raw_data cleanup. */ if (rc) { smb2sr_put_errno(sr, rc); return (SDRC_SUCCESS); } /* * SMB2 Read reply */ DataOff = SMB2_HDR_SIZE + 16; rc = smb_mbc_encodef( &sr->reply, "wb.lllC", 17, /* StructSize */ /* w */ DataOff, /* b. */ XferCount, /* l */ 0, /* DataRemaining */ /* l */ 0, /* reserved */ /* l */ &sr->raw_data); /* C */ if (rc) return (SDRC_ERROR); mutex_enter(&of->f_mutex); of->f_seek_pos = Offset + XferCount; mutex_exit(&of->f_mutex); return (SDRC_SUCCESS); }