int smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred) { int error; if (limit == 0) limit = 1000000; else if (limit > 1) limit *= 4; /* imperical */ ctx->f_scred = scred; for (;;) { if (ctx->f_flags & SMBFS_RDD_USESEARCH) { error = smbfs_findnextLM1(ctx, limit); } else error = smbfs_findnextLM2(ctx, limit); if (error) return error; if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { if ((ctx->f_nmlen == 2 && *(u_int16_t *)ctx->f_name == htole16(0x002e)) || (ctx->f_nmlen == 4 && *(u_int32_t *)ctx->f_name == htole32(0x002e002e))) continue; } else if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') || (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' && ctx->f_name[1] == '.')) continue; break; } smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, &ctx->f_nmlen, ctx->f_dnp->n_mount->sm_caseopt); ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen); return 0; }
int smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, const char *name, int nmlen) { int caseopt = SMB_CS_NONE; int error; if (SMB_UNICODE_STRINGS(vcp)) { error = mb_put_padbyte(mbp); if (error) return error; } if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) caseopt |= SMB_CS_UPPER; if (dnp != NULL) { error = smb_put_dmem(mbp, vcp, dnp->n_rpath, dnp->n_rplen, caseopt); if (error) return error; if (name) { /* Put the separator */ if (SMB_UNICODE_STRINGS(vcp)) error = mb_put_uint16le(mbp, '\\'); else error = mb_put_uint8(mbp, '\\'); if (error) return error; /* Put the name */ error = smb_put_dmem(mbp, vcp, name, nmlen, caseopt); if (error) return error; } } /* Put NULL terminator. */ if (SMB_UNICODE_STRINGS(vcp)) error = mb_put_uint16le(mbp, 0); else error = mb_put_uint8(mbp, 0); return error; }
int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src, int caseopt) { int error; error = smb_put_dmem(mbp, vcp, src, strlen(src), caseopt); if (error) return error; if (SMB_UNICODE_STRINGS(vcp)) return mb_put_uint16le(mbp, 0); return mb_put_uint8(mbp, 0); }
/* * Set DOS file attributes. mtime should be NULL for dialects above lm10 */ int smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime, struct smb_cred *scred) { struct smb_rq *rqp; struct smb_share *ssp = np->n_mount->sm_share; struct mbchain *mbp; u_long time; int error, svtz; rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK); error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred); if (error) { free(rqp, M_SMBFSDATA); return error; } svtz = SSTOVC(ssp)->vc_sopt.sv_tz; smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); mb_put_uint16le(mbp, attr); if (mtime) { smb_time_local2server(mtime, svtz, &time); } else time = 0; mb_put_uint32le(mbp, time); /* mtime */ mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_uint8(mbp, SMB_DT_ASCII); do { error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); if (error) break; mb_put_uint8(mbp, SMB_DT_ASCII); if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) { mb_put_padbyte(mbp); mb_put_uint8(mbp, 0); /* 1st byte of NULL Unicode char */ } mb_put_uint8(mbp, 0); smb_rq_bend(rqp); error = smb_rq_simple(rqp); if (error) { SMBERROR("smb_rq_simple(rqp) => error %d\n", error); break; } } while(0); smb_rq_done(rqp); free(rqp, M_SMBFSDATA); return error; }
static int smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, const char *wildcard, int wclen, int attr, struct smb_cred *scred) { if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { ctx->f_name = malloc(SMB_MAXFNAMELEN * 2, M_SMBFSDATA, M_WAITOK); } else ctx->f_name = malloc(SMB_MAXFNAMELEN, M_SMBFSDATA, M_WAITOK); ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ? SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO; ctx->f_attrmask = attr; ctx->f_wildcard = wildcard; ctx->f_wclen = wclen; return 0; }
int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src, size_t size, int caseopt) { struct iconv_drv *dp = vcp->vc_toserver; if (size == 0) return 0; if (dp == NULL) { return mb_put_mem(mbp, src, size, MB_MSYSTEM); } mbp->mb_copy = smb_copy_iconv; mbp->mb_udata = dp; if (SMB_UNICODE_STRINGS(vcp)) mb_put_padbyte(mbp); return mb_put_mem(mbp, src, size, MB_MCUSTOM); }
int smbfs_fname_tolocal(struct smb_vc *vcp, char *name, int *nmlen, int caseopt) { int copt = (caseopt == SMB_CS_LOWER ? KICONV_FROM_LOWER : (caseopt == SMB_CS_UPPER ? KICONV_FROM_UPPER : 0)); int error = 0; size_t ilen = *nmlen; size_t olen; char *ibuf = name; char outbuf[SMB_MAXFNAMELEN]; char *obuf = outbuf; if (vcp->vc_tolocal) { olen = sizeof(outbuf); bzero(outbuf, sizeof(outbuf)); /* error = iconv_conv_case (vcp->vc_tolocal, NULL, NULL, &obuf, &olen, copt); if (error) return error; */ error = iconv_conv_case (vcp->vc_tolocal, (const char **)&ibuf, &ilen, &obuf, &olen, copt); if (error && SMB_UNICODE_STRINGS(vcp)) { /* * If using unicode, leaving a file name as it was when * convert fails will cause a problem because the file name * will contain NULL. * Here, put '?' and give converted file name. */ *obuf = '?'; olen--; error = 0; } if (!error) { *nmlen = sizeof(outbuf) - olen; memcpy(name, outbuf, *nmlen); } } return error; }
static int smbfs_smb_search(struct smbfs_fctx *ctx) { struct smb_vc *vcp = SSTOVC(ctx->f_ssp); struct smb_rq *rqp; struct mbchain *mbp; struct mdchain *mdp; u_int8_t wc, bt; u_int16_t ec, dlen, bc; int maxent, error, iseof = 0; maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN); if (ctx->f_rq) { smb_rq_done(ctx->f_rq); ctx->f_rq = NULL; } error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp); if (error) return error; ctx->f_rq = rqp; smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); mb_put_uint16le(mbp, maxent); /* max entries to return */ mb_put_uint16le(mbp, ctx->f_attrmask); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_uint8(mbp, SMB_DT_ASCII); /* buffer format */ if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen); if (error) return error; mb_put_uint8(mbp, SMB_DT_VARIABLE); mb_put_uint16le(mbp, 0); /* context length */ ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; } else { if (SMB_UNICODE_STRINGS(vcp)) { mb_put_padbyte(mbp); mb_put_uint8(mbp, 0); } mb_put_uint8(mbp, 0); /* file name length */ mb_put_uint8(mbp, SMB_DT_VARIABLE); mb_put_uint16le(mbp, SMB_SKEYLEN); mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); } smb_rq_bend(rqp); error = smb_rq_simple(rqp); if (error) { if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) { error = 0; iseof = 1; ctx->f_flags |= SMBFS_RDD_EOF; } else return error; } smb_rq_getreply(rqp, &mdp); md_get_uint8(mdp, &wc); if (wc != 1) return iseof ? ENOENT : EBADRPC; md_get_uint16le(mdp, &ec); if (ec == 0) return ENOENT; ctx->f_ecnt = ec; md_get_uint16le(mdp, &bc); if (bc < 3) return EBADRPC; bc -= 3; md_get_uint8(mdp, &bt); if (bt != SMB_DT_VARIABLE) return EBADRPC; md_get_uint16le(mdp, &dlen); if (dlen != bc || dlen % SMB_DENTRYLEN != 0) return EBADRPC; return 0; }
static int smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit) { struct mdchain *mbp; struct smb_t2rq *t2p; char *cp; u_int8_t tb; u_int16_t date, time, wattr; u_int32_t size, next, dattr; int64_t lint; int error, svtz, cnt, fxsz, nmlen, recsz; if (ctx->f_ecnt == 0) { if (ctx->f_flags & SMBFS_RDD_EOF) return ENOENT; ctx->f_left = ctx->f_limit = limit; error = smbfs_smb_trans2find2(ctx); if (error) return error; } t2p = ctx->f_t2; mbp = &t2p->t2_rdata; svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz; switch (ctx->f_infolevel) { case SMB_INFO_STANDARD: next = 0; fxsz = 0; md_get_uint16le(mbp, &date); md_get_uint16le(mbp, &time); /* creation time */ md_get_uint16le(mbp, &date); md_get_uint16le(mbp, &time); /* access time */ smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime); md_get_uint16le(mbp, &date); md_get_uint16le(mbp, &time); /* access time */ smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime); md_get_uint32le(mbp, &size); ctx->f_attr.fa_size = size; md_get_uint32(mbp, NULL); /* allocation size */ md_get_uint16le(mbp, &wattr); ctx->f_attr.fa_attr = wattr; md_get_uint8(mbp, &tb); size = nmlen = tb; fxsz = 23; recsz = next = 24 + nmlen; /* docs misses zero byte at end */ break; case SMB_FIND_FILE_DIRECTORY_INFO: md_get_uint32le(mbp, &next); md_get_uint32(mbp, NULL); /* file index */ md_get_int64(mbp, NULL); /* creation time */ md_get_int64le(mbp, &lint); smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_atime); md_get_int64le(mbp, &lint); smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_mtime); md_get_int64le(mbp, &lint); smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_ctime); md_get_int64le(mbp, &lint); /* file size */ ctx->f_attr.fa_size = lint; md_get_int64(mbp, NULL); /* real size (should use) */ md_get_uint32le(mbp, &dattr); /* EA */ ctx->f_attr.fa_attr = dattr; md_get_uint32le(mbp, &size); /* name len */ fxsz = 64; recsz = next ? next : fxsz + size; break; default: SMBERROR("unexpected info level %d\n", ctx->f_infolevel); return EINVAL; } if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { nmlen = min(size, SMB_MAXFNAMELEN * 2); } else nmlen = min(size, SMB_MAXFNAMELEN); cp = ctx->f_name; error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM); if (error) return error; if (next) { cnt = next - nmlen - fxsz; if (cnt > 0) md_get_mem(mbp, NULL, cnt, MB_MSYSTEM); else if (cnt < 0) { SMBERROR("out of sync\n"); return EBADRPC; } } if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { if (nmlen > 1 && cp[nmlen - 1] == 0 && cp[nmlen - 2] == 0) nmlen -= 2; } else if (nmlen && cp[nmlen - 1] == 0) nmlen--; if (nmlen == 0) return EBADRPC; next = ctx->f_eofs + recsz; if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 && (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) { /* * Server needs a resume filename. */ if (ctx->f_rnamelen <= nmlen) { if (ctx->f_rname) free(ctx->f_rname, M_SMBFSDATA); ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK); ctx->f_rnamelen = nmlen; } bcopy(ctx->f_name, ctx->f_rname, nmlen); ctx->f_rname[nmlen] = 0; ctx->f_flags |= SMBFS_RDD_GOTRNAME; } ctx->f_nmlen = nmlen; ctx->f_eofs = next; ctx->f_ecnt--; ctx->f_left--; return 0; }
int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) { struct smb_rq *rqp; struct mbchain *mbp; const smb_unichar *unipp; smb_uniptr ntencpass = NULL; char *up, *pbuf, *encpass; const char *pp; int error, plen, uniplen, ulen, upper; KASSERT(scred->scr_l == vcp->vc_iod->iod_l); upper = 0; again: vcp->vc_smbuid = SMB_UID_UNKNOWN; error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp); if (error) return error; pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK); encpass = malloc(24, M_SMBTEMP, M_WAITOK); if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { /* * We try w/o uppercasing first so Samba mixed case * passwords work. If that fails we come back and try * uppercasing to satisfy OS/2 and Windows for Workgroups. */ if (upper) { iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN + 1); } else { strlcpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN + 1); } if (!SMB_UNICODE_STRINGS(vcp)) iconv_convstr(vcp->vc_toserver, pbuf, pbuf, SMB_MAXPASSWORDLEN + 1); if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { uniplen = plen = 24; smb_encrypt(pbuf, vcp->vc_ch, encpass); ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); if (SMB_UNICODE_STRINGS(vcp)) { strlcpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN + 1); } else iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN + 1); smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass); pp = encpass; unipp = ntencpass; } else { plen = strlen(pbuf) + 1; pp = pbuf; uniplen = plen * 2; ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK); smb_strtouni(ntencpass, smb_vc_getpass(vcp)); plen--; /* * The uniplen is zeroed because Samba cannot deal * with this 2nd cleartext password. This Samba * "bug" is actually a workaround for problems in * Microsoft clients. */ uniplen = 0/*-= 2*/; unipp = ntencpass; } } else { /* * In the share security mode password will be used * only in the tree authentication */ pp = ""; plen = 1; unipp = &smb_unieol; uniplen = 0; } smb_rq_wstart(rqp); mbp = &rqp->sr_rq; up = vcp->vc_username; ulen = strlen(up) + 1; /* * If userid is null we are attempting anonymous browse login * so passwords must be zero length. */ if (ulen == 1) plen = uniplen = 0; mb_put_uint8(mbp, 0xff); mb_put_uint8(mbp, 0); mb_put_uint16le(mbp, 0); mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); mb_put_uint16le(mbp, vcp->vc_number); mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); mb_put_uint16le(mbp, plen); if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { mb_put_uint32le(mbp, 0); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_mem(mbp, pp, plen, MB_MSYSTEM); smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); } else { mb_put_uint16le(mbp, uniplen); mb_put_uint32le(mbp, 0); /* reserved */ mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ? SMB_CAP_UNICODE : 0); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_mem(mbp, pp, plen, MB_MSYSTEM); mb_put_mem(mbp, (const void *)unipp, uniplen, MB_MSYSTEM); smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */ smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */ smb_put_dstring(mbp, vcp, "NetBSD", SMB_CS_NONE); /* Client's OS */ smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */ } smb_rq_bend(rqp); if (ntencpass) free(ntencpass, M_SMBTEMP); error = smb_rq_simple(rqp); SMBSDEBUG(("%d\n", error)); if (error) { if (error == EACCES) error = EAUTH; goto bad; } vcp->vc_smbuid = rqp->sr_rpuid; bad: free(encpass, M_SMBTEMP); free(pbuf, M_SMBTEMP); smb_rq_done(rqp); if (error && !upper && vcp->vc_sopt.sv_sm & SMB_SM_USER) { upper = 1; goto again; } 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; }
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; }
/* * Perform a full round of TRANS2 request */ static int smb_t2_request_int(struct smb_t2rq *t2p) { struct smb_vc *vcp = t2p->t2_vc; struct smb_cred *scred = t2p->t2_cred; struct mbchain *mbp; struct mdchain *mdp, mbparam, mbdata; mblk_t *m; struct smb_rq *rqp; int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i; int error, doff, poff, txdcount, txpcount, nmlen, nmsize; m = t2p->t2_tparam.mb_top; if (m) { md_initm(&mbparam, m); /* do not free it! */ totpcount = m_fixhdr(m); if (totpcount > 0xffff) /* maxvalue for ushort_t */ return (EINVAL); } else totpcount = 0; m = t2p->t2_tdata.mb_top; if (m) { md_initm(&mbdata, m); /* do not free it! */ totdcount = m_fixhdr(m); if (totdcount > 0xffff) return (EINVAL); } else totdcount = 0; leftdcount = totdcount; leftpcount = totpcount; txmax = vcp->vc_txmax; error = smb_rq_alloc(t2p->t2_source, t2p->t_name ? SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp); if (error) return (error); rqp->sr_timo = smb_timo_default; rqp->sr_flags |= SMBR_MULTIPACKET; t2p->t2_rq = rqp; mbp = &rqp->sr_rq; smb_rq_wstart(rqp); mb_put_uint16le(mbp, totpcount); mb_put_uint16le(mbp, totdcount); mb_put_uint16le(mbp, t2p->t2_maxpcount); mb_put_uint16le(mbp, t2p->t2_maxdcount); mb_put_uint8(mbp, t2p->t2_maxscount); mb_put_uint8(mbp, 0); /* reserved */ mb_put_uint16le(mbp, 0); /* flags */ mb_put_uint32le(mbp, 0); /* Timeout */ mb_put_uint16le(mbp, 0); /* reserved 2 */ len = mb_fixhdr(mbp); /* * Now we know the size of the trans overhead stuff: * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + nmsize), * where nmsize is the OTW size of the name, including * the unicode null terminator and any alignment. * Use this to decide which parts (and how much) * can go into this request: params, data */ nmlen = t2p->t_name ? t2p->t_name_len : 0; nmsize = nmlen + 1; /* null term. */ if (SMB_UNICODE_STRINGS(vcp)) { nmsize *= 2; /* we know put_dmem will need to align */ nmsize += 1; } len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmsize); if (len + leftpcount > txmax) { txpcount = min(leftpcount, txmax - len); poff = len; txdcount = 0; doff = 0; } else { txpcount = leftpcount; poff = txpcount ? len : 0; /* * Other client traffic seems to "ALIGN2" here. The extra * 2 byte pad we use has no observed downside and may be * required for some old servers(?) */ len = ALIGN4(len + txpcount); txdcount = min(leftdcount, txmax - len); doff = txdcount ? len : 0; } leftpcount -= txpcount; leftdcount -= txdcount; mb_put_uint16le(mbp, txpcount); mb_put_uint16le(mbp, poff); mb_put_uint16le(mbp, txdcount); mb_put_uint16le(mbp, doff); mb_put_uint8(mbp, t2p->t2_setupcount); mb_put_uint8(mbp, 0); for (i = 0; i < t2p->t2_setupcount; i++) { mb_put_uint16le(mbp, t2p->t2_setupdata[i]); } smb_rq_wend(rqp); smb_rq_bstart(rqp); if (t2p->t_name) { /* Put the string and terminating null. */ smb_put_dmem(mbp, vcp, t2p->t_name, nmlen + 1, SMB_CS_NONE, NULL); } else { /* nmsize accounts for padding, char size. */ mb_put_mem(mbp, NULL, nmsize, MB_MZERO); } len = mb_fixhdr(mbp); if (txpcount) { mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); error = md_get_mbuf(&mbparam, txpcount, &m); SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax); if (error) goto freerq; mb_put_mbuf(mbp, m); } len = mb_fixhdr(mbp); if (txdcount) { mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); error = md_get_mbuf(&mbdata, txdcount, &m); if (error) goto freerq; mb_put_mbuf(mbp, m); } smb_rq_bend(rqp); /* incredible, but thats it... */ error = smb_rq_enqueue(rqp); if (error) goto freerq; if (leftpcount || leftdcount) { error = smb_rq_reply(rqp); if (error) goto bad; /* * this is an interim response, ignore it. */ SMBRQ_LOCK(rqp); md_next_record(&rqp->sr_rp); SMBRQ_UNLOCK(rqp); } while (leftpcount || leftdcount) { error = smb_rq_new(rqp, t2p->t_name ? SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY); if (error) goto bad; mbp = &rqp->sr_rq; smb_rq_wstart(rqp); mb_put_uint16le(mbp, totpcount); mb_put_uint16le(mbp, totdcount); len = mb_fixhdr(mbp); /* * now we have known packet size as * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one, * and need to decide which parts should go into request */ len = ALIGN4(len + 6 * 2 + 2); if (t2p->t_name == NULL) len += 2; if (len + leftpcount > txmax) { txpcount = min(leftpcount, txmax - len); poff = len; txdcount = 0; doff = 0; } else { txpcount = leftpcount; poff = txpcount ? len : 0; len = ALIGN4(len + txpcount); txdcount = min(leftdcount, txmax - len); doff = txdcount ? len : 0; } mb_put_uint16le(mbp, txpcount); mb_put_uint16le(mbp, poff); mb_put_uint16le(mbp, totpcount - leftpcount); mb_put_uint16le(mbp, txdcount); mb_put_uint16le(mbp, doff); mb_put_uint16le(mbp, totdcount - leftdcount); leftpcount -= txpcount; leftdcount -= txdcount; if (t2p->t_name == NULL) mb_put_uint16le(mbp, t2p->t2_fid); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_uint8(mbp, 0); /* name */ len = mb_fixhdr(mbp); if (txpcount) { mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); error = md_get_mbuf(&mbparam, txpcount, &m); if (error) goto bad; mb_put_mbuf(mbp, m); } len = mb_fixhdr(mbp); if (txdcount) { mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); error = md_get_mbuf(&mbdata, txdcount, &m); if (error) goto bad; mb_put_mbuf(mbp, m); } smb_rq_bend(rqp); error = smb_iod_multirq(rqp); if (error) goto bad; } /* while left params or data */ error = smb_t2_reply(t2p); if (error && !(t2p->t2_flags & SMBT2_MOREDATA)) goto bad; mdp = &t2p->t2_rdata; if (mdp->md_top) { m_fixhdr(mdp->md_top); md_initm(mdp, mdp->md_top); } mdp = &t2p->t2_rparam; if (mdp->md_top) { m_fixhdr(mdp->md_top); md_initm(mdp, mdp->md_top); } bad: smb_iod_removerq(rqp); freerq: if (error && !(t2p->t2_flags & SMBT2_MOREDATA)) { if (rqp->sr_flags & SMBR_RESTART) t2p->t2_flags |= SMBT2_RESTART; md_done(&t2p->t2_rparam); md_done(&t2p->t2_rdata); } smb_rq_done(rqp); return (error); }