smb_sdrc_t smb_com_query_information(smb_request_t *sr) { uint16_t infolev = SMB_QUERY_INFORMATION; if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_query_by_path(sr, NULL, infolev) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * smb_com_trans2_query_path_information */ smb_sdrc_t smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa) { uint16_t infolev; smb_fqi_t *fqi = &sr->arg.dirop.fqi; if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u", sr, &infolev, &fqi->fq_path.pn_path) != 0) return (SDRC_ERROR); if (smb_query_by_path(sr, xa, infolev) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * 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); }
int smb_pathname_reduce( smb_request_t *sr, cred_t *cred, const char *path, smb_node_t *share_root_node, smb_node_t *cur_node, smb_node_t **dir_node, char *last_component) { smb_node_t *root_node; pathname_t ppn; char *usepath; int lookup_flags = FOLLOW; int trailing_slash = 0; int err = 0; int len; smb_node_t *vss_cur_node; smb_node_t *vss_root_node; smb_node_t *local_cur_node; smb_node_t *local_root_node; ASSERT(dir_node); ASSERT(last_component); *dir_node = NULL; *last_component = '\0'; vss_cur_node = NULL; vss_root_node = NULL; if (sr && sr->tid_tree) { if (STYPE_ISIPC(sr->tid_tree->t_res_type)) return (EACCES); } if (SMB_TREE_IS_CASEINSENSITIVE(sr)) lookup_flags |= FIGNORECASE; if (path == NULL) return (EINVAL); if (*path == '\0') return (ENOENT); usepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) { kmem_free(usepath, MAXPATHLEN); return (ENAMETOOLONG); } (void) strsubst(usepath, '\\', '/'); if (share_root_node) root_node = share_root_node; else root_node = sr->sr_server->si_root_smb_node; if (cur_node == NULL) cur_node = root_node; local_cur_node = cur_node; local_root_node = root_node; if (SMB_TREE_IS_DFSROOT(sr) && (sr->smb_flg2 & SMB_FLAGS2_DFS)) { err = smb_pathname_dfs_preprocess(sr, usepath, MAXPATHLEN); if (err != 0) { kmem_free(usepath, MAXPATHLEN); return (err); } len = strlen(usepath); } if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) { err = smb_vss_lookup_nodes(sr, root_node, cur_node, usepath, &vss_cur_node, &vss_root_node); if (err != 0) { kmem_free(usepath, MAXPATHLEN); return (err); } len = strlen(usepath); local_cur_node = vss_cur_node; local_root_node = vss_root_node; } if (usepath[len - 1] == '/') trailing_slash = 1; (void) strcanon(usepath, "/"); (void) pn_alloc(&ppn); if ((err = pn_set(&ppn, usepath)) != 0) { (void) pn_free(&ppn); kmem_free(usepath, MAXPATHLEN); if (vss_cur_node != NULL) (void) smb_node_release(vss_cur_node); if (vss_root_node != NULL) (void) smb_node_release(vss_root_node); return (err); } /* * If a path does not have a trailing slash, strip off the * last component. (We only need to return an smb_node for * the second to last component; a name is returned for the * last component.) */ if (trailing_slash) { (void) strlcpy(last_component, ".", MAXNAMELEN); } else { (void) pn_setlast(&ppn); (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); ppn.pn_path[0] = '\0'; } if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) { smb_node_ref(local_cur_node); *dir_node = local_cur_node; } else { err = smb_pathname(sr, ppn.pn_buf, lookup_flags, local_root_node, local_cur_node, NULL, dir_node, cred); } (void) pn_free(&ppn); kmem_free(usepath, MAXPATHLEN); /* * Prevent traversal to another file system if mount point * traversal is disabled. * * Note that we disregard whether the traversal of the path went * outside of the file system and then came back (say via a link). * This means that only symlinks that are expressed relatively to * the share root work. * * share_root_node is NULL when mapping a share, so we disregard * that case. */ if ((err == 0) && share_root_node) { if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) { err = EACCES; if ((sr) && (sr)->tid_tree && smb_tree_has_feature((sr)->tid_tree, SMB_TREE_TRAVERSE_MOUNTS)) err = 0; } } if (err) { if (*dir_node) { (void) smb_node_release(*dir_node); *dir_node = NULL; } *last_component = 0; } if (vss_cur_node != NULL) (void) smb_node_release(vss_cur_node); if (vss_root_node != NULL) (void) smb_node_release(vss_root_node); return (err); }
smb_sdrc_t smb_com_check_directory(smb_request_t *sr) { int rc; smb_fqi_t *fqi; smb_node_t *tnode; smb_node_t *node; char *path; smb_pathname_t *pn; if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } fqi = &sr->arg.dirop.fqi; pn = &fqi->fq_path; if (pn->pn_path[0] == '\0') { rc = smbsr_encode_empty_result(sr); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); } smb_pathname_init(sr, pn, pn->pn_path); if (!smb_pathname_validate(sr, pn) || !smb_validate_dirname(sr, pn)) { return (SDRC_ERROR); } path = pn->pn_path; tnode = sr->tid_tree->t_snode; rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, &fqi->fq_dnode, fqi->fq_last_comp); if (rc != 0) { smbsr_errno(sr, rc); return (SDRC_ERROR); } rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode); smb_node_release(fqi->fq_dnode); if (rc != 0) { if (rc == ENOENT) smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_PATH_NOT_FOUND); else smbsr_errno(sr, rc); return (SDRC_ERROR); } node = fqi->fq_fnode; if (!smb_node_is_dir(node)) { smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, ERRDOS, ERROR_PATH_NOT_FOUND); smb_node_release(node); return (SDRC_ERROR); } if ((sr->smb_flg2 & SMB_FLAGS2_DFS) && smb_node_is_dfslink(node)) { smbsr_error(sr, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); smb_node_release(node); return (SDRC_ERROR); } rc = smb_fsop_access(sr, sr->user_cr, node, FILE_TRAVERSE); smb_node_release(node); if (rc != 0) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } rc = smbsr_encode_empty_result(sr); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
/* * See [MS-DFSC] for details about this command */ uint32_t smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl) { dfs_info_t *referrals; dfs_referral_response_t refrsp; dfs_reftype_t reftype; char *path; uint16_t maxver; uint32_t status; int rc; /* * The caller checks this, because the error reporting method * varies across SMB versions. */ ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type)); /* * XXX Instead of decoding the referral request and encoding * the response here (in-kernel) we could pass the given * request buffer in our door call, and let that return the * response buffer ready to stuff into out_mbc. That would * allow all this decoding/encoding to happen at user-level. * (and most of this file would go away. :-) */ switch (fsctl->CtlCode) { case FSCTL_DFS_GET_REFERRALS: /* * Input data is (w) MaxReferralLevel, (U) path */ rc = smb_mbc_decodef(fsctl->in_mbc, "%wu", sr, &maxver, &path); if (rc != 0) return (NT_STATUS_INVALID_PARAMETER); break; case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */ default: return (NT_STATUS_NOT_SUPPORTED); } reftype = smb_dfs_get_reftype((const char *)path); switch (reftype) { case DFS_REFERRAL_INVALID: /* Need to check the error for this case */ return (NT_STATUS_INVALID_PARAMETER); case DFS_REFERRAL_DOMAIN: case DFS_REFERRAL_DC: /* MS-DFSC: this error is returned by non-DC root */ return (NT_STATUS_INVALID_PARAMETER); case DFS_REFERRAL_SYSVOL: /* MS-DFSC: this error is returned by non-DC root */ return (NT_STATUS_NO_SUCH_DEVICE); default: break; } status = smb_dfs_referrals_get(sr, path, reftype, &refrsp); if (status != NT_STATUS_SUCCESS) return (status); referrals = &refrsp.rp_referrals; smb_dfs_encode_hdr(fsctl->out_mbc, referrals); /* * Server may respond with any referral version at or below * the maximum specified in the request. */ switch (maxver) { case DFS_REFERRAL_V1: status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals); break; case DFS_REFERRAL_V2: status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals); break; case DFS_REFERRAL_V3: status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, DFS_REFERRAL_V3); break; case DFS_REFERRAL_V4: default: status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, DFS_REFERRAL_V4); break; } smb_dfs_referrals_free(&refrsp); return (status); }