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)
        
*** 31,43 ****
   *
   * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
   */
  
  /*
-  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  #include <sys/param.h>
  #include <sys/kmem.h>
  #include <sys/systm.h>
--- 31,44 ----
   *
   * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
   */
  
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
+  *
+  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
   */
  
  #include <sys/param.h>
  #include <sys/kmem.h>
  #include <sys/systm.h>
*** 50,90 ****
  #include <sys/sunddi.h>
  #include <sys/cmn_err.h>
  
  #include <netsmb/smb_osdep.h>
  
  #include <netsmb/smb.h>
  #include <netsmb/smb_conn.h>
  #include <netsmb/smb_rq.h>
  #include <netsmb/smb_subr.h>
  #include <netsmb/smb_dev.h>
  
  static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
  
  /*
-  * Ioctl function for SMBIOC_FLAGS2
-  */
- int
- smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags)
- {
-         struct smb_vc *vcp = NULL;
- 
-         /* This ioctl requires a session. */
-         if ((vcp = sdp->sd_vc) == NULL)
-                 return (ENOTCONN);
- 
-         /*
-          * Return the flags2 value.
-          */
-         if (ddi_copyout(&vcp->vc_hflags2, (void *)arg,
-             sizeof (u_int16_t), flags))
-                 return (EFAULT);
- 
-         return (0);
- }
- 
- /*
   * Ioctl function for SMBIOC_GETSSNKEY
   * Size copied out is SMBIOC_HASH_SZ.
   *
   * The RPC library needs this for encrypting things
   * like "set password" requests.  This is called
--- 51,70 ----
  #include <sys/sunddi.h>
  #include <sys/cmn_err.h>
  
  #include <netsmb/smb_osdep.h>
  
+ #include <smb/winioctl.h>
  #include <netsmb/smb.h>
  #include <netsmb/smb_conn.h>
  #include <netsmb/smb_rq.h>
  #include <netsmb/smb_subr.h>
  #include <netsmb/smb_dev.h>
  
  static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
  
  /*
   * Ioctl function for SMBIOC_GETSSNKEY
   * Size copied out is SMBIOC_HASH_SZ.
   *
   * The RPC library needs this for encrypting things
   * like "set password" requests.  This is called
*** 103,338 ****
                  return (ENOTCONN);
  
          /*
           * Return the session key.
           */
!         if (ddi_copyout(vcp->vc_ssn_key, (void *)arg,
              SMBIOC_HASH_SZ, flags))
                  return (EFAULT);
  
          return (0);
  }
  
  /*
!  * Ioctl function for SMBIOC_REQUEST
   */
  int
! smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct smb_share *ssp;
!         smbioc_rq_t *ioc = NULL;
!         struct smb_rq *rqp = NULL;
!         struct mbchain *mbp;
!         struct mdchain *mdp;
!         uint32_t rsz;
          int err, mbseg;
  
!         /* This ioctl requires a share. */
!         if ((ssp = sdp->sd_share) == NULL)
!                 return (ENOTCONN);
  
!         smb_credinit(&scred, cr);
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
  
-         /* See ddi_copyin, ddi_copyout */
-         mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
- 
          /*
!          * Lots of SMB commands could be safe, but
!          * these are the only ones used by libsmbfs.
           */
!         switch (ioc->ioc_cmd) {
!                 /* These are OK */
!         case SMB_COM_CLOSE:
!         case SMB_COM_FLUSH:
!         case SMB_COM_NT_CREATE_ANDX:
!         case SMB_COM_OPEN_PRINT_FILE:
!         case SMB_COM_CLOSE_PRINT_FILE:
!                 break;
! 
!         default:
!                 err = EPERM;
!                 goto out;
!         }
! 
!         err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp);
          if (err)
                  goto out;
  
