1 /*
   2  * Copyright (c) 2000-2001 Boris Popov
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. All advertising materials mentioning features or use of this software
  14  *    must display the following acknowledgement:
  15  *    This product includes software developed by Boris Popov.
  16  * 4. Neither the name of the author nor the names of any co-contributors
  17  *    may be used to endorse or promote products derived from this software
  18  *    without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30  * SUCH DAMAGE.
  31  *
  32  * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
  33  */
  34 
  35 /*
  36  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  37  * Use is subject to license terms.
  38  *
  39  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  40  */
  41 
  42 #include <sys/param.h>
  43 #include <sys/kmem.h>
  44 #include <sys/systm.h>
  45 #include <sys/policy.h>
  46 #include <sys/conf.h>
  47 #include <sys/proc.h>
  48 #include <sys/fcntl.h>
  49 #include <sys/file.h>
  50 #include <sys/socket.h>
  51 #include <sys/sunddi.h>
  52 #include <sys/cmn_err.h>
  53 
  54 #include <netsmb/smb_osdep.h>
  55 
  56 #include <smb/winioctl.h>
  57 #include <netsmb/smb.h>
  58 #include <netsmb/smb_conn.h>
  59 #include <netsmb/smb_rq.h>
  60 #include <netsmb/smb_subr.h>
  61 #include <netsmb/smb_dev.h>
  62 
  63 static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
  64 
  65 /*
  66  * Ioctl function for SMBIOC_GETSSNKEY
  67  * Size copied out is SMBIOC_HASH_SZ.
  68  *
  69  * The RPC library needs this for encrypting things
  70  * like "set password" requests.  This is called
  71  * with an active RPC binding, so the connection
  72  * will already be active (but this checks).
  73  */
  74 int
  75 smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags)
  76 {
  77         struct smb_vc *vcp = NULL;
  78 
  79         /* This ioctl requires an active session. */
  80         if ((vcp = sdp->sd_vc) == NULL)
  81                 return (ENOTCONN);
  82         if (vcp->vc_state != SMBIOD_ST_VCACTIVE)
  83                 return (ENOTCONN);
  84 
  85         /*
  86          * Return the session key.
  87          */
  88         if (vcp->vc_ssnkey == NULL ||
  89             vcp->vc_ssnkeylen < SMBIOC_HASH_SZ)
  90                 return (EINVAL);
  91         if (ddi_copyout(vcp->vc_ssnkey, (void *)arg,
  92             SMBIOC_HASH_SZ, flags))
  93                 return (EFAULT);
  94 
  95         return (0);
  96 }
  97 
  98 /*
  99  * Ioctl function for SMBIOC_XACTNP (transact named pipe)
 100  */
 101 int
 102 smb_usr_xnp(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
 103 {
 104         struct smb_cred scred;
 105         struct smb_share *ssp;
 106         struct smb_fh *fhp;
 107         smbioc_xnp_t *ioc = NULL;
 108         struct mbchain send_mb;
 109         struct mdchain recv_md;
 110         uint32_t rdlen;
 111         int err, mbseg;
 112 
 113         /* This ioctl requires a file handle. */
 114         if ((fhp = sdp->sd_fh) == NULL)
 115                 return (EINVAL);
 116         ssp = FHTOSS(fhp);
 117 
 118         /* After reconnect, force close+reopen */
 119         if (fhp->fh_vcgenid != ssp->ss_vcgenid)
 120                 return (ESTALE);
 121 
 122         bzero(&send_mb, sizeof (send_mb));
 123         bzero(&recv_md, sizeof (recv_md));
 124 
 125         ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
 126         if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
 127                 err = EFAULT;
 128                 goto out;
 129         }
 130 
 131         /*
 132          * Copyin the send data, into an mbchain,
 133          * save output buffer size.
 134          */
 135         mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
 136         err = smb_cpdatain(&send_mb, ioc->ioc_tdlen, ioc->ioc_tdata, mbseg);
 137         if (err)
 138                 goto out;
 139         rdlen = ioc->ioc_rdlen;
 140 
 141         /*
 142          * Run the SMB2 ioctl or SMB1 trans2
 143          */
 144         smb_credinit(&scred, cr);
 145         if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
 146                 err = smb2_smb_ioctl(ssp, &fhp->fh_fid2,
 147                     &send_mb, &recv_md, &rdlen,
 148                     FSCTL_PIPE_TRANSCEIVE, &scred);
 149         } else {
 150                 err = smb_t2_xnp(ssp, fhp->fh_fid1,
 151                     &send_mb, &recv_md, &rdlen,
 152                     &ioc->ioc_more, &scred);
 153         }
 154         smb_credrele(&scred);
 155 
 156         /* Copyout returned data. */
 157         if (err == 0 && recv_md.md_top != NULL) {
 158                 /* User's buffer large enough for copyout? */
 159                 size_t len = m_fixhdr(recv_md.md_top);
 160                 if (len > ioc->ioc_rdlen) {
 161                         err = EMSGSIZE;
 162                         goto out;
 163                 }
 164                 err = md_get_mem(&recv_md, ioc->ioc_rdata, len, mbseg);
 165                 if (err)
 166                         goto out;
 167         } else
 168                 ioc->ioc_rdlen = 0;
 169 
 170         /* Tell caller received length */
 171         if (rdlen <= ioc->ioc_rdlen) {
 172                 /* Normal case */
 173                 ioc->ioc_rdlen = rdlen;
 174         } else {
 175                 /* Buffer overlow. Leave ioc_rdlen */
 176                 ioc->ioc_more = 1;
 177         }
 178 
 179         (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
 180 
 181 out:
 182         kmem_free(ioc, sizeof (*ioc));
 183 
 184         return (err);
 185 }
 186 
 187 /* helper for _t2request */
 188 static int
 189 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
 190 {
 191         int error;
 192 
 193         if (len == 0)
 194                 return (0);
 195         error = mb_init(mbp);
 196         if (error)
 197                 return (error);
 198         return (mb_put_mem(mbp, data, len, mbseg));
 199 }
 200 
 201 /*
 202  * Helper for nsmb_ioctl cases
 203  * SMBIOC_READ, SMBIOC_WRITE
 204  */
 205 int
 206 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
 207 {
 208         struct smb_cred scred;
 209         struct smb_share *ssp;
 210         struct smb_fh *fhp;
 211         smbioc_rw_t *ioc = NULL;
 212         struct iovec aiov[1];
 213         struct uio  auio;
 214         int err;
 215         uio_rw_t rw;
 216 
 217         /* This ioctl requires a file handle. */
 218         if ((fhp = sdp->sd_fh) == NULL)
 219                 return (EINVAL);
 220         ssp = FHTOSS(fhp);
 221 
 222         /* After reconnect, force close+reopen */
 223         if (fhp->fh_vcgenid != ssp->ss_vcgenid)
 224                 return (ESTALE);
 225 
 226         ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
 227         if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
 228                 err = EFAULT;
 229                 goto out;
 230         }
 231 
 232         switch (cmd) {
 233         case SMBIOC_READ:
 234                 rw = UIO_READ;
 235                 break;
 236         case SMBIOC_WRITE:
 237                 rw = UIO_WRITE;
 238                 break;
 239         default:
 240                 err = ENODEV;
 241                 goto out;
 242         }
 243 
 244         aiov[0].iov_base = ioc->ioc_base;
 245         aiov[0].iov_len = (size_t)ioc->ioc_cnt;
 246 
 247         auio.uio_iov = aiov;
 248         auio.uio_iovcnt = 1;
 249         auio.uio_loffset = ioc->ioc_offset;
 250         auio.uio_segflg = (flags & FKIOCTL) ?
 251             UIO_SYSSPACE : UIO_USERSPACE;
 252         auio.uio_fmode = 0;
 253         auio.uio_resid = (size_t)ioc->ioc_cnt;
 254 
 255         smb_credinit(&scred, cr);
 256         err = smb_rwuio(fhp, rw, &auio, &scred, 0);
 257         smb_credrele(&scred);
 258 
 259         /*
 260          * On return ioc_cnt holds the
 261          * number of bytes transferred.
 262          */
 263         ioc->ioc_cnt -= auio.uio_resid;
 264 
 265         (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
 266 
 267 out:
 268         kmem_free(ioc, sizeof (*ioc));
 269 
 270         return (err);
 271 }
 272 
 273 /*
 274  * Helper for nsmb_ioctl case
 275  * SMBIOC_NTCREATE
 276  */
 277 int
 278 smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
 279 {
 280         struct smb_cred scred;
 281         struct mbchain name_mb;
 282         struct smb_share *ssp;
 283         struct smb_fh *fhp = NULL;
 284         smbioc_ntcreate_t *ioc = NULL;
 285         int err, nmlen;
 286 
 287         mb_init(&name_mb);
 288 
 289         /* This ioctl requires a share. */
 290         if ((ssp = sdp->sd_share) == NULL)
 291                 return (ENOTCONN);
 292 
 293         /* Must not already have a file handle. */
 294         if (sdp->sd_fh != NULL)
 295                 return (EINVAL);
 296 
 297         ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
 298         if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
 299                 err = EFAULT;
 300                 goto out;
 301         }
 302 
 303         /* Build name_mb */
 304         ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0';
 305         nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1);
 306         err = smb_put_dmem(&name_mb, SSTOVC(ssp),
 307             ioc->ioc_name, nmlen,
 308             SMB_CS_NONE, NULL);
 309         if (err != 0)
 310                 goto out;
 311 
 312         err = smb_fh_create(ssp, &fhp);
 313         if (err != 0)
 314                 goto out;
 315 
 316         /*
 317          * Do the OtW open, save the FID.
 318          */
 319         smb_credinit(&scred, cr);
 320         err = smb_smb_ntcreate(ssp, &name_mb,
 321             0,  /* create flags */
 322             ioc->ioc_req_acc,
 323             ioc->ioc_efattr,
 324             ioc->ioc_share_acc,
 325             ioc->ioc_open_disp,
 326             ioc->ioc_creat_opts,
 327             NTCREATEX_IMPERSONATION_IMPERSONATION,
 328             &scred,
 329             fhp,
 330             NULL,
 331             NULL);
 332         smb_credrele(&scred);
 333         if (err != 0)
 334                 goto out;
 335 
 336         fhp->fh_rights = ioc->ioc_req_acc;
 337         smb_fh_opened(fhp);
 338         sdp->sd_fh = fhp;
 339         fhp = NULL;
 340 
 341 out:
 342         if (fhp != NULL)
 343                 smb_fh_rele(fhp);
 344         kmem_free(ioc, sizeof (*ioc));
 345         mb_done(&name_mb);
 346 
 347         return (err);
 348 }
 349 
 350 /*
 351  * Helper for nsmb_ioctl case
 352  * SMBIOC_PRINTJOB
 353  */
 354 int
 355 smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
 356 {
 357         static const char invalid_chars[] = SMB_FILENAME_INVALID_CHARS;
 358         struct smb_cred scred;
 359         struct mbchain name_mb;
 360         struct smb_share *ssp;
 361         struct smb_fh *fhp = NULL;
 362         smbioc_printjob_t *ioc = NULL;
 363         int err, cklen, nmlen;
 364         uint32_t access = SA_RIGHT_FILE_WRITE_DATA |
 365             SA_RIGHT_FILE_READ_ATTRIBUTES;
 366 
 367         mb_init(&name_mb);
 368 
 369         /* This ioctl requires a share. */
 370         if ((ssp = sdp->sd_share) == NULL)
 371                 return (ENOTCONN);
 372 
 373         /* The share must be a print queue. */
 374         if (ssp->ss_type != STYPE_PRINTQ)
 375                 return (EINVAL);
 376 
 377         /* Must not already have a file handle. */
 378         if (sdp->sd_fh != NULL)
 379                 return (EINVAL);
 380 
 381         smb_credinit(&scred, cr);
 382         ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
 383         if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
 384                 err = EFAULT;
 385                 goto out;
 386         }
 387 
 388         /*
 389          * Use the print job title as the file name to open, but
 390          * check for invalid characters first.  See the notes in
 391          * libsmbfs/smb/print.c about job name sanitizing.
 392          */
 393         ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
 394         nmlen = strnlen(ioc->ioc_title, SMBIOC_MAX_NAME-1);
 395         cklen = strcspn(ioc->ioc_title, invalid_chars);
 396         if (cklen < nmlen) {
 397                 err = EINVAL;
 398                 goto out;
 399         }
 400 
 401         /* Build name_mb */
 402         err = smb_put_dmem(&name_mb, SSTOVC(ssp),
 403             ioc->ioc_title, nmlen,
 404             SMB_CS_NONE, NULL);
 405         if (err != 0)
 406                 goto out;
 407 
 408         err = smb_fh_create(ssp, &fhp);
 409         if (err != 0)
 410                 goto out;
 411 
 412         /*
 413          * Do the OtW open, save the FID.
 414          */
 415         smb_credinit(&scred, cr);
 416         if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
 417                 err = smb2_smb_ntcreate(ssp, &name_mb,
 418                     NULL, NULL, /* cctx in, out */
 419                     0,  /* create flags */
 420                     access,
 421                     SMB_EFA_NORMAL,
 422                     NTCREATEX_SHARE_ACCESS_NONE,
 423                     NTCREATEX_DISP_CREATE,
 424                     NTCREATEX_OPTIONS_NON_DIRECTORY_FILE,
 425                     NTCREATEX_IMPERSONATION_IMPERSONATION,
 426                     &scred,
 427                     &fhp->fh_fid2,
 428                     NULL,
 429                     NULL);
 430         } else {
 431                 err = smb_smb_open_prjob(ssp, ioc->ioc_title,
 432                     ioc->ioc_setuplen, ioc->ioc_prmode,
 433                     &scred, &fhp->fh_fid1);
 434         }
 435         smb_credrele(&scred);
 436         if (err != 0)
 437                 goto out;
 438 
 439         fhp->fh_rights = access;
 440         smb_fh_opened(fhp);
 441         sdp->sd_fh = fhp;
 442         fhp = NULL;
 443 
 444 out:
 445         if (fhp != NULL)
 446                 smb_fh_rele(fhp);
 447         kmem_free(ioc, sizeof (*ioc));
 448         mb_done(&name_mb);
 449 
 450         return (err);
 451 }
 452 
 453 /*
 454  * Helper for nsmb_ioctl case
 455  * SMBIOC_CLOSEFH
 456  */
 457 /*ARGSUSED*/
 458 int
 459 smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
 460 {
 461         struct smb_fh *fhp;
 462 
 463         /* This ioctl requires a file handle. */
 464         if ((fhp = sdp->sd_fh) == NULL)
 465                 return (EINVAL);
 466         sdp->sd_fh = NULL;
 467 
 468         smb_fh_close(fhp);
 469         smb_fh_rele(fhp);
 470 
 471         return (0);
 472 }
 473 
 474 /*
 475  * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
 476  * Find or create a session (a.k.a. "VC" in here)
 477  */
 478 int
 479 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
 480 {
 481         struct smb_cred scred;
 482         smbioc_ossn_t *ossn = NULL;
 483         struct smb_vc *vcp = NULL;
 484         int error = 0;
 485         uid_t realuid;
 486 
 487         /* Should be no VC */
 488         if (sdp->sd_vc != NULL)
 489                 return (EISCONN);
 490 
 491         smb_credinit(&scred, cr);
 492         ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
 493         if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
 494                 error = EFAULT;
 495                 goto out;
 496         }
 497 
 498         /*
 499          * Only superuser can specify a UID or GID.
 500          */
 501         realuid = crgetruid(cr);
 502         if (ossn->ssn_owner == SMBM_ANY_OWNER)
 503                 ossn->ssn_owner = realuid;
 504         else {
 505                 /*
 506                  * Do we have the privilege to create with the
 507                  * specified uid?  (does uid == cr->cr_uid, etc.)
 508                  */
 509                 if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
 510                         error = EPERM;
 511                         goto out;
 512                 }
 513                 /* ossn->ssn_owner is OK */
 514         }
 515 
 516         /*
 517          * Make sure the strings are null terminated.
 518          */
 519         ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
 520         ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
 521         ossn->ssn_id.id_user[   SMBIOC_MAX_NAME-1] = '\0';
 522 
 523         if (cmd == SMBIOC_SSN_CREATE)
 524                 ossn->ssn_vopt |= SMBVOPT_CREATE;
 525         else /* FIND */
 526                 ossn->ssn_vopt &= ~SMBVOPT_CREATE;
 527 
 528         error = smb_vc_findcreate(ossn, &scred, &vcp);
 529         if (error)
 530                 goto out;
 531         ASSERT(vcp != NULL);
 532 
 533         /*
 534          * We have a VC, held, but not locked.
 535          * If we're creating, mark this instance as
 536          * an open from IOD so close can do cleanup.
 537          *
 538          * XXX: Would be nice to have a back pointer
 539          * from the VC to this (IOD) sdp instance.
 540          */
 541         if (cmd == SMBIOC_SSN_CREATE) {
 542                 if (vcp->iod_thr != NULL) {
 543                         error = EEXIST;
 544                         goto out;
 545                 }
 546                 sdp->sd_flags |= NSMBFL_IOD;
 547         } else {
 548                 /*
 549                  * Wait for it to finish connecting
 550                  * (or reconnect) if necessary.
 551                  */
 552                 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
 553                         error = smb_iod_reconnect(vcp);
 554                         if (error != 0)
 555                                 goto out;
 556                 }
 557         }
 558 
 559         /*
 560          * The VC has a hold from _findvc
 561          * which we keep until _SSN_RELE
 562          * or nsmb_close().
 563          */
 564         sdp->sd_level = SMBL_VC;
 565         sdp->sd_vc = vcp;
 566         vcp = NULL;
 567         (void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
 568 
 569 out:
 570         if (vcp) {
 571                 /* Error path: rele hold from _findcreate */
 572                 smb_vc_rele(vcp);
 573         }
 574         kmem_free(ossn, sizeof (*ossn));
 575         smb_credrele(&scred);
 576 
 577         return (error);
 578 }
 579 
 580 /*
 581  * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
 582  * Release or kill the current session.
 583  */
 584 int
 585 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
 586 {
 587         struct smb_vc *vcp = NULL;
 588 
 589         /* Must have a VC. */
 590         if ((vcp = sdp->sd_vc) == NULL)
 591                 return (ENOTCONN);
 592 
 593         /* If we have a share ref, drop it too. */
 594         if (sdp->sd_share) {
 595                 smb_share_rele(sdp->sd_share);
 596                 sdp->sd_share = NULL;
 597                 sdp->sd_level = SMBL_VC;
 598         }
 599 
 600         if (cmd == SMBIOC_SSN_KILL)
 601                 smb_vc_kill(vcp);
 602 
 603         /* Drop the VC ref. */
 604         smb_vc_rele(vcp);
 605         sdp->sd_vc = NULL;
 606         sdp->sd_level = 0;
 607 
 608         return (0);
 609 }
 610 
 611 /*
 612  * Find or create a tree (connected share)
 613  */
 614 int
 615 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
 616 {
 617         struct smb_cred scred;
 618         smbioc_tcon_t *tcon = NULL;
 619         struct smb_vc *vcp = NULL;
 620         struct smb_share *ssp = NULL;
 621         int error = 0;
 622 
 623         /* Must have a VC. */
 624         if ((vcp = sdp->sd_vc) == NULL)
 625                 return (ENOTCONN);
 626         /* Should not have a share. */
 627         if (sdp->sd_share != NULL)
 628                 return (EISCONN);
 629 
 630         smb_credinit(&scred, cr);
 631         tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
 632         if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
 633                 error = EFAULT;
 634                 goto out;
 635         }
 636 
 637         /*
 638          * Make sure the strings are null terminated.
 639          */
 640         tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
 641         tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
 642 
 643         if (cmd == SMBIOC_TREE_CONNECT)
 644                 tcon->tc_opt |= SMBSOPT_CREATE;
 645         else /* FIND */
 646                 tcon->tc_opt &= ~SMBSOPT_CREATE;
 647 
 648         error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
 649         if (error)
 650                 goto out;
 651         ASSERT(ssp != NULL);
 652 
 653         /*
 654          * We have a share, held, but not locked.
 655          * If we're creating, do tree connect now,
 656          * otherwise let that wait for a request.
 657          */
 658         if (cmd == SMBIOC_TREE_CONNECT) {
 659                 error = smb_share_tcon(ssp, &scred);
 660                 if (error)
 661                         goto out;
 662         }
 663 
 664         /*
 665          * Give caller the real share type from
 666          * the tree connect response, so they can
 667          * see if they got the requested type.
 668          */
 669         tcon->tc_sh.sh_type = ssp->ss_type;
 670 
 671         /*
 672          * The share has a hold from _tcon
 673          * which we keep until nsmb_close()
 674          * or the SMBIOC_TDIS below.
 675          */
 676         sdp->sd_level = SMBL_SHARE;
 677         sdp->sd_share = ssp;
 678         ssp = NULL;
 679         (void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
 680 
 681 out:
 682         if (ssp) {
 683                 /* Error path: rele hold from _findcreate */
 684                 smb_share_rele(ssp);
 685         }
 686         /*
 687          * This structure may contain a
 688          * cleartext password, so zap it.
 689          */
 690         bzero(tcon, sizeof (*tcon));
 691         kmem_free(tcon, sizeof (*tcon));
 692         smb_credrele(&scred);
 693 
 694         return (error);
 695 }
 696 
 697 /*
 698  * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
 699  * Release or kill the current tree
 700  */
 701 int
 702 smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
 703 {
 704         struct smb_share *ssp = NULL;
 705 
 706         /* Must have a VC and a share. */
 707         if (sdp->sd_vc == NULL)
 708                 return (ENOTCONN);
 709         if ((ssp = sdp->sd_share) == NULL)
 710                 return (ENOTCONN);
 711 
 712         if (cmd == SMBIOC_TREE_KILL)
 713                 smb_share_kill(ssp);
 714 
 715         /* Drop the share ref. */
 716         smb_share_rele(sdp->sd_share);
 717         sdp->sd_share = NULL;
 718         sdp->sd_level = SMBL_VC;
 719 
 720         return (0);
 721 }
 722 
 723 /*
 724  * Ioctl handler for all SMBIOC_IOD_...
 725  */
 726 int
 727 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
 728 {
 729         struct smb_vc *vcp;
 730         int err = 0;
 731 
 732         /* Must be the IOD. */
 733         if ((sdp->sd_flags & NSMBFL_IOD) == 0)
 734                 return (EINVAL);
 735         /* Must have a VC and no share. */
 736         if ((vcp = sdp->sd_vc) == NULL)
 737                 return (EINVAL);
 738         if (sdp->sd_share != NULL)
 739                 return (EINVAL);
 740 
 741         /*
 742          * Is there already an IOD for this VC?
 743          * (Should never happen.)
 744          */
 745         SMB_VC_LOCK(vcp);
 746         if (vcp->iod_thr == NULL)
 747                 vcp->iod_thr = curthread;
 748         else
 749                 err = EEXIST;
 750         SMB_VC_UNLOCK(vcp);
 751         if (err)
 752                 return (err);
 753 
 754         /*
 755          * Copy the "work" state, etc. into the VC,
 756          * and back to the caller on the way out.
 757          * Clear the "out only" part.
 758          */
 759         if (ddi_copyin((void *)arg, &vcp->vc_work,
 760             sizeof (smbioc_ssn_work_t), flags)) {
 761                 err = EFAULT;
 762                 goto out;
 763         }
 764         vcp->vc_work.wk_out_state = 0;
 765 
 766         switch (cmd) {
 767 
 768         case SMBIOC_IOD_CONNECT:
 769                 err = nsmb_iod_connect(vcp, cr);
 770                 break;
 771 
 772         case SMBIOC_IOD_NEGOTIATE:
 773                 err = nsmb_iod_negotiate(vcp, cr);
 774                 break;
 775 
 776         case SMBIOC_IOD_SSNSETUP:
 777                 err = nsmb_iod_ssnsetup(vcp, cr);
 778                 break;
 779 
 780         case SMBIOC_IOD_WORK:
 781                 err = smb_iod_vc_work(vcp, flags, cr);
 782                 break;
 783 
 784         case SMBIOC_IOD_IDLE:
 785                 err = smb_iod_vc_idle(vcp);
 786                 break;
 787 
 788         case SMBIOC_IOD_RCFAIL:
 789                 err = smb_iod_vc_rcfail(vcp);
 790                 break;
 791 
 792         default:
 793                 err = ENOTTY;
 794                 break;
 795         }
 796 
 797 out:
 798         vcp->vc_work.wk_out_state = vcp->vc_state;
 799         (void) ddi_copyout(&vcp->vc_work, (void *)arg,
 800             sizeof (smbioc_ssn_work_t), flags);
 801 
 802         /*
 803          * The IOD thread is leaving the driver.  Clear iod_thr,
 804          * and wake up anybody waiting for us to quit.
 805          */
 806         SMB_VC_LOCK(vcp);
 807         vcp->iod_thr = NULL;
 808         cv_broadcast(&vcp->vc_statechg);
 809         SMB_VC_UNLOCK(vcp);
 810 
 811         return (err);
 812 }
 813 
 814 int
 815 smb_usr_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
 816 {
 817         int err;
 818 
 819         /*
 820          * Serialize ioctl calls.  The smb_usr_... functions
 821          * don't expect concurrent calls on a given sdp.
 822          */
 823         mutex_enter(&sdp->sd_lock);
 824         if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) {
 825                 mutex_exit(&sdp->sd_lock);
 826                 return (EBUSY);
 827         }
 828         sdp->sd_flags |= NSMBFL_IOCTL;
 829         mutex_exit(&sdp->sd_lock);
 830 
 831         err = 0;
 832         switch (cmd) {
 833         case SMBIOC_GETVERS:
 834                 (void) ddi_copyout(&nsmb_version, (void *)arg,
 835                     sizeof (nsmb_version), flags);
 836                 break;
 837 
 838         case SMBIOC_GETSSNKEY:
 839                 err = smb_usr_get_ssnkey(sdp, arg, flags);
 840                 break;
 841 
 842         case SMBIOC_DUP_DEV:
 843                 err = smb_usr_dup_dev(sdp, arg, flags);
 844                 break;
 845 
 846         case SMBIOC_XACTNP:
 847                 err = smb_usr_xnp(sdp, arg, flags, cr);
 848                 break;
 849 
 850         case SMBIOC_READ:
 851         case SMBIOC_WRITE:
 852                 err = smb_usr_rw(sdp, cmd, arg, flags, cr);
 853                 break;
 854 
 855         case SMBIOC_NTCREATE:
 856                 err = smb_usr_ntcreate(sdp, arg, flags, cr);
 857                 break;
 858 
 859         case SMBIOC_PRINTJOB:
 860                 err = smb_usr_printjob(sdp, arg, flags, cr);
 861                 break;
 862 
 863         case SMBIOC_CLOSEFH:
 864                 err = smb_usr_closefh(sdp, cr);
 865                 break;
 866 
 867         case SMBIOC_SSN_CREATE:
 868         case SMBIOC_SSN_FIND:
 869                 err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
 870                 break;
 871 
 872         case SMBIOC_SSN_KILL:
 873         case SMBIOC_SSN_RELE:
 874                 err = smb_usr_drop_ssn(sdp, cmd);
 875                 break;
 876 
 877         case SMBIOC_TREE_CONNECT:
 878         case SMBIOC_TREE_FIND:
 879                 err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
 880                 break;
 881 
 882         case SMBIOC_TREE_KILL:
 883         case SMBIOC_TREE_RELE:
 884                 err = smb_usr_drop_tree(sdp, cmd);
 885                 break;
 886 
 887         case SMBIOC_IOD_CONNECT:
 888         case SMBIOC_IOD_NEGOTIATE:
 889         case SMBIOC_IOD_SSNSETUP:
 890         case SMBIOC_IOD_WORK:
 891         case SMBIOC_IOD_IDLE:
 892         case SMBIOC_IOD_RCFAIL:
 893                 err = smb_usr_iod_ioctl(sdp, cmd, arg, flags, cr);
 894                 break;
 895 
 896         case SMBIOC_PK_ADD:
 897         case SMBIOC_PK_DEL:
 898         case SMBIOC_PK_CHK:
 899         case SMBIOC_PK_DEL_OWNER:
 900         case SMBIOC_PK_DEL_EVERYONE:
 901                 err = smb_pkey_ioctl(cmd, arg, flags, cr);
 902                 break;
 903 
 904         default:
 905                 err = ENOTTY;
 906                 break;
 907         }
 908 
 909         mutex_enter(&sdp->sd_lock);
 910         sdp->sd_flags &= ~NSMBFL_IOCTL;
 911         mutex_exit(&sdp->sd_lock);
 912 
 913         return (err);
 914 }