Print this page
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
NEX-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)

*** 32,41 **** --- 32,43 ---- * $Id: smb_rq.c,v 1.29 2005/02/11 01:44:17 lindak Exp $ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Portions Copyright (C) 2001 - 2013 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/time.h>
*** 49,62 **** --- 51,66 ---- #include <sys/sdt.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> /* * How long to wait before restarting a request (after reconnect) */ #define SMB_RCNDELAY 2 /* seconds */
*** 67,79 **** */ #define SMBMAXRESTARTS 0 static int smb_rq_reply(struct smb_rq *rqp); static int smb_rq_enqueue(struct smb_rq *rqp); - static int smb_rq_getenv(struct smb_connobj *layer, - struct smb_vc **vcpp, struct smb_share **sspp); static int smb_rq_new(struct smb_rq *rqp, uchar_t cmd); static int smb_t2_reply(struct smb_t2rq *t2p); static int smb_nt_reply(struct smb_ntrq *ntp); --- 71,82 ---- */ #define SMBMAXRESTARTS 0 static int smb_rq_reply(struct smb_rq *rqp); + static int smb_rq_parsehdr(struct smb_rq *rqp); static int smb_rq_enqueue(struct smb_rq *rqp); static int smb_rq_new(struct smb_rq *rqp, uchar_t cmd); static int smb_t2_reply(struct smb_t2rq *t2p); static int smb_nt_reply(struct smb_ntrq *ntp);
*** 104,113 **** --- 107,117 ---- struct smb_rq **rqpp) { struct smb_rq *rqp; int error; + // XXX kmem cache? rqp = (struct smb_rq *)kmem_alloc(sizeof (struct smb_rq), KM_SLEEP); if (rqp == NULL) return (ENOMEM); error = smb_rq_init(rqp, layer, cmd, scred); if (error) {
*** 145,155 **** * when creating and completing requests. */ rqp->sr_rexmit = SMBMAXRESTARTS; rqp->sr_cred = scred; /* Note: ref hold done by caller. */ - rqp->sr_pid = (uint16_t)ddi_get_pid(); error = smb_rq_new(rqp, cmd); return (error); } --- 149,158 ----
*** 161,198 **** int error; ASSERT(rqp != NULL); rqp->sr_sendcnt = 0; - rqp->sr_cmd = cmd; mb_done(mbp); md_done(&rqp->sr_rp); error = mb_init(mbp); if (error) return (error); /* ! * Is this the right place to save the flags? */ rqp->sr_rqflags = vcp->vc_hflags; rqp->sr_rqflags2 = vcp->vc_hflags2; /* * The SMB header is filled in later by * smb_rq_fillhdr (see below) * Just reserve space here. */ mb_put_mem(mbp, NULL, SMB_HDRLEN, MB_MZERO); return (0); } /* * Given a request with it's body already composed, * rewind to the start and fill in the SMB header. ! * This is called after the request is enqueued, * so we have the final MID, seq num. etc. */ void smb_rq_fillhdr(struct smb_rq *rqp) { --- 164,224 ---- int error; ASSERT(rqp != NULL); rqp->sr_sendcnt = 0; mb_done(mbp); md_done(&rqp->sr_rp); error = mb_init(mbp); if (error) return (error); + if (vcp->vc_flags & SMBV_SMB2) { /* ! * SMB2 request initialization */ + rqp->sr2_command = cmd; + rqp->sr2_creditcharge = 1; + rqp->sr2_creditsrequested = 1; + rqp->sr_pid = 0xFEFF; /* Made up, just like Windows */ + rqp->sr2_rqflags = 0; + if ((vcp->vc_flags & SMBV_SIGNING) != 0 && + vcp->vc_mackey != NULL) { + rqp->sr2_rqflags |= SMB2_FLAGS_SIGNED; + } + + /* + * The SMB2 header is filled in later by + * smb2_rq_fillhdr (see smb2_rq.c) + * Just reserve space here. + */ + mb_put_mem(mbp, NULL, SMB2_HDRLEN, MB_MZERO); + } else { + /* + * SMB1 request initialization + */ + rqp->sr_cmd = cmd; + rqp->sr_pid = (uint32_t)ddi_get_pid(); rqp->sr_rqflags = vcp->vc_hflags; rqp->sr_rqflags2 = vcp->vc_hflags2; /* * The SMB header is filled in later by * smb_rq_fillhdr (see below) * Just reserve space here. */ mb_put_mem(mbp, NULL, SMB_HDRLEN, MB_MZERO); + } return (0); } /* * Given a request with it's body already composed, * rewind to the start and fill in the SMB header. ! * This is called when the request is enqueued, * so we have the final MID, seq num. etc. */ void smb_rq_fillhdr(struct smb_rq *rqp) {
*** 215,225 **** mb_put_uint16le(mbp, rqp->sr_rqflags2); mb_put_uint16le(mbp, 0); /* pid-high */ mb_put_mem(mbp, NULL, 8, MB_MZERO); /* MAC sig. (later) */ mb_put_uint16le(mbp, 0); /* reserved */ mb_put_uint16le(mbp, rqp->sr_rqtid); ! mb_put_uint16le(mbp, rqp->sr_pid); mb_put_uint16le(mbp, rqp->sr_rquid); mb_put_uint16le(mbp, rqp->sr_mid); /* This will free the mblk from dupb. */ mb_done(mbp); --- 241,251 ---- mb_put_uint16le(mbp, rqp->sr_rqflags2); mb_put_uint16le(mbp, 0); /* pid-high */ mb_put_mem(mbp, NULL, 8, MB_MZERO); /* MAC sig. (later) */ mb_put_uint16le(mbp, 0); /* reserved */ mb_put_uint16le(mbp, rqp->sr_rqtid); ! mb_put_uint16le(mbp, (uint16_t)rqp->sr_pid); mb_put_uint16le(mbp, rqp->sr_rquid); mb_put_uint16le(mbp, rqp->sr_mid); /* This will free the mblk from dupb. */ mb_done(mbp);
*** 279,288 **** --- 305,316 ---- { struct smb_vc *vcp = rqp->sr_vc; struct smb_share *ssp = rqp->sr_share; int error = 0; + ASSERT((vcp->vc_flags & SMBV_SMB2) == 0); + /* * Normal requests may initiate a reconnect, * and/or wait for state changes to finish. * Some requests set the NORECONNECT flag * to avoid all that (i.e. tree discon)
*** 323,338 **** * Store them in the request. */ ok_out: rqp->sr_rquid = vcp->vc_smbuid; rqp->sr_rqtid = ssp ? ssp->ss_tid : SMB_TID_UNKNOWN; ! error = smb_iod_addrq(rqp); return (error); } /* * Mark location of the word count, which is filled in later by * smb_rw_wend(). Also initialize the counter that it uses * to figure out what value to fill in. * * Note that the word count happens to be 8-bit. --- 351,430 ---- * Store them in the request. */ ok_out: rqp->sr_rquid = vcp->vc_smbuid; rqp->sr_rqtid = ssp ? ssp->ss_tid : SMB_TID_UNKNOWN; ! error = smb1_iod_addrq(rqp); return (error); } /* + * Used by the IOD thread during connection setup, + * and for smb_echo after network timeouts. Note that + * unlike smb_rq_simple, callers must check sr_error. + */ + int + smb_rq_internal(struct smb_rq *rqp, int timeout) + { + struct smb_vc *vcp = rqp->sr_vc; + int error; + + ASSERT((vcp->vc_flags & SMBV_SMB2) == 0); + + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = timeout; /* in seconds */ + rqp->sr_state = SMBRQ_NOTSENT; + + /* + * In-line smb_rq_enqueue(rqp) here, as we don't want it + * trying to reconnect etc. for an internal request. + */ + rqp->sr_rquid = vcp->vc_smbuid; + rqp->sr_rqtid = SMB_TID_UNKNOWN; + rqp->sr_flags |= SMBR_INTERNAL; + error = smb1_iod_addrq(rqp); + if (error != 0) + return (error); + + /* + * In-line a variant of smb_rq_reply(rqp) here as we may + * need to do custom parsing for SMB1-to-SMB2 negotiate. + */ + if (rqp->sr_timo == SMBNOREPLYWAIT) { + smb_iod_removerq(rqp); + return (0); + } + + error = smb_iod_waitrq_int(rqp); + if (error) + return (error); + + /* + * If the request was signed, validate the + * signature on the response. + */ + if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { + error = smb_rq_verify(rqp); + if (error) + return (error); + } + + /* + * Parse the SMB header. + */ + error = smb_rq_parsehdr(rqp); + + /* + * Skip the error translation smb_rq_reply does. + * Callers of this expect "raw" NT status. + */ + + return (error); + } + + /* * Mark location of the word count, which is filled in later by * smb_rw_wend(). Also initialize the counter that it uses * to figure out what value to fill in. * * Note that the word count happens to be 8-bit.
*** 396,414 **** rqp->sr_bcount[0] = bcnt & 0xFF; rqp->sr_bcount[1] = (bcnt >> 8); } int - smb_rq_intr(struct smb_rq *rqp) - { - if (rqp->sr_flags & SMBR_INTR) - return (EINTR); - - return (0); - } - - static int smb_rq_getenv(struct smb_connobj *co, struct smb_vc **vcpp, struct smb_share **sspp) { struct smb_vc *vcp = NULL; struct smb_share *ssp = NULL; --- 488,497 ----
*** 455,472 **** return (error); } /* ! * Wait for reply on the request */ static int smb_rq_reply(struct smb_rq *rqp) { ! struct mdchain *mdp = &rqp->sr_rp; ! u_int8_t tb; ! int error, rperror = 0; if (rqp->sr_timo == SMBNOREPLYWAIT) { smb_iod_removerq(rqp); return (0); } --- 538,553 ---- return (error); } /* ! * Wait for a reply to this request, then parse it. */ static int smb_rq_reply(struct smb_rq *rqp) { ! int error; if (rqp->sr_timo == SMBNOREPLYWAIT) { smb_iod_removerq(rqp); return (0); }
*** 486,503 **** } /* * Parse the SMB header */ ! error = md_get_uint32le(mdp, NULL); ! if (error) return (error); ! error = md_get_uint8(mdp, &tb); ! error = md_get_uint32le(mdp, &rqp->sr_error); ! error = md_get_uint8(mdp, &rqp->sr_rpflags); ! error = md_get_uint16le(mdp, &rqp->sr_rpflags2); if (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) { /* * Do a special check for STATUS_BUFFER_OVERFLOW; * it's not an error. */ if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) { --- 567,593 ---- } /* * Parse the SMB header */ ! error = smb_rq_parsehdr(rqp); ! if (error != 0) return (error); ! ! if (rqp->sr_error != 0) { if (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) { + error = smb_maperr32(rqp->sr_error); + } else { + uint8_t errClass = rqp->sr_error & 0xff; + uint16_t errCode = rqp->sr_error >> 16; + /* Convert to NT status */ + rqp->sr_error = smb_doserr2status(errClass, errCode); + error = smb_maperror(errClass, errCode); + } + } + + if (error != 0) { /* * Do a special check for STATUS_BUFFER_OVERFLOW; * it's not an error. */ if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) {
*** 504,541 **** /* * Don't report it as an error to our caller; * they can look at rqp->sr_error if they * need to know whether we got a * STATUS_BUFFER_OVERFLOW. - * XXX - should we do that for all errors - * where (error & 0xC0000000) is 0x80000000, - * i.e. all warnings? */ - rperror = 0; - } else - rperror = smb_maperr32(rqp->sr_error); - } else { - rqp->sr_errclass = rqp->sr_error & 0xff; - rqp->sr_serror = rqp->sr_error >> 16; - rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror); - } - if (rperror == EMOREDATA) { - rperror = E2BIG; rqp->sr_flags |= SMBR_MOREDATA; ! } else rqp->sr_flags &= ~SMBR_MOREDATA; ! error = md_get_uint32le(mdp, NULL); ! error = md_get_uint32le(mdp, NULL); ! error = md_get_uint32le(mdp, NULL); ! error = md_get_uint16le(mdp, &rqp->sr_rptid); ! error = md_get_uint16le(mdp, &rqp->sr_rppid); ! error = md_get_uint16le(mdp, &rqp->sr_rpuid); error = md_get_uint16le(mdp, &rqp->sr_rpmid); ! return ((error) ? error : rperror); } #define ALIGN4(a) (((a) + 3) & ~3) --- 594,662 ---- /* * Don't report it as an error to our caller; * they can look at rqp->sr_error if they * need to know whether we got a * STATUS_BUFFER_OVERFLOW. */ rqp->sr_flags |= SMBR_MOREDATA; ! error = 0; ! } ! } else { rqp->sr_flags &= ~SMBR_MOREDATA; + } ! return (error); ! } ! /* ! * Parse the SMB header ! */ ! static int ! smb_rq_parsehdr(struct smb_rq *rqp) ! { ! struct mdchain mdp_save; ! struct mdchain *mdp = &rqp->sr_rp; ! u_int8_t tb, sig[4]; ! int error; ! ! /* ! * Parse the signature. The reader already checked that ! * the signature is valid. Here we just have to check ! * for SMB1-to-SMB2 negotiate. Caller handles an EPROTO ! * as a signal that we got an SMB2 reply. If we return ! * EPROTO, rewind the mdchain back where it was. ! */ ! mdp_save = *mdp; ! error = md_get_mem(mdp, sig, 4, MB_MSYSTEM); ! if (error) ! return (error); ! if (sig[0] != SMB_HDR_V1) { ! if (rqp->sr_cmd == SMB_COM_NEGOTIATE) { ! *mdp = mdp_save; ! return (EPROTO); ! } ! return (EBADRPC); ! } ! ! /* Check cmd */ ! error = md_get_uint8(mdp, &tb); ! if (tb != rqp->sr_cmd) ! return (EBADRPC); ! ! md_get_uint32le(mdp, &rqp->sr_error); ! md_get_uint8(mdp, &rqp->sr_rpflags); ! md_get_uint16le(mdp, &rqp->sr_rpflags2); ! ! /* Skip: pid-high(2), MAC sig(8), reserved(2) */ ! md_get_mem(mdp, NULL, 12, MB_MSYSTEM); ! ! md_get_uint16le(mdp, &rqp->sr_rptid); ! md_get_uint16le(mdp, &rqp->sr_rppid); ! md_get_uint16le(mdp, &rqp->sr_rpuid); error = md_get_uint16le(mdp, &rqp->sr_rpmid); ! return (error); } #define ALIGN4(a) (((a) + 3) & ~3)
*** 1132,1142 **** 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)) --- 1253,1263 ---- if (error) goto bad; mb_put_mbuf(mbp, m); } smb_rq_bend(rqp); ! error = smb1_iod_multirq(rqp); if (error) goto bad; } /* while left params or data */ error = smb_t2_reply(t2p); if (error && !(t2p->t2_flags & SMBT2_MOREDATA))
*** 1343,1353 **** 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_nt_reply(ntp); if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) --- 1464,1474 ---- if (error) goto bad; mb_put_mbuf(mbp, m); } smb_rq_bend(rqp); ! error = smb1_iod_multirq(rqp); if (error) goto bad; } /* while left params or data */ error = smb_nt_reply(ntp); if (error && !(ntp->nt_flags & SMBT2_MOREDATA))
*** 1435,1440 **** --- 1556,1641 ---- delay(SEC_TO_TICK(SMB_RCNDELAY)); } mutex_exit(&(ntp)->nt_lock); } return (error); + } + + /* + * Run an SMB transact named pipe. + * Note: send_mb is consumed. + */ + int + smb_t2_xnp(struct smb_share *ssp, uint16_t fid, + struct mbchain *send_mb, struct mdchain *recv_md, + uint32_t *data_out_sz, /* max / returned */ + uint32_t *more, struct smb_cred *scrp) + { + struct smb_t2rq *t2p = NULL; + mblk_t *m; + uint16_t setup[2]; + int err; + + setup[0] = TRANS_TRANSACT_NAMED_PIPE; + setup[1] = fid; + + t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP); + err = smb_t2_init(t2p, SSTOCP(ssp), setup, 2, scrp); + if (err) { + *data_out_sz = 0; + goto out; + } + + t2p->t2_setupcount = 2; + t2p->t2_setupdata = setup; + + t2p->t_name = "\\PIPE\\"; + t2p->t_name_len = 6; + + t2p->t2_maxscount = 0; + t2p->t2_maxpcount = 0; + t2p->t2_maxdcount = (uint16_t)*data_out_sz; + + /* Transmit parameters (none) */ + + /* + * Transmit data + * + * Copy the mb, and clear the source so we + * don't end up with a double free. + */ + t2p->t2_tdata = *send_mb; + bzero(send_mb, sizeof (*send_mb)); + + /* + * Run the request + */ + err = smb_t2_request(t2p); + + /* No returned parameters. */ + + if (err == 0 && (m = t2p->t2_rdata.md_top) != NULL) { + /* + * Received data + * + * Copy the mdchain, and clear the source so we + * don't end up with a double free. + */ + *data_out_sz = msgdsize(m); + md_initm(recv_md, m); + t2p->t2_rdata.md_top = NULL; + } else { + *data_out_sz = 0; + } + + if (t2p->t2_sr_error == NT_STATUS_BUFFER_OVERFLOW) + *more = 1; + + out: + if (t2p != NULL) { + /* Note: t2p->t_name no longer allocated */ + smb_t2_done(t2p); + kmem_free(t2p, sizeof (*t2p)); + } + + return (err); }