-         mbp = &rqp->sr_rq;
-         err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg);
- 
-         err = smb_rq_simple(rqp);
-         if (err == 0) {
                  /*
!                  * This may have been an open, so save the
!                  * generation ID of the share, which we
!                  * check before trying read or write.
                   */
!                 sdp->sd_vcgenid = ssp->ss_vcgenid;
! 
!                 /*
!                  * Have reply data. to copyout.
!                  * SMB header already parsed.
!                  */
!                 mdp = &rqp->sr_rp;
!                 rsz = msgdsize(mdp->md_top) - SMB_HDRLEN;
!                 if (ioc->ioc_rbufsz < rsz) {
!                         err = EOVERFLOW;
!                         goto out;
                  }
-                 ioc->ioc_rbufsz = rsz;
-                 err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg);
-                 if (err)
-                         goto out;
- 
-         }
- 
-         ioc->ioc_errclass = rqp->sr_errclass;
-         ioc->ioc_serror = rqp->sr_serror;
-         ioc->ioc_error = rqp->sr_error;
-         (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
- 
- out:
-         if (rqp != NULL)
-                 smb_rq_done(rqp); /* free rqp */
-         kmem_free(ioc, sizeof (*ioc));
          smb_credrele(&scred);
  
!         return (err);
! 
! }
! 
! /*
!  * Ioctl function for SMBIOC_T2RQ
!  */
! int
! smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
! {
!         struct smb_cred scred;
!         struct smb_share *ssp;
!         smbioc_t2rq_t *ioc = NULL;
!         struct smb_t2rq *t2p = NULL;
!         struct mdchain *mdp;
!         int err, len, mbseg;
! 
!         /* This ioctl requires a share. */
!         if ((ssp = sdp->sd_share) == NULL)
!                 return (ENOTCONN);
! 
!         smb_credinit(&scred, cr);
!         ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
!         if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
!                 err = EFAULT;
!                 goto out;
!         }
! 
!         /* See ddi_copyin, ddi_copyout */
!         mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
! 
!         if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) {
!                 err = EINVAL;
!                 goto out;
!         }
! 
!         /*
!          * Fill in the FID for libsmbfs transact named pipe.
!          */
!         if (ioc->ioc_setupcnt > 1 && ioc->ioc_setup[1] == 0xFFFF) {
!                 if (sdp->sd_vcgenid != ssp->ss_vcgenid) {
!                         err = ESTALE;
!                         goto out;
!                 }
!                 ioc->ioc_setup[1] = (uint16_t)sdp->sd_smbfid;
!         }
! 
!         t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP);
!         err = smb_t2_init(t2p, SSTOCP(ssp),
!             ioc->ioc_setup, ioc->ioc_setupcnt, &scred);
!         if (err)
!                 goto out;
!         t2p->t2_setupcount = ioc->ioc_setupcnt;
!         t2p->t2_setupdata  = ioc->ioc_setup;
! 
!         /* This ioc member is a fixed-size array. */
!         if (ioc->ioc_name[0]) {
!                 /* Get the name length - carefully! */
!                 ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0';
!                 t2p->t_name_len = strlen(ioc->ioc_name);
!                 t2p->t_name = ioc->ioc_name;
!         }
!         t2p->t2_maxscount = 0;
!         t2p->t2_maxpcount = ioc->ioc_rparamcnt;
!         t2p->t2_maxdcount = ioc->ioc_rdatacnt;
! 
!         /* Transmit parameters */
!         err = smb_cpdatain(&t2p->t2_tparam,
!             ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg);
!         if (err)
!                 goto out;
! 
!         /* Transmit data */
!         err = smb_cpdatain(&t2p->t2_tdata,
!             ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg);
!         if (err)
!                 goto out;
! 
!         err = smb_t2_request(t2p);
! 
!         /* Copyout returned parameters. */
!         mdp = &t2p->t2_rparam;
!         if (err == 0 && mdp->md_top != NULL) {
!                 /* User's buffer large enough? */
!                 len = m_fixhdr(mdp->md_top);
!                 if (len > ioc->ioc_rparamcnt) {
                          err = EMSGSIZE;
                          goto out;
                  }
!                 ioc->ioc_rparamcnt = (ushort_t)len;
!                 err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg);
                  if (err)
                          goto out;
          } else
!                 ioc->ioc_rparamcnt = 0;
  
!         /* Copyout returned data. */
!         mdp = &t2p->t2_rdata;
!         if (err == 0 && mdp->md_top != NULL) {
!                 /* User's buffer large enough? */
!                 len = m_fixhdr(mdp->md_top);
!                 if (len > ioc->ioc_rdatacnt) {
!                         err = EMSGSIZE;
!                         goto out;
                  }
-                 ioc->ioc_rdatacnt = (ushort_t)len;
-                 err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg);
-                 if (err)
-                         goto out;
-         } else
-                 ioc->ioc_rdatacnt = 0;
  
