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 }
915