static int smb_usr_vc2spec(struct smbioc_ossn *dp, struct smb_vcspec *spec) { int flags = 0; bzero(spec, sizeof(*spec)); #ifdef NETSMB_NO_ANON_USER if (dp->ioc_user[0] == 0) return EINVAL; #endif if (dp->ioc_server == NULL) return EINVAL; if (dp->ioc_localcs[0] == 0) { SMBERROR("no local charset ?\n"); return EINVAL; } spec->sap = smb_memdupin(dp->ioc_server, dp->ioc_svlen); if (spec->sap == NULL) return ENOMEM; if (dp->ioc_local) { spec->lap = smb_memdupin(dp->ioc_local, dp->ioc_lolen); if (spec->lap == NULL) { smb_usr_vcspec_free(spec); return ENOMEM; } } spec->srvname = dp->ioc_srvname; spec->pass = dp->ioc_password; spec->domain = dp->ioc_workgroup; spec->username = dp->ioc_user; spec->mode = dp->ioc_mode; spec->rights = dp->ioc_rights; spec->owner = dp->ioc_owner; spec->group = dp->ioc_group; spec->localcs = dp->ioc_localcs; spec->servercs = dp->ioc_servercs; if (dp->ioc_opt & SMBVOPT_PRIVATE) flags |= SMBV_PRIVATE; if (dp->ioc_opt & SMBVOPT_SINGLESHARE) flags |= SMBV_PRIVATE | SMBV_SINGLESHARE; spec->flags = flags; return 0; }
/* * Called from user land so we always have a reference on the share. */ int smb_usr_query_dir(struct smb_share *share, struct smb2ioc_query_dir *query_dir_ioc, vfs_context_t context) { int error; struct smb2_query_dir_rq *queryp = NULL; SMB_MALLOC(queryp, struct smb2_query_dir_rq *, sizeof(struct smb2_query_dir_rq), M_SMBTEMP, M_WAITOK | M_ZERO); if (queryp == NULL) { SMBERROR("SMB_MALLOC failed\n"); error = ENOMEM; goto bad; } /* Take 32 bit world pointers and convert them to user_addr_t. */ if (query_dir_ioc->ioc_rcv_output_len > 0) { if (vfs_context_is64bit(context)) { queryp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_READ); } else { query_dir_ioc->ioc_kern_rcv_output = CAST_USER_ADDR_T(query_dir_ioc->ioc_rcv_output); queryp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_READ); } if (queryp->rcv_output_uio) { uio_addiov(queryp->rcv_output_uio, query_dir_ioc->ioc_kern_rcv_output, query_dir_ioc->ioc_rcv_output_len); } else { SMBERROR("uio_create failed\n"); error = ENOMEM; goto bad; } } /* Take the 32 bit world pointers and convert them to user_addr_t. */ if (!vfs_context_is64bit (context)) { query_dir_ioc->ioc_kern_name = CAST_USER_ADDR_T(query_dir_ioc->ioc_name); } /* ioc_name_len includes the null byte, ioc_kern_name is a c-style string */ if (query_dir_ioc->ioc_kern_name && query_dir_ioc->ioc_name_len) { queryp->name_len = query_dir_ioc->ioc_name_len; queryp->namep = smb_memdupin(query_dir_ioc->ioc_kern_name, query_dir_ioc->ioc_name_len); if (queryp->namep == NULL) { SMBERROR("smb_memdupin failed\n"); error = ENOMEM; goto bad; } } queryp->file_info_class = query_dir_ioc->ioc_file_info_class; queryp->flags = query_dir_ioc->ioc_flags; queryp->file_index = query_dir_ioc->ioc_file_index; queryp->output_buffer_len = query_dir_ioc->ioc_rcv_output_len; queryp->fid = query_dir_ioc->ioc_fid; queryp->name_len = query_dir_ioc->ioc_name_len; queryp->name_flags = query_dir_ioc->ioc_name_flags; /* * Never used for user ioctl query dir. User must have already opened * the dir to be searched. */ queryp->dnp = NULL; /* * Since this is from user space, there is no mounted file system, so * there are no vnodes and thus no queryp->dnp. This means that namep * must be non NULL. * * If ioc_rcv_output_len is not 0, then copy results directly to user * buffer and let them parse it. */ if ((queryp->namep == NULL) || (queryp->name_len == 0)) { SMBERROR("missing name \n"); error = EINVAL; goto bad; } /* Now do the real work */ error = smb2_smb_query_dir(share, queryp, NULL, context); /* always return the ntstatus error */ query_dir_ioc->ioc_ret_ntstatus = queryp->ret_ntstatus; if (error) { goto bad; } /* Fill in amount of data returned in Query Dir reply */ query_dir_ioc->ioc_ret_output_len = queryp->ret_buffer_len; /* Fill in actual amount of data returned */ query_dir_ioc->ioc_rcv_output_len = queryp->output_buffer_len; bad: if (queryp != NULL) { if (queryp->ret_rqp != NULL) { smb_rq_done(queryp->ret_rqp); } if (queryp->namep) SMB_FREE(queryp->namep, M_SMBSTR); if (queryp->rcv_output_uio != NULL) { uio_free(queryp->rcv_output_uio); } SMB_FREE(queryp, M_SMBTEMP); } return error; }
int smb_usr_get_dfs_referral(struct smb_share *share, struct smb_vc *vcp, struct smb2ioc_get_dfs_referral *get_dfs_refer_ioc, vfs_context_t context) { int error; struct smb2_ioctl_rq *ioctlp = NULL; struct smb2_get_dfs_referral dfs_referral; char *local_pathp = NULL; uint32_t local_path_len = get_dfs_refer_ioc->ioc_file_name_len; size_t network_path_len = PATH_MAX + 1; char *network_pathp = NULL; SMB_MALLOC(ioctlp, struct smb2_ioctl_rq *, sizeof(struct smb2_ioctl_rq), M_SMBTEMP, M_WAITOK | M_ZERO); if (ioctlp == NULL) { SMBERROR("SMB_MALLOC failed\n"); error = ENOMEM; goto bad; } again: /* Take the 32 bit world pointers and convert them to user_addr_t. */ bzero(&dfs_referral, sizeof(dfs_referral)); dfs_referral.file_namep = NULL; dfs_referral.max_referral_level = get_dfs_refer_ioc->ioc_max_referral_level; if (!vfs_context_is64bit (context)) { get_dfs_refer_ioc->ioc_kern_file_name = CAST_USER_ADDR_T(get_dfs_refer_ioc->ioc_file_name); } if (!(get_dfs_refer_ioc->ioc_kern_file_name)) { error = EINVAL; goto bad; } /* ioc_file_name_len includes the null byte, ioc_kern_file_name is a c-style string */ if (get_dfs_refer_ioc->ioc_kern_file_name && get_dfs_refer_ioc->ioc_file_name_len) { local_pathp = smb_memdupin(get_dfs_refer_ioc->ioc_kern_file_name, local_path_len); } if (local_pathp == NULL) { SMBERROR("smb_memdupin failed\n"); error = ENOMEM; goto bad; } /* * Need to convert from local path to a network path */ SMB_MALLOC(network_pathp, char *, network_path_len, M_TEMP, M_WAITOK | M_ZERO); if (network_pathp == NULL) { error = ENOMEM; goto bad; } error = smb_convert_path_to_network(local_pathp, local_path_len, network_pathp, &network_path_len, '\\', SMB_UTF_SFM_CONVERSIONS, SMB_UNICODE_STRINGS(vcp)); if (error) { SMBERROR("smb_convert_path_to_network failed : %d\n", error); goto bad; } dfs_referral.file_namep = network_pathp; dfs_referral.file_name_len = (uint32_t) network_path_len; /* Take 32 bit world pointers and convert them to user_addr_t. */ if (get_dfs_refer_ioc->ioc_rcv_output_len > 0) { if (vfs_context_is64bit(context)) { ioctlp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_READ); } else { get_dfs_refer_ioc->ioc_kern_rcv_output = CAST_USER_ADDR_T(get_dfs_refer_ioc->ioc_rcv_output); ioctlp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_READ); } if (ioctlp->rcv_output_uio) { uio_addiov(ioctlp->rcv_output_uio, get_dfs_refer_ioc->ioc_kern_rcv_output, get_dfs_refer_ioc->ioc_rcv_output_len); } else { error = ENOMEM; SMBERROR("uio_create failed\n"); goto bad; } } ioctlp->share = share; ioctlp->ctl_code = FSCTL_DFS_GET_REFERRALS; ioctlp->fid = -1; ioctlp->snd_input_buffer = (uint8_t *) &dfs_referral; ioctlp->snd_input_len = sizeof(struct smb2_get_dfs_referral); ioctlp->snd_output_len = 0; ioctlp->rcv_input_len = 0; /* Handle servers that dislike large output buffer lengths */ if (vcp->vc_misc_flags & SMBV_63K_IOCTL) { ioctlp->rcv_output_len = kSMB_63K; } else { ioctlp->rcv_output_len = get_dfs_refer_ioc->ioc_rcv_output_len; } /* Now do the real work */ error = smb2_smb_ioctl(share, ioctlp, NULL, context); if ((error) && (ioctlp->ret_ntstatus == STATUS_INVALID_PARAMETER) && !(vcp->vc_misc_flags & SMBV_63K_IOCTL)) { /* * <14281932> Could this be a server that can not handle * larger than 65535 bytes in an IOCTL? */ SMBWARNING("SMB 2/3 server cant handle large OutputBufferLength in DFS Referral. Reducing to 63Kb.\n"); vcp->vc_misc_flags |= SMBV_63K_IOCTL; ioctlp->ret_ntstatus = 0; if (ioctlp->snd_input_uio != NULL) { uio_free(ioctlp->snd_input_uio); ioctlp->snd_input_uio = NULL; } if (ioctlp->snd_output_uio != NULL) { uio_free(ioctlp->snd_output_uio); ioctlp->snd_output_uio = NULL; } if (ioctlp->rcv_input_uio != NULL) { uio_free(ioctlp->rcv_input_uio); ioctlp->rcv_input_uio = NULL; } if (ioctlp->rcv_output_uio != NULL) { uio_free(ioctlp->rcv_output_uio); ioctlp->rcv_output_uio = NULL; } goto again; } /* always return the ntstatus error */ get_dfs_refer_ioc->ioc_ret_ntstatus = ioctlp->ret_ntstatus; if (error) { goto bad; } /* Fill in actual bytes returned */ get_dfs_refer_ioc->ioc_ret_output_len = ioctlp->ret_output_len; bad: if (ioctlp != NULL) { if (ioctlp->snd_input_uio != NULL) { uio_free(ioctlp->snd_input_uio); } if (ioctlp->snd_output_uio != NULL) { uio_free(ioctlp->snd_output_uio); } if (ioctlp->rcv_input_uio != NULL) { uio_free(ioctlp->rcv_input_uio); } if (ioctlp->rcv_output_uio != NULL) { uio_free(ioctlp->rcv_output_uio); } SMB_FREE(ioctlp, M_SMBTEMP); } if (local_pathp) { SMB_FREE(local_pathp, M_SMBSTR); } if (network_pathp) { SMB_FREE(network_pathp, M_SMBSTR); } return error; }
/* * Called from user land so we always have a reference on the share. */ int smb_usr_check_dir(struct smb_share *share, struct smb_vc *vcp, struct smb2ioc_check_dir *check_dir_ioc, vfs_context_t context) { int error; char *local_pathp = NULL; uint32_t local_path_len = check_dir_ioc->ioc_path_len; uint32_t desired_access = SMB2_FILE_READ_ATTRIBUTES | SMB2_SYNCHRONIZE; uint32_t share_access = NTCREATEX_SHARE_ACCESS_ALL; /* Tell create, the namep is a path to an item */ uint64_t create_flags = SMB2_CREATE_NAME_IS_PATH; struct smbfattr *fap = NULL; size_t network_path_len = PATH_MAX + 1; char *network_pathp = NULL; /* Assume no error */ check_dir_ioc->ioc_ret_ntstatus = 0; /* * Compound Create/Close call should be sufficient. * If item exists, verify it is a dir. */ /* Take the 32 bit world pointers and convert them to user_addr_t. */ if (!vfs_context_is64bit(context)) { check_dir_ioc->ioc_kern_path = CAST_USER_ADDR_T(check_dir_ioc->ioc_path); } if (!(check_dir_ioc->ioc_kern_path)) { error = EINVAL; goto bad; } /* local_path_len includes the null byte, ioc_kern_path is a c-style string */ if (check_dir_ioc->ioc_kern_path && local_path_len) { local_pathp = smb_memdupin(check_dir_ioc->ioc_kern_path, local_path_len); } if (local_pathp == NULL) { SMBERROR("smb_memdupin failed\n"); error = ENOMEM; goto bad; } /* * Need to convert from local path to a network path */ SMB_MALLOC(network_pathp, char *, network_path_len, M_TEMP, M_WAITOK | M_ZERO); if (network_pathp == NULL) { error = ENOMEM; goto bad; } error = smb_convert_path_to_network(local_pathp, local_path_len, network_pathp, &network_path_len, '\\', SMB_UTF_SFM_CONVERSIONS, SMB_UNICODE_STRINGS(vcp)); if (error) { SMBERROR("smb_convert_path_to_network failed : %d\n", error); goto bad; } /* * Set up for Compound Create/Close call */ SMB_MALLOC(fap, struct smbfattr *, sizeof(struct smbfattr), M_SMBTEMP, M_WAITOK | M_ZERO); if (fap == NULL) { SMBERROR("SMB_MALLOC failed\n"); error = ENOMEM; goto bad; } /* Send a Create/Close */ error = smb2fs_smb_cmpd_create(share, NULL, network_pathp, network_path_len, NULL, 0, desired_access, VDIR, share_access, FILE_OPEN, create_flags, &check_dir_ioc->ioc_ret_ntstatus, NULL, fap, NULL, context); if (error) { goto bad; } /* found something, verify its a dir */ if (!(fap->fa_attr & SMB_EFA_DIRECTORY)) { error = ENOTDIR; check_dir_ioc->ioc_ret_ntstatus = STATUS_NOT_A_DIRECTORY; } bad: if (local_pathp) { SMB_FREE(local_pathp, M_SMBSTR); } if (network_pathp) { SMB_FREE(network_pathp, M_SMBSTR); } if (fap) { SMB_FREE(fap, M_SMBTEMP); } return error; }
/* * Called from user land so we always have a reference on the share. */ int smb_usr_create(struct smb_share *share, struct smb2ioc_create *create_ioc, vfs_context_t context) { int error; struct smb2_create_rq *createp = NULL; SMB_MALLOC(createp, struct smb2_create_rq *, sizeof(struct smb2_create_rq), M_SMBTEMP, M_WAITOK | M_ZERO); if (createp == NULL) { SMBERROR("SMB_MALLOC failed\n"); error = ENOMEM; goto bad; } /* Take the 32 bit world pointers and convert them to user_addr_t. */ if (!vfs_context_is64bit (context)) { create_ioc->ioc_kern_name = CAST_USER_ADDR_T(create_ioc->ioc_name); } /* ioc_name_len includes the null byte, ioc_kern_name is a c-style string */ if (create_ioc->ioc_kern_name && create_ioc->ioc_name_len) { createp->name_len = create_ioc->ioc_name_len; createp->namep = smb_memdupin(create_ioc->ioc_kern_name, create_ioc->ioc_name_len); if (createp->namep == NULL) { SMBERROR("smb_memdupin failed\n"); error = ENOMEM; goto bad; } } createp->flags = SMB2_CREATE_GET_MAX_ACCESS; createp->oplock_level = create_ioc->ioc_oplock_level; createp->impersonate_level = create_ioc->ioc_impersonate_level; createp->desired_access = create_ioc->ioc_desired_access; createp->file_attributes = create_ioc->ioc_file_attributes; createp->share_access = create_ioc->ioc_share_access; createp->disposition = create_ioc->ioc_disposition; createp->create_options = create_ioc->ioc_create_options; createp->dnp = NULL; /* Now do the real work */ error = smb2_smb_create(share, createp, NULL, context); /* always return the ntstatus error */ create_ioc->ioc_ret_ntstatus = createp->ret_ntstatus; if (error) { goto bad; } /* Fill in return parameters */ create_ioc->ioc_ret_attributes = createp->ret_attributes; create_ioc->ioc_ret_oplock_level = createp->ret_oplock_level; create_ioc->ioc_ret_create_action = createp->ret_create_action; create_ioc->ioc_ret_create_time = createp->ret_create_time; create_ioc->ioc_ret_access_time = createp->ret_access_time; create_ioc->ioc_ret_write_time = createp->ret_write_time; create_ioc->ioc_ret_change_time = createp->ret_change_time; create_ioc->ioc_ret_alloc_size = createp->ret_alloc_size; create_ioc->ioc_ret_eof = createp->ret_eof; create_ioc->ioc_ret_fid = createp->ret_fid; create_ioc->ioc_ret_max_access = createp->ret_max_access; bad: if (createp != NULL) { if (createp->namep) SMB_FREE(createp->namep, M_SMBSTR); SMB_FREE(createp, M_SMBTEMP); } return error; }