-         ioc->ioc_errclass = t2p->t2_sr_errclass;
-         ioc->ioc_serror = t2p->t2_sr_serror;
-         ioc->ioc_error = t2p->t2_sr_error;
-         ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2;
- 
          (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
  
- 
  out:
-         if (t2p != NULL) {
-                 /* Note: t2p->t_name no longer allocated */
-                 smb_t2_done(t2p);
-                 kmem_free(t2p, sizeof (*t2p));
-         }
          kmem_free(ioc, sizeof (*ioc));
-         smb_credrele(&scred);
  
          return (err);
  }
  
  /* helper for _t2request */
--- 83,187 ----
                  return (ENOTCONN);
  
          /*
           * Return the session key.
           */
!         if (vcp->vc_ssnkey == NULL ||
!             vcp->vc_ssnkeylen < SMBIOC_HASH_SZ)
!                 return (EINVAL);
!         if (ddi_copyout(vcp->vc_ssnkey, (void *)arg,
              SMBIOC_HASH_SZ, flags))
                  return (EFAULT);
  
          return (0);
  }
  
  /*
!  * Ioctl function for SMBIOC_XACTNP (transact named pipe)
   */
  int
! smb_usr_xnp(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct smb_share *ssp;
!         struct smb_fh *fhp;
!         smbioc_xnp_t *ioc = NULL;
!         struct mbchain send_mb;
!         struct mdchain recv_md;
!         uint32_t rdlen;
          int err, mbseg;
  
!         /* This ioctl requires a file handle. */
!         if ((fhp = sdp->sd_fh) == NULL)
!                 return (EINVAL);
!         ssp = FHTOSS(fhp);
  
!         /* After reconnect, force close+reopen */
!         if (fhp->fh_vcgenid != ssp->ss_vcgenid)
!                 return (ESTALE);
! 
!         bzero(&send_mb, sizeof (send_mb));
!         bzero(&recv_md, sizeof (recv_md));
! 
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
  
          /*
!          * Copyin the send data, into an mbchain,
!          * save output buffer size.
           */
!         mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
!         err = smb_cpdatain(&send_mb, ioc->ioc_tdlen, ioc->ioc_tdata, mbseg);
          if (err)
                  goto out;
+         rdlen = ioc->ioc_rdlen;
  
          /*
!          * Run the SMB2 ioctl or SMB1 trans2
           */
!         smb_credinit(&scred, cr);
!         if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
!                 err = smb2_smb_ioctl(ssp, &fhp->fh_fid2,
!                     &send_mb, &recv_md, &rdlen,
!                     FSCTL_PIPE_TRANSCEIVE, &scred);
!         } else {
!                 err = smb_t2_xnp(ssp, fhp->fh_fid1,
!                     &send_mb, &recv_md, &rdlen,
!                     &ioc->ioc_more, &scred);
          }
          smb_credrele(&scred);
  
!         /* Copyout returned data. */
!         if (err == 0 && recv_md.md_top != NULL) {
!                 /* User's buffer large enough for copyout? */
!                 size_t len = m_fixhdr(recv_md.md_top);
!                 if (len > ioc->ioc_rdlen) {
                          err = EMSGSIZE;
                          goto out;
                  }
!                 err = md_get_mem(&recv_md, ioc->ioc_rdata, len, mbseg);
                  if (err)
                          goto out;
          } else
!                 ioc->ioc_rdlen = 0;
  
!         /* Tell caller received length */
!         if (rdlen <= ioc->ioc_rdlen) {
!                 /* Normal case */
!                 ioc->ioc_rdlen = rdlen;
!         } else {
!                 /* Buffer overlow. Leave ioc_rdlen */
!                 ioc->ioc_more = 1;
          }
  
          (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
  
  out:
          kmem_free(ioc, sizeof (*ioc));
  
          return (err);
  }
  
  /* helper for _t2request */
*** 356,381 ****
  int
  smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct smb_share *ssp;
          smbioc_rw_t *ioc = NULL;
          struct iovec aiov[1];
          struct uio  auio;
-         uint16_t fh;
          int err;
          uio_rw_t rw;
  
!         /* This ioctl requires a share. */
!         if ((ssp = sdp->sd_share) == NULL)
!                 return (ENOTCONN);
  
          /* After reconnect, force close+reopen */
!         if (sdp->sd_vcgenid != ssp->ss_vcgenid)
                  return (ESTALE);
  
-         smb_credinit(&scred, cr);
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
--- 205,230 ----
  int
  smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct smb_share *ssp;
