#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/random.h>
#include <sys/note.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <smb/ntaccess.h>
#include <smb/winioctl.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb.h>
#include <netsmb/smb2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb2_rq.h>
#define NDIALECTS 3
static const uint16_t smb2_dialects[NDIALECTS] = {
SMB2_DIALECT_0210,
SMB2_DIALECT_0300,
SMB2_DIALECT_0302,
};
uint32_t smb2_clnt_caps =
SMB2_CAP_LARGE_MTU |
SMB2_CAP_ENCRYPTION;
uint16_t smb2_ss_req_credits = 64;
int smb2_timo_notice = 15;
int smb2_timo_default = 30;
int smb2_timo_logon = 45;
int smb2_timo_open = 45;
int smb2_timo_read = 45;
int smb2_timo_write = 60;
int smb2_timo_append = 90;
int
smb2_parse_smb1nego_resp(struct smb_rq *rqp)
{
struct smb_vc *vcp = rqp->sr_vc;
struct smb_sopt *sp = &vcp->vc_sopt;
struct mdchain *mdp;
uint16_t length = 0;
int error;
smb_rq_getreply(rqp, &mdp);
error = smb2_rq_parsehdr(rqp);
if (error != 0)
return (error);
md_get_uint16le(mdp, &length);
if (length != 65)
return (EBADRPC);
md_get_uint16le(mdp, &sp->sv2_security_mode);
error = md_get_uint16le(mdp, &sp->sv_proto);
if (error != 0)
return (error);
if (sp->sv_proto != SMB2_DIALECT_02ff) {
SMBERROR("Unknown dialect 0x%x\n", sp->sv_proto);
return (EINVAL);
}
sp->sv_proto = SMB_DIALECT_SMB2_FF;
vcp->vc2_next_message_id = 1;
vcp->vc2_limit_message_id = 2;
return (0);
}
int
smb2_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
{
smb_sopt_t *sp = &vcp->vc_sopt;
smbioc_ssn_work_t *wk = &vcp->vc_work;
struct smb_rq *rqp = NULL;
struct mbchain *mbp = NULL;
struct mdchain *mdp = NULL;
uint16_t *ndialects_p;
uint16_t ndialects = NDIALECTS;
boolean_t will_sign = B_FALSE;
uint16_t length = 0;
uint16_t security_mode;
uint16_t sec_buf_off;
uint16_t sec_buf_len;
int err, i;
if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
security_mode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
} else {
security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
}
err = smb_rq_alloc(VCTOCP(vcp), SMB2_NEGOTIATE, scred, &rqp);
if (err)
return (err);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 36);
ndialects_p = mb_reserve(mbp, 2);
mb_put_uint16le(mbp, security_mode);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, smb2_clnt_caps);
mb_put_mem(mbp, vcp->vc_cl_guid, 16, MB_MSYSTEM);
mb_put_uint64le(mbp, 0);
for (i = 0; i < ndialects; i++) {
if (smb2_dialects[i] > vcp->vc_maxver)
break;
mb_put_uint16le(mbp, smb2_dialects[i]);
}
*ndialects_p = htoles(i);
err = smb2_rq_internal(rqp, smb2_timo_default);
if (err) {
goto errout;
}
if (rqp->sr_error != NT_STATUS_SUCCESS) {
err = ENOTSUP;
goto errout;
}
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &length);
if (length != 65) {
err = EBADRPC;
goto errout;
}
md_get_uint16le(mdp, &sp->sv2_security_mode);
md_get_uint16le(mdp, &sp->sv_proto);
md_get_uint16le(mdp, NULL);
md_get_mem(mdp, sp->sv2_guid, 16, MB_MSYSTEM);
md_get_uint32le(mdp, &sp->sv2_capabilities);
md_get_uint32le(mdp, &sp->sv2_maxtransact);
md_get_uint32le(mdp, &sp->sv2_maxread);
md_get_uint32le(mdp, &sp->sv2_maxwrite);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint16le(mdp, &sec_buf_off);
err = md_get_uint16le(mdp, &sec_buf_len);
if (err != 0)
goto errout;
md_get_uint32le(mdp, NULL);
if (sec_buf_len != 0) {
int skip = (int)sec_buf_off - (SMB2_HDRLEN + 64);
if (skip < 0) {
err = EBADRPC;
goto errout;
}
if (skip > 0) {
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
if (wk->wk_u_auth_rlen < sec_buf_len) {
SMBSDEBUG("vc_auth_rbuf too small");
wk->wk_u_auth_rlen = sec_buf_len;
err = EMSGSIZE;
goto errout;
}
wk->wk_u_auth_rlen = sec_buf_len;
err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr,
sec_buf_len, MB_MUSER);
if (err) {
goto errout;
}
}
if ((sp->sv2_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) ||
(vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED))
will_sign = B_TRUE;
if (vcp->vc_vopt & SMBVOPT_ANONYMOUS)
will_sign = B_FALSE;
SMBSDEBUG("Security signatures: %d", (int)will_sign);
if (will_sign)
vcp->vc_flags |= SMBV_SIGNING;
sp->sv_caps = (SMB_CAP_UNICODE |
SMB_CAP_LARGE_FILES |
SMB_CAP_STATUS32 |
SMB_CAP_LARGE_READX |
SMB_CAP_LARGE_WRITEX |
SMB_CAP_EXT_SECURITY);
if (sp->sv2_capabilities & SMB2_CAP_DFS)
sp->sv_caps |= SMB_CAP_DFS;
if (sp->sv_proto >= SMB2_DIALECT_0300 &&
(sp->sv2_capabilities & SMB2_CAP_ENCRYPTION) != 0) {
nsmb_crypt_init_mech(vcp);
}
if (sp->sv2_maxread < 0x8000) {
SMBSDEBUG("maxread too small\n");
err = ENOTSUP;
goto errout;
}
if (sp->sv2_maxwrite < 0x8000) {
SMBSDEBUG("maxwrite too small\n");
err = ENOTSUP;
goto errout;
}
if (sp->sv2_maxtransact < 0x4000) {
SMBSDEBUG("maxtransact too small\n");
err = ENOTSUP;
goto errout;
}
vcp->vc_rxmax = sp->sv2_maxread;
vcp->vc_wxmax = sp->sv2_maxwrite;
vcp->vc_txmax = sp->sv2_maxtransact;
smb_rq_done(rqp);
return (0);
errout:
smb_rq_done(rqp);
if (err == 0)
err = EBADRPC;
return (err);
}
int
smb2_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
{
smbioc_ssn_work_t *wk = &vcp->vc_work;
struct smb_rq *rqp = NULL;
struct mbchain *mbp = NULL;
struct mdchain *mdp = NULL;
char *sb;
int err, ret;
uint16_t sblen;
uint16_t length = 0;
uint16_t session_flags;
uint16_t sec_buf_off;
uint16_t sec_buf_len;
uint8_t security_mode;
if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
security_mode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
} else {
security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
}
sb = wk->wk_u_auth_wbuf.lp_ptr;
sblen = (uint16_t)wk->wk_u_auth_wlen;
err = smb_rq_alloc(VCTOCP(vcp), SMB2_SESSION_SETUP, scred, &rqp);
if (err != 0) {
ret = err;
goto out;
}
rqp->sr2_creditsrequested = smb2_ss_req_credits;
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 25);
mb_put_uint8(mbp, 0);
mb_put_uint8(mbp, security_mode);
mb_put_uint32le(mbp, smb2_clnt_caps);
mb_put_uint32le(mbp, 0);
mb_put_uint16le(mbp, mbp->mb_count + 12);
mb_put_uint16le(mbp, sblen);
mb_put_uint64le(mbp, vcp->vc2_prev_session_id);
err = mb_put_mem(mbp, sb, sblen, MB_MUSER);
if (err != 0) {
ret = err;
goto out;
}
ret = smb2_rq_internal(rqp, smb2_timo_logon);
if (ret != 0)
goto out;
switch (rqp->sr_error) {
case NT_STATUS_SUCCESS:
break;
case NT_STATUS_MORE_PROCESSING_REQUIRED:
ret = EINPROGRESS;
break;
default:
ret = EAUTH;
goto out;
}
if (vcp->vc2_session_id == 0)
vcp->vc2_session_id = rqp->sr2_rspsessionid;
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &length);
if (length != 9) {
ret = EBADRPC;
goto out;
}
md_get_uint16le(mdp, &session_flags);
md_get_uint16le(mdp, &sec_buf_off);
err = md_get_uint16le(mdp, &sec_buf_len);
if (err != 0) {
ret = err;
goto out;
}
if (sec_buf_len != 0) {
int skip = (int)sec_buf_off - (SMB2_HDRLEN + 8);
if (skip < 0) {
ret = EBADRPC;
goto out;
}
if (skip > 0) {
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
if (wk->wk_u_auth_rlen < sec_buf_len) {
SMBSDEBUG("vc_auth_rbuf too small");
wk->wk_u_auth_rlen = sec_buf_len;
ret = EMSGSIZE;
goto out;
}
wk->wk_u_auth_rlen = sec_buf_len;
err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr,
sec_buf_len, MB_MUSER);
if (err != 0) {
ret = err;
goto out;
}
}
if (ret == 0) {
vcp->vc_sopt.sv2_sessflags = session_flags;
if ((vcp->vc_sopt.sv2_sessflags &
SMB2_SESSION_FLAG_ENCRYPT_DATA) != 0 &&
vcp->vc3_crypt_mech == NULL) {
cmn_err(CE_NOTE, "SMB server requires encryption"
" but no crypto mechanism found");
ret = ENOTSUP;
goto out;
}
}
out:
if (err != 0 && err != EINPROGRESS) {
vcp->vc2_session_id = 0;
}
if (rqp)
smb_rq_done(rqp);
return (ret);
}
int
smb2_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred)
{
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
if (vcp->vc2_session_id == 0)
return (0);
error = smb_rq_alloc(VCTOCP(vcp), SMB2_LOGOFF, scred, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 4);
mb_put_uint16le(mbp, 0);
rqp->sr_flags |= SMBR_NORECONNECT;
error = smb2_rq_internal(rqp, 5);
smb_rq_done(rqp);
return (error);
}
int
smb2_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
{
struct smb_vc *vcp;
struct smb_rq *rqp = NULL;
struct mbchain *mbp;
struct mdchain *mdp;
char *unc_name = NULL;
int error, unc_len;
uint16_t plen, *plenp;
uint16_t options = 0;
uint_t cnt0;
uint32_t net_stype;
uint16_t structure_size = 0;
uint8_t smb2stype;
vcp = SSTOVC(ssp);
error = smb_rq_alloc(VCTOCP(vcp), SMB2_TREE_CONNECT, scred, &rqp);
if (error)
return (error);
unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name);
unc_name = kmem_alloc(unc_len, KM_SLEEP);
(void) snprintf(unc_name, unc_len, "\\\\%s\\%s",
vcp->vc_srvname, ssp->ss_name);
SMBSDEBUG("unc_name: \"%s\"", unc_name);
mbp = &rqp->sr_rq;
mb_put_uint16le(mbp, 9);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, 72);
plenp = mb_reserve(mbp, 2);
cnt0 = mbp->mb_count;
error = smb_put_dmem(mbp, vcp, unc_name, unc_len - 1,
SMB_CS_NONE, NULL);
if (error)
goto out;
plen = (uint16_t)(mbp->mb_count - cnt0);
*plenp = htoles(plen);
rqp->sr_flags |= SMBR_NOINTR_RECV;
error = smb2_rq_simple(rqp);
SMBSDEBUG("%d\n", error);
if (error) {
SMBERROR("(%s) failed, status=0x%x",
unc_name, rqp->sr_error);
goto out;
}
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &structure_size);
if (structure_size != 16) {
error = EBADRPC;
goto out;
}
md_get_uint8(mdp, &smb2stype);
md_get_uint8(mdp, NULL);
md_get_uint32le(mdp, &ssp->ss2_share_flags);
md_get_uint32le(mdp, &ssp->ss2_share_caps);
error = md_get_uint32le(mdp, NULL);
if (error)
goto out;
if ((ssp->ss2_share_flags & SMB2_SHAREFLAG_ENCRYPT_DATA) != 0 &&
vcp->vc3_crypt_mech == NULL) {
cmn_err(CE_NOTE, "SMB share requires encryption"
" but no crypto mechanism found");
error = ENOTSUP;
goto out;
}
switch (smb2stype) {
case SMB2_SHARE_TYPE_DISK:
net_stype = STYPE_DISKTREE;
break;
case SMB2_SHARE_TYPE_PIPE:
net_stype = STYPE_IPC;
break;
case SMB2_SHARE_TYPE_PRINT:
net_stype = STYPE_PRINTQ;
break;
default:
net_stype = STYPE_UNKNOWN;
break;
}
ssp->ss_type = net_stype;
if (ssp->ss2_share_caps & SMB2_SHARE_CAP_DFS)
options |= SMB_SHARE_IS_IN_DFS;
SMB_SS_LOCK(ssp);
ssp->ss2_tree_id = rqp->sr2_rsptreeid;
ssp->ss_vcgenid = vcp->vc_genid;
ssp->ss_options = options;
ssp->ss_flags |= SMBS_CONNECTED;
SMB_SS_UNLOCK(ssp);
out:
if (unc_name)
kmem_free(unc_name, unc_len);
smb_rq_done(rqp);
return (error);
}
int
smb2_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
{
struct smb_vc *vcp;
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
if (ssp->ss2_tree_id == SMB2_TID_UNKNOWN)
return (0);
vcp = SSTOVC(ssp);
error = smb_rq_alloc(VCTOCP(vcp), SMB2_TREE_DISCONNECT, scred, &rqp);
if (error)
return (error);
rqp->sr_share = ssp;
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 4);
mb_put_uint16le(mbp, 0);
rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
error = smb2_rq_simple_timed(rqp, 5);
smb_rq_done(rqp);
ssp->ss2_tree_id = SMB2_TID_UNKNOWN;
return (error);
}
static int
put_name_skip_slash(struct mbchain *mbp, struct mbchain *name_mbp)
{
mblk_t *m;
if (name_mbp == NULL)
return (0);
m = name_mbp->mb_top;
if (m == NULL)
return (0);
m = dupmsg(m);
if (m == NULL)
return (ENOSR);
if (MBLKL(m) >= 2 &&
m->b_rptr[0] == '\\' &&
m->b_rptr[1] == '\0')
m->b_rptr += 2;
return (mb_put_mbuf(mbp, m));
}
int
smb2_smb_ntcreate(
struct smb_share *ssp,
struct mbchain *name_mb,
struct mbchain *cctx_in,
struct mdchain *cctx_out,
uint32_t cr_flags,
uint32_t req_acc,
uint32_t efa,
uint32_t share_acc,
uint32_t open_disp,
uint32_t createopt,
uint32_t impersonate,
struct smb_cred *scrp,
smb2fid_t *fidp,
uint32_t *cr_act_p,
struct smbfattr *fap)
{
struct smbfattr fa;
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
uint16_t *name_offp;
uint16_t *name_lenp;
uint32_t *cctx_offp;
uint32_t *cctx_lenp;
uint32_t rcc_off, rcc_len;
smb2fid_t smb2_fid;
uint64_t llongint;
uint32_t longint, createact;
uint_t off, len;
int error;
uint16_t StructSize = 57;
bzero(&fa, sizeof (fa));
error = smb_rq_alloc(SSTOCP(ssp), SMB2_CREATE, scrp, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, StructSize);
mb_put_uint8(mbp, 0);
mb_put_uint8(mbp, SMB2_OPLOCK_LEVEL_NONE);
mb_put_uint32le(mbp, impersonate);
mb_put_uint64le(mbp, cr_flags);
mb_put_uint64le(mbp, 0);
mb_put_uint32le(mbp, req_acc);
mb_put_uint32le(mbp, efa);
mb_put_uint32le(mbp, share_acc);
mb_put_uint32le(mbp, open_disp);
mb_put_uint32le(mbp, createopt);
name_offp = mb_reserve(mbp, 2);
name_lenp = mb_reserve(mbp, 2);
cctx_offp = mb_reserve(mbp, 4);
cctx_lenp = mb_reserve(mbp, 4);
off = mbp->mb_count;
*name_offp = htoles((uint16_t)off);
error = put_name_skip_slash(mbp, name_mb);
if (error)
goto out;
len = mbp->mb_count - off;
*name_lenp = htoles((uint16_t)len);
if (cctx_in != NULL) {
off = mbp->mb_count;
*cctx_offp = htolel((uint32_t)off);
mb_put_mbchain(mbp, cctx_in);
len = mbp->mb_count - off;
*cctx_lenp = htolel((uint32_t)len);
} else {
*cctx_offp = 0;
*cctx_lenp = 0;
}
if (mbp->mb_count < (StructSize + SMB2_HDRLEN))
mb_put_uint8(mbp, 0);
rqp->sr_flags |= SMBR_NOINTR_RECV;
error = smb2_rq_simple_timed(rqp, smb2_timo_open);
if (error)
goto out;
smb_rq_getreply(rqp, &mdp);
error = md_get_uint16le(mdp, &StructSize);
if (StructSize != 89) {
error = EBADRPC;
goto out;
}
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint32le(mdp, &createact);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_createtime);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_atime);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_mtime);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_ctime);
md_get_uint64le(mdp, &llongint);
fa.fa_allocsz = llongint;
md_get_uint64le(mdp, &llongint);
fa.fa_size = llongint;
md_get_uint32le(mdp, &longint);
fa.fa_attr = longint;
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, &smb2_fid.fid_persistent);
error = md_get_uint64le(mdp, &smb2_fid.fid_volatile);
if (error)
goto out;
error = md_get_uint32le(mdp, &rcc_off);
if (error)
goto out;
error = md_get_uint32le(mdp, &rcc_len);
if (error)
goto out;
if (rcc_len != 0) {
int skip = (int)rcc_off - (SMB2_HDRLEN + 88);
if (skip < 0) {
error = EBADRPC;
goto out;
}
if (skip > 0) {
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
if (cctx_out != NULL) {
mblk_t *m = NULL;
error = md_get_mbuf(mdp, rcc_len, &m);
if (error)
goto out;
md_initm(cctx_out, m);
}
}
out:
smb_rq_done(rqp);
if (error)
return (error);
*fidp = smb2_fid;
if (cr_act_p)
*cr_act_p = createact;
if (fap)
*fap = fa;
return (0);
}
int
smb2_smb_close(struct smb_share *ssp, smb2fid_t *fid, struct smb_cred *scrp)
{
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_CLOSE, scrp, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 24);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint64le(mbp, fid->fid_persistent);
mb_put_uint64le(mbp, fid->fid_volatile);
rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
error = smb2_rq_simple(rqp);
smb_rq_done(rqp);
return (error);
}
int
smb2_smb_ioctl(
struct smb_share *ssp,
smb2fid_t *fid,
struct mbchain *data_in,
struct mdchain *data_out,
uint32_t *data_out_sz,
uint32_t ctl_code,
struct smb_cred *scrp)
{
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
uint32_t *data_in_offp;
uint32_t *data_in_lenp;
uint32_t data_out_off;
uint32_t data_out_len;
uint16_t length = 0;
uint_t off, len;
int error;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_IOCTL, scrp, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 57);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, ctl_code);
mb_put_uint64le(mbp, fid->fid_persistent);
mb_put_uint64le(mbp, fid->fid_volatile);
data_in_offp = mb_reserve(mbp, 4);
data_in_lenp = mb_reserve(mbp, 4);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, *data_out_sz);
mb_put_uint32le(mbp, SMB2_IOCTL_IS_FSCTL);
mb_put_uint32le(mbp, 0);
if (data_in != NULL) {
off = mbp->mb_count;
*data_in_offp = htolel((uint32_t)off);
mb_put_mbchain(mbp, data_in);
len = mbp->mb_count - off;
*data_in_lenp = htolel((uint32_t)len);
} else {
*data_in_offp = 0;
*data_in_lenp = 0;
}
error = smb2_rq_simple_timed(rqp, smb2_timo_default);
if (error)
goto out;
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &length);
if (length != 49) {
error = EBADRPC;
goto out;
}
md_get_uint16le(mdp, NULL);
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint32le(mdp, NULL);
md_get_uint32le(mdp, NULL);
error = md_get_uint32le(mdp, &data_out_off);
if (error)
goto out;
error = md_get_uint32le(mdp, &data_out_len);
if (error)
goto out;
md_get_uint32le(mdp, NULL);
md_get_uint32le(mdp, NULL);
*data_out_sz = data_out_len;
if (data_out_len != 0) {
int skip = (int)data_out_off - (SMB2_HDRLEN + 48);
if (skip < 0) {
error = EBADRPC;
goto out;
}
if (skip > 0) {
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
if (data_out != NULL) {
mblk_t *m = NULL;
error = md_get_mbuf(mdp, data_out_len, &m);
if (error)
goto out;
md_initm(data_out, m);
}
}
out:
smb_rq_done(rqp);
return (error);
}
int
smb2_smb_read(smb_fh_t *fhp, uint32_t *lenp,
uio_t *uiop, smb_cred_t *scred, int timo)
{
struct smb_share *ssp = FHTOSS(fhp);
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
int error;
uint64_t off64 = uiop->uio_loffset;
uint32_t rlen;
uint16_t length = 0;
uint8_t data_offset;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_READ, scred, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 49);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, *lenp);
mb_put_uint64le(mbp, off64);
mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent);
mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile);
mb_put_uint32le(mbp, 1);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint8(mbp, 0);
if (timo == 0)
timo = smb2_timo_read;
error = smb2_rq_simple_timed(rqp, timo);
if (error)
goto out;
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &length);
if (length != 17) {
error = EBADRPC;
goto out;
}
md_get_uint8(mdp, &data_offset);
md_get_uint8(mdp, NULL);
error = md_get_uint32le(mdp, &rlen);
if (error)
goto out;
md_get_uint32le(mdp, NULL);
md_get_uint32le(mdp, NULL);
if (data_offset < (SMB2_HDRLEN + 16)) {
error = EBADRPC;
goto out;
}
if (data_offset > (SMB2_HDRLEN + 16)) {
int skip = data_offset - (SMB2_HDRLEN + 16);
md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
if (rlen == 0) {
*lenp = rlen;
goto out;
}
if (rlen > *lenp) {
SMBSDEBUG("bad server! rlen %d, len %d\n",
rlen, *lenp);
rlen = *lenp;
}
error = md_get_uio(mdp, uiop, rlen);
if (error)
goto out;
*lenp = rlen;
out:
smb_rq_done(rqp);
return (error);
}
int
smb2_smb_write(smb_fh_t *fhp, uint32_t *lenp,
uio_t *uiop, smb_cred_t *scred, int timo)
{
struct smb_share *ssp = FHTOSS(fhp);
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
int error;
uint64_t off64 = uiop->uio_loffset;
uint32_t rlen;
uint16_t data_offset;
uint16_t length = 0;
error = smb_rq_alloc(SSTOCP(ssp), SMB2_WRITE, scred, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 49);
data_offset = SMB2_HDRLEN + 48;
mb_put_uint16le(mbp, data_offset);
mb_put_uint32le(mbp, *lenp);
mb_put_uint64le(mbp, off64);
mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent);
mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
error = mb_put_uio(mbp, uiop, *lenp);
if (error)
goto out;
if (timo == 0)
timo = smb2_timo_write;
error = smb2_rq_simple_timed(rqp, timo);
if (error)
goto out;
smb_rq_getreply(rqp, &mdp);
md_get_uint16le(mdp, &length);
if (length != 17) {
error = EBADRPC;
goto out;
}
md_get_uint16le(mdp, NULL);
error = md_get_uint32le(mdp, &rlen);
if (error)
goto out;
md_get_uint32le(mdp, NULL);
md_get_uint32le(mdp, NULL);
*lenp = rlen;
out:
smb_rq_done(rqp);
return (error);
}
int
smb2_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
{
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
error = smb_rq_alloc(VCTOCP(vcp), SMB2_ECHO, scred, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
mb_put_uint16le(mbp, 4);
mb_put_uint16le(mbp, 0);
rqp->sr_flags |= SMBR_NORECONNECT;
error = smb2_rq_internal(rqp, timo);
smb_rq_done(rqp);
return (error);
}