+         struct smb_fh *fhp;
          smbioc_rw_t *ioc = NULL;
          struct iovec aiov[1];
          struct uio  auio;
          int err;
          uio_rw_t rw;
  
!         /* This ioctl requires a file handle. */
!         if ((fhp = sdp->sd_fh) == NULL)
!                 return (EINVAL);
!         ssp = FHTOSS(fhp);
  
          /* After reconnect, force close+reopen */
!         if (fhp->fh_vcgenid != ssp->ss_vcgenid)
                  return (ESTALE);
  
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
*** 390,408 ****
          default:
                  err = ENODEV;
                  goto out;
          }
  
-         /*
-          * If caller passes -1 in ioc_fh, then
-          * use the FID from SMBIOC_NTCREATE.
-          */
-         if (ioc->ioc_fh == -1)
-                 fh = (uint16_t)sdp->sd_smbfid;
-         else
-                 fh = (uint16_t)ioc->ioc_fh;
- 
          aiov[0].iov_base = ioc->ioc_base;
          aiov[0].iov_len = (size_t)ioc->ioc_cnt;
  
          auio.uio_iov = aiov;
          auio.uio_iovcnt = 1;
--- 239,248 ----
*** 410,420 ****
          auio.uio_segflg = (flags & FKIOCTL) ?
              UIO_SYSSPACE : UIO_USERSPACE;
          auio.uio_fmode = 0;
          auio.uio_resid = (size_t)ioc->ioc_cnt;
  
!         err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0);
  
          /*
           * On return ioc_cnt holds the
           * number of bytes transferred.
           */
--- 250,262 ----
          auio.uio_segflg = (flags & FKIOCTL) ?
              UIO_SYSSPACE : UIO_USERSPACE;
          auio.uio_fmode = 0;
          auio.uio_resid = (size_t)ioc->ioc_cnt;
  
!         smb_credinit(&scred, cr);
!         err = smb_rwuio(fhp, rw, &auio, &scred, 0);
!         smb_credrele(&scred);
  
          /*
           * On return ioc_cnt holds the
           * number of bytes transferred.
           */
*** 422,432 ****
  
          (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
  
  out:
          kmem_free(ioc, sizeof (*ioc));
-         smb_credrele(&scred);
  
          return (err);
  }
  
  /*
--- 264,273 ----
*** 437,460 ****
  smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct mbchain name_mb;
          struct smb_share *ssp;
          smbioc_ntcreate_t *ioc = NULL;
-         uint16_t fid;
          int err, nmlen;
  
          /* This ioctl requires a share. */
          if ((ssp = sdp->sd_share) == NULL)
                  return (ENOTCONN);
  
!         /* Must not be already open. */
!         if (sdp->sd_smbfid != -1)
                  return (EINVAL);
  
-         mb_init(&name_mb);
-         smb_credinit(&scred, cr);
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
--- 278,301 ----
  smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct mbchain name_mb;
          struct smb_share *ssp;
+         struct smb_fh *fhp = NULL;
          smbioc_ntcreate_t *ioc = NULL;
          int err, nmlen;
  
+         mb_init(&name_mb);
+ 
          /* This ioctl requires a share. */
          if ((ssp = sdp->sd_share) == NULL)
                  return (ENOTCONN);
  
!         /* Must not already have a file handle. */
!         if (sdp->sd_fh != NULL)
                  return (EINVAL);
  
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
*** 466,497 ****
              ioc->ioc_name, nmlen,
              SMB_CS_NONE, NULL);
          if (err != 0)
                  goto out;
  
!         /* Do the OtW open, save the FID. */
          err = smb_smb_ntcreate(ssp, &name_mb,
              0,  /* create flags */
              ioc->ioc_req_acc,
              ioc->ioc_efattr,
              ioc->ioc_share_acc,
              ioc->ioc_open_disp,
              ioc->ioc_creat_opts,
              NTCREATEX_IMPERSONATION_IMPERSONATION,
              &scred,
!             &fid,
              NULL,
              NULL);
          if (err != 0)
                  goto out;
  
!         sdp->sd_smbfid = fid;
!         sdp->sd_vcgenid = ssp->ss_vcgenid;
  
  out:
          kmem_free(ioc, sizeof (*ioc));
-         smb_credrele(&scred);
          mb_done(&name_mb);
  
          return (err);
  }
  
--- 307,349 ----
              ioc->ioc_name, nmlen,
              SMB_CS_NONE, NULL);
          if (err != 0)
                  goto out;
  
!         err = smb_fh_create(ssp, &fhp);
!         if (err != 0)
!                 goto out;
! 
!         /*
!          * Do the OtW open, save the FID.
!          */
!         smb_credinit(&scred, cr);
          err = smb_smb_ntcreate(ssp, &name_mb,
              0,  /* create flags */
              ioc->ioc_req_acc,
              ioc->ioc_efattr,
              ioc->ioc_share_acc,
              ioc->ioc_open_disp,
              ioc->ioc_creat_opts,
              NTCREATEX_IMPERSONATION_IMPERSONATION,
              &scred,
!             fhp,
              NULL,
              NULL);
+         smb_credrele(&scred);
          if (err != 0)
                  goto out;
  
!         fhp->fh_rights = ioc->ioc_req_acc;
!         smb_fh_opened(fhp);
!         sdp->sd_fh = fhp;
!         fhp = NULL;
  
  out:
+         if (fhp != NULL)
+                 smb_fh_rele(fhp);
          kmem_free(ioc, sizeof (*ioc));
          mb_done(&name_mb);
  
          return (err);
  }
  
*** 500,581 ****
   * SMBIOC_PRINTJOB
   */
  int
  smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
          struct smb_cred scred;
          struct smb_share *ssp;
          smbioc_printjob_t *ioc = NULL;
!         uint16_t fid;
!         int err;
  
          /* This ioctl requires a share. */
          if ((ssp = sdp->sd_share) == NULL)
                  return (ENOTCONN);
  
          /* The share must be a print queue. */
          if (ssp->ss_type != STYPE_PRINTQ)
                  return (EINVAL);
  
!         /* Must not be already open. */
!         if (sdp->sd_smbfid != -1)
                  return (EINVAL);
  
          smb_credinit(&scred, cr);
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
          ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
  
!         /* Do the OtW open, save the FID. */
          err = smb_smb_open_prjob(ssp, ioc->ioc_title,
              ioc->ioc_setuplen, ioc->ioc_prmode,
!             &scred, &fid);
          if (err != 0)
                  goto out;
  
!         sdp->sd_smbfid = fid;
!         sdp->sd_vcgenid = ssp->ss_vcgenid;
  
  out:
          kmem_free(ioc, sizeof (*ioc));
!         smb_credrele(&scred);
  
          return (err);
  }
  
  /*
   * Helper for nsmb_ioctl case
   * SMBIOC_CLOSEFH
   */
  int
  smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
  {
!         struct smb_cred scred;
!         struct smb_share *ssp;
!         uint16_t fid;
!         int err;
  
!         /* This ioctl requires a share. */
!         if ((ssp = sdp->sd_share) == NULL)
!                 return (ENOTCONN);
  
!         if (sdp->sd_smbfid == -1)
!                 return (0);
!         fid = (uint16_t)sdp->sd_smbfid;
!         sdp->sd_smbfid = -1;
  
!         smb_credinit(&scred, cr);
!         if (ssp->ss_type == STYPE_PRINTQ)
!                 err = smb_smb_close_prjob(ssp, fid, &scred);
!         else
!                 err = smb_smb_close(ssp, fid, NULL, &scred);
!         smb_credrele(&scred);
! 
!         return (err);
  }
  
  /*
   * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
   * Find or create a session (a.k.a. "VC" in here)
--- 352,476 ----
   * SMBIOC_PRINTJOB
   */
  int
  smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
+         static const char invalid_chars[] = SMB_FILENAME_INVALID_CHARS;
          struct smb_cred scred;
+         struct mbchain name_mb;
          struct smb_share *ssp;
+         struct smb_fh *fhp = NULL;
          smbioc_printjob_t *ioc = NULL;
!         int err, cklen, nmlen;
!         uint32_t access = SA_RIGHT_FILE_WRITE_DATA |
!             SA_RIGHT_FILE_READ_ATTRIBUTES;
  
+         mb_init(&name_mb);
+ 
          /* This ioctl requires a share. */
          if ((ssp = sdp->sd_share) == NULL)
                  return (ENOTCONN);
  
          /* The share must be a print queue. */
          if (ssp->ss_type != STYPE_PRINTQ)
                  return (EINVAL);
  
!         /* Must not already have a file handle. */
!         if (sdp->sd_fh != NULL)
                  return (EINVAL);
  
          smb_credinit(&scred, cr);
          ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
          if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
                  err = EFAULT;
                  goto out;
          }
+ 
+         /*
+          * Use the print job title as the file name to open, but
+          * check for invalid characters first.  See the notes in
+          * libsmbfs/smb/print.c about job name sanitizing.
+          */
          ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
+         nmlen = strnlen(ioc->ioc_title, SMBIOC_MAX_NAME-1);
+         cklen = strcspn(ioc->ioc_title, invalid_chars);
+         if (cklen < nmlen) {
+                 err = EINVAL;
+                 goto out;
+         }
  
!         /* Build name_mb */
!         err = smb_put_dmem(&name_mb, SSTOVC(ssp),
!             ioc->ioc_title, nmlen,
!             SMB_CS_NONE, NULL);
!         if (err != 0)
!                 goto out;
! 
!         err = smb_fh_create(ssp, &fhp);
!         if (err != 0)
!                 goto out;
! 
!         /*
!          * Do the OtW open, save the FID.
!          */
!         smb_credinit(&scred, cr);
!         if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
!                 err = smb2_smb_ntcreate(ssp, &name_mb,
!                     NULL, NULL, /* cctx in, out */
!                     0,  /* create flags */
!                     access,
!                     SMB_EFA_NORMAL,
!                     NTCREATEX_SHARE_ACCESS_NONE,
!                     NTCREATEX_DISP_CREATE,
!                     NTCREATEX_OPTIONS_NON_DIRECTORY_FILE,
!                     NTCREATEX_IMPERSONATION_IMPERSONATION,
!                     &scred,
!                     &fhp->fh_fid2,
!                     NULL,
!                     NULL);
!         } else {
                  err = smb_smb_open_prjob(ssp, ioc->ioc_title,
                      ioc->ioc_setuplen, ioc->ioc_prmode,
!                     &scred, &fhp->fh_fid1);
!         }
!         smb_credrele(&scred);
          if (err != 0)
                  goto out;
  
!         fhp->fh_rights = access;
!         smb_fh_opened(fhp);
!         sdp->sd_fh = fhp;
!         fhp = NULL;
  
  out:
+         if (fhp != NULL)
+                 smb_fh_rele(fhp);
          kmem_free(ioc, sizeof (*ioc));
!         mb_done(&name_mb);
  
          return (err);
  }
  
  /*
   * Helper for nsmb_ioctl case
   * SMBIOC_CLOSEFH
   */
+ /*ARGSUSED*/
  int
  smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
  {
!         struct smb_fh *fhp;
  
!         /* This ioctl requires a file handle. */
!         if ((fhp = sdp->sd_fh) == NULL)
!                 return (EINVAL);
!         sdp->sd_fh = NULL;
  
!         smb_fh_close(fhp);
!         smb_fh_rele(fhp);
  
!         return (0);
  }
  
  /*
   * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
   * Find or create a session (a.k.a. "VC" in here)
*** 823,850 ****
          sdp->sd_level = SMBL_VC;
  
          return (0);
  }
  
- 
  /*
!  * Ioctl function: SMBIOC_IOD_WORK
!  *
!  * Become the reader (IOD) thread, until either the connection is
!  * reset by the server, or until the connection is idle longer than
!  * some max time. (max idle time not yet implemented)
   */
  int
! smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
  {
!         struct smb_vc *vcp = NULL;
          int err = 0;
  
!         /* Must have a valid session. */
          if ((vcp = sdp->sd_vc) == NULL)
                  return (EINVAL);
!         if (vcp->vc_flags & SMBV_GONE)
                  return (EINVAL);
  
          /*
           * Is there already an IOD for this VC?
           * (Should never happen.)
--- 718,743 ----
          sdp->sd_level = SMBL_VC;
  
          return (0);
  }
  
  /*
!  * Ioctl handler for all SMBIOC_IOD_...
   */
  int
! smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
  {
!         struct smb_vc *vcp;
          int err = 0;
  
!         /* Must be the IOD. */
!         if ((sdp->sd_flags & NSMBFL_IOD) == 0)
!                 return (EINVAL);
!         /* Must have a VC and no share. */
          if ((vcp = sdp->sd_vc) == NULL)
                  return (EINVAL);
!         if (sdp->sd_share != NULL)
                  return (EINVAL);
  
          /*
           * Is there already an IOD for this VC?
           * (Should never happen.)
*** 857,899 ****
          SMB_VC_UNLOCK(vcp);
          if (err)
                  return (err);
  
          /*
!          * Copy the "work" state, etc. into the VC
!          * The MAC key is copied separately.
           */
          if (ddi_copyin((void *)arg, &vcp->vc_work,
              sizeof (smbioc_ssn_work_t), flags)) {
                  err = EFAULT;
                  goto out;
          }
!         if (vcp->vc_u_maclen) {
!                 vcp->vc_mackeylen = vcp->vc_u_maclen;
!                 vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP);
!                 if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey,
!                     vcp->vc_mackeylen, flags)) {
!                         err = EFAULT;
!                         goto out;
!                 }
!         }
  
!         err = smb_iod_vc_work(vcp, cr);
  
!         /* Caller wants state here. */
!         vcp->vc_work.wk_out_state = vcp->vc_state;
  
!         (void) ddi_copyout(&vcp->vc_work, (void *)arg,
!             sizeof (smbioc_ssn_work_t), flags);
  
! out:
!         if (vcp->vc_mackey) {
!                 kmem_free(vcp->vc_mackey, vcp->vc_mackeylen);
!                 vcp->vc_mackey = NULL;
!                 vcp->vc_mackeylen = 0;
          }
  
          /*
           * The IOD thread is leaving the driver.  Clear iod_thr,
           * and wake up anybody waiting for us to quit.
           */
          SMB_VC_LOCK(vcp);
--- 750,806 ----
          SMB_VC_UNLOCK(vcp);
          if (err)
                  return (err);
  
          /*
!          * Copy the "work" state, etc. into the VC,
!          * and back to the caller on the way out.
!          * Clear the "out only" part.
           */
          if (ddi_copyin((void *)arg, &vcp->vc_work,
              sizeof (smbioc_ssn_work_t), flags)) {
                  err = EFAULT;
                  goto out;
          }
!         vcp->vc_work.wk_out_state = 0;
  
!         switch (cmd) {
  
!         case SMBIOC_IOD_CONNECT:
!                 err = nsmb_iod_connect(vcp, cr);
!                 break;
  
!         case SMBIOC_IOD_NEGOTIATE:
!                 err = nsmb_iod_negotiate(vcp, cr);
!                 break;
  
!         case SMBIOC_IOD_SSNSETUP:
!                 err = nsmb_iod_ssnsetup(vcp, cr);
!                 break;
! 
!         case SMBIOC_IOD_WORK:
!                 err = smb_iod_vc_work(vcp, flags, cr);
!                 break;
! 
!         case SMBIOC_IOD_IDLE:
!                 err = smb_iod_vc_idle(vcp);
!                 break;
! 
!         case SMBIOC_IOD_RCFAIL:
!                 err = smb_iod_vc_rcfail(vcp);
!                 break;
! 
!         default:
!                 err = ENOTTY;
!                 break;
          }
  
+ out:
+         vcp->vc_work.wk_out_state = vcp->vc_state;
+         (void) ddi_copyout(&vcp->vc_work, (void *)arg,
+             sizeof (smbioc_ssn_work_t), flags);
+ 
          /*
           * The IOD thread is leaving the driver.  Clear iod_thr,
           * and wake up anybody waiting for us to quit.
           */
          SMB_VC_LOCK(vcp);
*** 902,970 ****
          SMB_VC_UNLOCK(vcp);
  
          return (err);
  }
  
- /*
-  * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL
-  *
-  * Wait for user-level requests to be enqueued on this session,
-  * and then return to the user-space helper, which will then
-  * initiate a reconnect, etc.
-  */
  int
! smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags)
  {
!         struct smb_vc *vcp = NULL;
!         int err = 0;
  
-         /* Must have a valid session. */
-         if ((vcp = sdp->sd_vc) == NULL)
-                 return (EINVAL);
-         if (vcp->vc_flags & SMBV_GONE)
-                 return (EINVAL);
- 
          /*
!          * Is there already an IOD for this VC?
!          * (Should never happen.)
           */
!         SMB_VC_LOCK(vcp);
!         if (vcp->iod_thr == NULL)
!                 vcp->iod_thr = curthread;
!         else
!                 err = EEXIST;
!         SMB_VC_UNLOCK(vcp);
!         if (err)
!                 return (err);
  
!         /* nothing to copyin */
! 
          switch (cmd) {
!         case SMBIOC_IOD_IDLE:
!                 err = smb_iod_vc_idle(vcp);
                  break;
  
          case SMBIOC_IOD_RCFAIL:
!                 err = smb_iod_vc_rcfail(vcp);
                  break;
  
          default:
                  err = ENOTTY;
!                 goto out;
          }
  
!         /* Both of these ioctls copy out the new state. */
!         (void) ddi_copyout(&vcp->vc_state, (void *)arg,
!             sizeof (int), flags);
  
- out:
-         /*
-          * The IOD thread is leaving the driver.  Clear iod_thr,
-          * and wake up anybody waiting for us to quit.
-          */
-         SMB_VC_LOCK(vcp);
-         vcp->iod_thr = NULL;
-         cv_broadcast(&vcp->vc_statechg);
-         SMB_VC_UNLOCK(vcp);
- 
          return (err);
  }
--- 809,914 ----
          SMB_VC_UNLOCK(vcp);
  
          return (err);
  }
  
  int
! smb_usr_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
  {
!         int err;
  
          /*
!          * Serialize ioctl calls.  The smb_usr_... functions
!          * don't expect concurrent calls on a given sdp.
           */
!         mutex_enter(&sdp->sd_lock);
!         if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) {
!                 mutex_exit(&sdp->sd_lock);
!                 return (EBUSY);
!         }
!         sdp->sd_flags |= NSMBFL_IOCTL;
!         mutex_exit(&sdp->sd_lock);
  
!         err = 0;
          switch (cmd) {
!         case SMBIOC_GETVERS:
!                 (void) ddi_copyout(&nsmb_version, (void *)arg,
!                     sizeof (nsmb_version), flags);
                  break;
  
+         case SMBIOC_GETSSNKEY:
+                 err = smb_usr_get_ssnkey(sdp, arg, flags);
+                 break;
+ 
+         case SMBIOC_DUP_DEV:
+                 err = smb_usr_dup_dev(sdp, arg, flags);
+                 break;
+ 
+         case SMBIOC_XACTNP:
+                 err = smb_usr_xnp(sdp, arg, flags, cr);
+                 break;
+ 
+         case SMBIOC_READ:
+         case SMBIOC_WRITE:
+                 err = smb_usr_rw(sdp, cmd, arg, flags, cr);
+                 break;
+ 
+         case SMBIOC_NTCREATE:
+                 err = smb_usr_ntcreate(sdp, arg, flags, cr);
+                 break;
+ 
+         case SMBIOC_PRINTJOB:
+                 err = smb_usr_printjob(sdp, arg, flags, cr);
+                 break;
+ 
+         case SMBIOC_CLOSEFH:
+                 err = smb_usr_closefh(sdp, cr);
+                 break;
+ 
+         case SMBIOC_SSN_CREATE:
+         case SMBIOC_SSN_FIND:
+                 err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
+                 break;
+ 
+         case SMBIOC_SSN_KILL:
+         case SMBIOC_SSN_RELE:
+                 err = smb_usr_drop_ssn(sdp, cmd);
+                 break;
+ 
+         case SMBIOC_TREE_CONNECT:
+         case SMBIOC_TREE_FIND:
+                 err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
+                 break;
+ 
+         case SMBIOC_TREE_KILL:
+         case SMBIOC_TREE_RELE:
+                 err = smb_usr_drop_tree(sdp, cmd);
+                 break;
+ 
+         case SMBIOC_IOD_CONNECT:
+         case SMBIOC_IOD_NEGOTIATE:
+         case SMBIOC_IOD_SSNSETUP:
+         case SMBIOC_IOD_WORK:
+         case SMBIOC_IOD_IDLE:
          case SMBIOC_IOD_RCFAIL:
!                 err = smb_usr_iod_ioctl(sdp, cmd, arg, flags, cr);
                  break;
  
+         case SMBIOC_PK_ADD:
+         case SMBIOC_PK_DEL:
+         case SMBIOC_PK_CHK:
+         case SMBIOC_PK_DEL_OWNER:
+         case SMBIOC_PK_DEL_EVERYONE:
+                 err = smb_pkey_ioctl(cmd, arg, flags, cr);
+                 break;
+ 
          default:
                  err = ENOTTY;
!                 break;
          }
  
!         mutex_enter(&sdp->sd_lock);
!         sdp->sd_flags &= ~NSMBFL_IOCTL;
!         mutex_exit(&sdp->sd_lock);
  
          return (err);
  }