xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c (revision d8109ce4330e1b8ad6c29f9fccacec969066bb9d)
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 2019 Nexenta by DDN, 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 	md_done(&recv_md);
184 	mb_done(&send_mb);
185 
186 	return (err);
187 }
188 
189 /* helper for _t2request */
190 static int
191 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
192 {
193 	int error;
194 
195 	if (len == 0)
196 		return (0);
197 	error = mb_init(mbp);
198 	if (error)
199 		return (error);
200 	return (mb_put_mem(mbp, data, len, mbseg));
201 }
202 
203 /*
204  * Helper for nsmb_ioctl cases
205  * SMBIOC_READ, SMBIOC_WRITE
206  */
207 int
208 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
209 {
210 	struct smb_cred scred;
211 	struct smb_share *ssp;
212 	struct smb_fh *fhp;
213 	smbioc_rw_t *ioc = NULL;
214 	struct iovec aiov[1];
215 	struct uio  auio;
216 	int err;
217 	uio_rw_t rw;
218 
219 	/* This ioctl requires a file handle. */
220 	if ((fhp = sdp->sd_fh) == NULL)
221 		return (EINVAL);
222 	ssp = FHTOSS(fhp);
223 
224 	/* After reconnect, force close+reopen */
225 	if (fhp->fh_vcgenid != ssp->ss_vcgenid)
226 		return (ESTALE);
227 
228 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
229 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
230 		err = EFAULT;
231 		goto out;
232 	}
233 
234 	switch (cmd) {
235 	case SMBIOC_READ:
236 		rw = UIO_READ;
237 		break;
238 	case SMBIOC_WRITE:
239 		rw = UIO_WRITE;
240 		break;
241 	default:
242 		err = ENODEV;
243 		goto out;
244 	}
245 
246 	aiov[0].iov_base = ioc->ioc_base;
247 	aiov[0].iov_len = (size_t)ioc->ioc_cnt;
248 
249 	auio.uio_iov = aiov;
250 	auio.uio_iovcnt = 1;
251 	auio.uio_loffset = ioc->ioc_offset;
252 	auio.uio_segflg = (flags & FKIOCTL) ?
253 	    UIO_SYSSPACE : UIO_USERSPACE;
254 	auio.uio_fmode = 0;
255 	auio.uio_resid = (size_t)ioc->ioc_cnt;
256 
257 	smb_credinit(&scred, cr);
258 	err = smb_rwuio(fhp, rw, &auio, &scred, 0);
259 	smb_credrele(&scred);
260 
261 	/*
262 	 * On return ioc_cnt holds the
263 	 * number of bytes transferred.
264 	 */
265 	ioc->ioc_cnt -= auio.uio_resid;
266 
267 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
268 
269 out:
270 	kmem_free(ioc, sizeof (*ioc));
271 
272 	return (err);
273 }
274 
275 /*
276  * Helper for nsmb_ioctl case
277  * SMBIOC_NTCREATE
278  */
279 int
280 smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
281 {
282 	struct smb_cred scred;
283 	struct mbchain name_mb;
284 	struct smb_share *ssp;
285 	struct smb_fh *fhp = NULL;
286 	smbioc_ntcreate_t *ioc = NULL;
287 	int err, nmlen;
288 
289 	mb_init(&name_mb);
290 
291 	/* This ioctl requires a share. */
292 	if ((ssp = sdp->sd_share) == NULL)
293 		return (ENOTCONN);
294 
295 	/* Must not already have a file handle. */
296 	if (sdp->sd_fh != NULL)
297 		return (EINVAL);
298 
299 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
300 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
301 		err = EFAULT;
302 		goto out;
303 	}
304 
305 	/* Build name_mb */
306 	ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0';
307 	nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1);
308 	err = smb_put_dmem(&name_mb, SSTOVC(ssp),
309 	    ioc->ioc_name, nmlen,
310 	    SMB_CS_NONE, NULL);
311 	if (err != 0)
312 		goto out;
313 
314 	err = smb_fh_create(ssp, &fhp);
315 	if (err != 0)
316 		goto out;
317 
318 	/*
319 	 * Do the OtW open, save the FID.
320 	 */
321 	smb_credinit(&scred, cr);
322 	err = smb_smb_ntcreate(ssp, &name_mb,
323 	    0,	/* create flags */
324 	    ioc->ioc_req_acc,
325 	    ioc->ioc_efattr,
326 	    ioc->ioc_share_acc,
327 	    ioc->ioc_open_disp,
328 	    ioc->ioc_creat_opts,
329 	    NTCREATEX_IMPERSONATION_IMPERSONATION,
330 	    &scred,
331 	    fhp,
332 	    NULL,
333 	    NULL);
334 	smb_credrele(&scred);
335 	if (err != 0)
336 		goto out;
337 
338 	fhp->fh_rights = ioc->ioc_req_acc;
339 	smb_fh_opened(fhp);
340 	sdp->sd_fh = fhp;
341 	fhp = NULL;
342 
343 out:
344 	if (fhp != NULL)
345 		smb_fh_rele(fhp);
346 	kmem_free(ioc, sizeof (*ioc));
347 	mb_done(&name_mb);
348 
349 	return (err);
350 }
351 
352 /*
353  * Helper for nsmb_ioctl case
354  * SMBIOC_PRINTJOB
355  */
356 int
357 smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
358 {
359 	static const char invalid_chars[] = SMB_FILENAME_INVALID_CHARS;
360 	struct smb_cred scred;
361 	struct mbchain name_mb;
362 	struct smb_share *ssp;
363 	struct smb_fh *fhp = NULL;
364 	smbioc_printjob_t *ioc = NULL;
365 	int err, cklen, nmlen;
366 	uint32_t access = SA_RIGHT_FILE_WRITE_DATA |
367 	    SA_RIGHT_FILE_READ_ATTRIBUTES;
368 
369 	mb_init(&name_mb);
370 
371 	/* This ioctl requires a share. */
372 	if ((ssp = sdp->sd_share) == NULL)
373 		return (ENOTCONN);
374 
375 	/* The share must be a print queue. */
376 	if (ssp->ss_type != STYPE_PRINTQ)
377 		return (EINVAL);
378 
379 	/* Must not already have a file handle. */
380 	if (sdp->sd_fh != NULL)
381 		return (EINVAL);
382 
383 	smb_credinit(&scred, cr);
384 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
385 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
386 		err = EFAULT;
387 		goto out;
388 	}
389 
390 	/*
391 	 * Use the print job title as the file name to open, but
392 	 * check for invalid characters first.  See the notes in
393 	 * libsmbfs/smb/print.c about job name sanitizing.
394 	 */
395 	ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
396 	nmlen = strnlen(ioc->ioc_title, SMBIOC_MAX_NAME-1);
397 	cklen = strcspn(ioc->ioc_title, invalid_chars);
398 	if (cklen < nmlen) {
399 		err = EINVAL;
400 		goto out;
401 	}
402 
403 	/* Build name_mb */
404 	err = smb_put_dmem(&name_mb, SSTOVC(ssp),
405 	    ioc->ioc_title, nmlen,
406 	    SMB_CS_NONE, NULL);
407 	if (err != 0)
408 		goto out;
409 
410 	err = smb_fh_create(ssp, &fhp);
411 	if (err != 0)
412 		goto out;
413 
414 	/*
415 	 * Do the OtW open, save the FID.
416 	 */
417 	smb_credinit(&scred, cr);
418 	if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
419 		err = smb2_smb_ntcreate(ssp, &name_mb,
420 		    NULL, NULL, /* cctx in, out */
421 		    0,	/* create flags */
422 		    access,
423 		    SMB_EFA_NORMAL,
424 		    NTCREATEX_SHARE_ACCESS_NONE,
425 		    NTCREATEX_DISP_CREATE,
426 		    NTCREATEX_OPTIONS_NON_DIRECTORY_FILE,
427 		    NTCREATEX_IMPERSONATION_IMPERSONATION,
428 		    &scred,
429 		    &fhp->fh_fid2,
430 		    NULL,
431 		    NULL);
432 	} else {
433 		err = smb_smb_open_prjob(ssp, ioc->ioc_title,
434 		    ioc->ioc_setuplen, ioc->ioc_prmode,
435 		    &scred, &fhp->fh_fid1);
436 	}
437 	smb_credrele(&scred);
438 	if (err != 0)
439 		goto out;
440 
441 	fhp->fh_rights = access;
442 	smb_fh_opened(fhp);
443 	sdp->sd_fh = fhp;
444 	fhp = NULL;
445 
446 out:
447 	if (fhp != NULL)
448 		smb_fh_rele(fhp);
449 	kmem_free(ioc, sizeof (*ioc));
450 	mb_done(&name_mb);
451 
452 	return (err);
453 }
454 
455 /*
456  * Helper for nsmb_ioctl case
457  * SMBIOC_CLOSEFH
458  */
459 /*ARGSUSED*/
460 int
461 smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
462 {
463 	struct smb_fh *fhp;
464 
465 	/* This ioctl requires a file handle. */
466 	if ((fhp = sdp->sd_fh) == NULL)
467 		return (EINVAL);
468 	sdp->sd_fh = NULL;
469 
470 	smb_fh_close(fhp);
471 	smb_fh_rele(fhp);
472 
473 	return (0);
474 }
475 
476 /*
477  * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
478  * Find or create a session (a.k.a. "VC" in here)
479  */
480 int
481 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
482 {
483 	struct smb_cred scred;
484 	smbioc_ossn_t *ossn = NULL;
485 	struct smb_vc *vcp = NULL;
486 	int error = 0;
487 	uid_t realuid;
488 
489 	/* Should be no VC */
490 	if (sdp->sd_vc != NULL)
491 		return (EISCONN);
492 
493 	smb_credinit(&scred, cr);
494 	ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
495 	if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
496 		error = EFAULT;
497 		goto out;
498 	}
499 
500 	/*
501 	 * Only superuser can specify a UID or GID.
502 	 */
503 	realuid = crgetruid(cr);
504 	if (ossn->ssn_owner == SMBM_ANY_OWNER)
505 		ossn->ssn_owner = realuid;
506 	else {
507 		/*
508 		 * Do we have the privilege to create with the
509 		 * specified uid?  (does uid == cr->cr_uid, etc.)
510 		 */
511 		if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
512 			error = EPERM;
513 			goto out;
514 		}
515 		/* ossn->ssn_owner is OK */
516 	}
517 
518 	/*
519 	 * Make sure the strings are null terminated.
520 	 */
521 	ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
522 	ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
523 	ossn->ssn_id.id_user[   SMBIOC_MAX_NAME-1] = '\0';
524 
525 	if (cmd == SMBIOC_SSN_CREATE)
526 		ossn->ssn_vopt |= SMBVOPT_CREATE;
527 	else /* FIND */
528 		ossn->ssn_vopt &= ~SMBVOPT_CREATE;
529 
530 	error = smb_vc_findcreate(ossn, &scred, &vcp);
531 	if (error)
532 		goto out;
533 	ASSERT(vcp != NULL);
534 
535 	/*
536 	 * We have a VC, held, but not locked.
537 	 * If we're creating, mark this instance as
538 	 * an open from IOD so close can do cleanup.
539 	 *
540 	 * XXX: Would be nice to have a back pointer
541 	 * from the VC to this (IOD) sdp instance.
542 	 */
543 	if (cmd == SMBIOC_SSN_CREATE) {
544 		if (vcp->iod_thr != NULL) {
545 			error = EEXIST;
546 			goto out;
547 		}
548 		sdp->sd_flags |= NSMBFL_IOD;
549 	} else {
550 		/*
551 		 * Wait for it to finish connecting
552 		 * (or reconnect) if necessary.
553 		 */
554 		if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
555 			error = smb_iod_reconnect(vcp);
556 			if (error != 0)
557 				goto out;
558 		}
559 	}
560 
561 	/*
562 	 * The VC has a hold from _findvc
563 	 * which we keep until _SSN_RELE
564 	 * or nsmb_close().
565 	 */
566 	sdp->sd_level = SMBL_VC;
567 	sdp->sd_vc = vcp;
568 	vcp = NULL;
569 	(void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
570 
571 out:
572 	if (vcp) {
573 		/* Error path: rele hold from _findcreate */
574 		smb_vc_rele(vcp);
575 	}
576 	kmem_free(ossn, sizeof (*ossn));
577 	smb_credrele(&scred);
578 
579 	return (error);
580 }
581 
582 /*
583  * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
584  * Release or kill the current session.
585  */
586 int
587 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
588 {
589 	struct smb_vc *vcp = NULL;
590 
591 	/* Must have a VC. */
592 	if ((vcp = sdp->sd_vc) == NULL)
593 		return (ENOTCONN);
594 
595 	/* If we have a share ref, drop it too. */
596 	if (sdp->sd_share) {
597 		smb_share_rele(sdp->sd_share);
598 		sdp->sd_share = NULL;
599 		sdp->sd_level = SMBL_VC;
600 	}
601 
602 	if (cmd == SMBIOC_SSN_KILL)
603 		smb_vc_kill(vcp);
604 
605 	/* Drop the VC ref. */
606 	smb_vc_rele(vcp);
607 	sdp->sd_vc = NULL;
608 	sdp->sd_level = 0;
609 
610 	return (0);
611 }
612 
613 /*
614  * Find or create a tree (connected share)
615  */
616 int
617 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
618 {
619 	struct smb_cred scred;
620 	smbioc_tcon_t *tcon = NULL;
621 	struct smb_vc *vcp = NULL;
622 	struct smb_share *ssp = NULL;
623 	int error = 0;
624 
625 	/* Must have a VC. */
626 	if ((vcp = sdp->sd_vc) == NULL)
627 		return (ENOTCONN);
628 	/* Should not have a share. */
629 	if (sdp->sd_share != NULL)
630 		return (EISCONN);
631 
632 	smb_credinit(&scred, cr);
633 	tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
634 	if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
635 		error = EFAULT;
636 		goto out;
637 	}
638 
639 	/*
640 	 * Make sure the strings are null terminated.
641 	 */
642 	tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
643 	tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
644 
645 	if (cmd == SMBIOC_TREE_CONNECT)
646 		tcon->tc_opt |= SMBSOPT_CREATE;
647 	else /* FIND */
648 		tcon->tc_opt &= ~SMBSOPT_CREATE;
649 
650 	error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
651 	if (error)
652 		goto out;
653 	ASSERT(ssp != NULL);
654 
655 	/*
656 	 * We have a share, held, but not locked.
657 	 * If we're creating, do tree connect now,
658 	 * otherwise let that wait for a request.
659 	 */
660 	if (cmd == SMBIOC_TREE_CONNECT) {
661 		error = smb_share_tcon(ssp, &scred);
662 		if (error)
663 			goto out;
664 	}
665 
666 	/*
667 	 * Give caller the real share type from
668 	 * the tree connect response, so they can
669 	 * see if they got the requested type.
670 	 */
671 	tcon->tc_sh.sh_type = ssp->ss_type;
672 
673 	/*
674 	 * The share has a hold from _tcon
675 	 * which we keep until nsmb_close()
676 	 * or the SMBIOC_TDIS below.
677 	 */
678 	sdp->sd_level = SMBL_SHARE;
679 	sdp->sd_share = ssp;
680 	ssp = NULL;
681 	(void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
682 
683 out:
684 	if (ssp) {
685 		/* Error path: rele hold from _findcreate */
686 		smb_share_rele(ssp);
687 	}
688 	/*
689 	 * This structure may contain a
690 	 * cleartext password, so zap it.
691 	 */
692 	bzero(tcon, sizeof (*tcon));
693 	kmem_free(tcon, sizeof (*tcon));
694 	smb_credrele(&scred);
695 
696 	return (error);
697 }
698 
699 /*
700  * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
701  * Release or kill the current tree
702  */
703 int
704 smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
705 {
706 	struct smb_share *ssp = NULL;
707 
708 	/* Must have a VC and a share. */
709 	if (sdp->sd_vc == NULL)
710 		return (ENOTCONN);
711 	if ((ssp = sdp->sd_share) == NULL)
712 		return (ENOTCONN);
713 
714 	if (cmd == SMBIOC_TREE_KILL)
715 		smb_share_kill(ssp);
716 
717 	/* Drop the share ref. */
718 	smb_share_rele(sdp->sd_share);
719 	sdp->sd_share = NULL;
720 	sdp->sd_level = SMBL_VC;
721 
722 	return (0);
723 }
724 
725 /*
726  * Ioctl handler for all SMBIOC_IOD_...
727  */
728 int
729 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
730 {
731 	struct smb_vc *vcp;
732 	int err = 0;
733 
734 	/* Must be the IOD. */
735 	if ((sdp->sd_flags & NSMBFL_IOD) == 0)
736 		return (EINVAL);
737 	/* Must have a VC and no share. */
738 	if ((vcp = sdp->sd_vc) == NULL)
739 		return (EINVAL);
740 	if (sdp->sd_share != NULL)
741 		return (EINVAL);
742 
743 	/*
744 	 * Is there already an IOD for this VC?
745 	 * (Should never happen.)
746 	 */
747 	SMB_VC_LOCK(vcp);
748 	if (vcp->iod_thr == NULL)
749 		vcp->iod_thr = curthread;
750 	else
751 		err = EEXIST;
752 	SMB_VC_UNLOCK(vcp);
753 	if (err)
754 		return (err);
755 
756 	/*
757 	 * Copy the "work" state, etc. into the VC,
758 	 * and back to the caller on the way out.
759 	 * Clear the "out only" part.
760 	 */
761 	if (ddi_copyin((void *)arg, &vcp->vc_work,
762 	    sizeof (smbioc_ssn_work_t), flags)) {
763 		err = EFAULT;
764 		goto out;
765 	}
766 	vcp->vc_work.wk_out_state = 0;
767 
768 	switch (cmd) {
769 
770 	case SMBIOC_IOD_CONNECT:
771 		err = nsmb_iod_connect(vcp, cr);
772 		break;
773 
774 	case SMBIOC_IOD_NEGOTIATE:
775 		err = nsmb_iod_negotiate(vcp, cr);
776 		break;
777 
778 	case SMBIOC_IOD_SSNSETUP:
779 		err = nsmb_iod_ssnsetup(vcp, cr);
780 		break;
781 
782 	case SMBIOC_IOD_WORK:
783 		err = smb_iod_vc_work(vcp, flags, cr);
784 		break;
785 
786 	case SMBIOC_IOD_IDLE:
787 		err = smb_iod_vc_idle(vcp);
788 		break;
789 
790 	case SMBIOC_IOD_RCFAIL:
791 		err = smb_iod_vc_rcfail(vcp);
792 		break;
793 
794 	default:
795 		err = ENOTTY;
796 		break;
797 	}
798 
799 out:
800 	vcp->vc_work.wk_out_state = vcp->vc_state;
801 	(void) ddi_copyout(&vcp->vc_work, (void *)arg,
802 	    sizeof (smbioc_ssn_work_t), flags);
803 
804 	/*
805 	 * The IOD thread is leaving the driver.  Clear iod_thr,
806 	 * and wake up anybody waiting for us to quit.
807 	 */
808 	SMB_VC_LOCK(vcp);
809 	vcp->iod_thr = NULL;
810 	cv_broadcast(&vcp->vc_statechg);
811 	SMB_VC_UNLOCK(vcp);
812 
813 	return (err);
814 }
815 
816 int
817 smb_usr_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
818 {
819 	int err;
820 
821 	/*
822 	 * Serialize ioctl calls.  The smb_usr_... functions
823 	 * don't expect concurrent calls on a given sdp.
824 	 */
825 	mutex_enter(&sdp->sd_lock);
826 	if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) {
827 		mutex_exit(&sdp->sd_lock);
828 		return (EBUSY);
829 	}
830 	sdp->sd_flags |= NSMBFL_IOCTL;
831 	mutex_exit(&sdp->sd_lock);
832 
833 	err = 0;
834 	switch (cmd) {
835 	case SMBIOC_GETVERS:
836 		(void) ddi_copyout(&nsmb_version, (void *)arg,
837 		    sizeof (nsmb_version), flags);
838 		break;
839 
840 	case SMBIOC_GETSSNKEY:
841 		err = smb_usr_get_ssnkey(sdp, arg, flags);
842 		break;
843 
844 	case SMBIOC_DUP_DEV:
845 		err = smb_usr_dup_dev(sdp, arg, flags);
846 		break;
847 
848 	case SMBIOC_XACTNP:
849 		err = smb_usr_xnp(sdp, arg, flags, cr);
850 		break;
851 
852 	case SMBIOC_READ:
853 	case SMBIOC_WRITE:
854 		err = smb_usr_rw(sdp, cmd, arg, flags, cr);
855 		break;
856 
857 	case SMBIOC_NTCREATE:
858 		err = smb_usr_ntcreate(sdp, arg, flags, cr);
859 		break;
860 
861 	case SMBIOC_PRINTJOB:
862 		err = smb_usr_printjob(sdp, arg, flags, cr);
863 		break;
864 
865 	case SMBIOC_CLOSEFH:
866 		err = smb_usr_closefh(sdp, cr);
867 		break;
868 
869 	case SMBIOC_SSN_CREATE:
870 	case SMBIOC_SSN_FIND:
871 		err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
872 		break;
873 
874 	case SMBIOC_SSN_KILL:
875 	case SMBIOC_SSN_RELE:
876 		err = smb_usr_drop_ssn(sdp, cmd);
877 		break;
878 
879 	case SMBIOC_TREE_CONNECT:
880 	case SMBIOC_TREE_FIND:
881 		err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
882 		break;
883 
884 	case SMBIOC_TREE_KILL:
885 	case SMBIOC_TREE_RELE:
886 		err = smb_usr_drop_tree(sdp, cmd);
887 		break;
888 
889 	case SMBIOC_IOD_CONNECT:
890 	case SMBIOC_IOD_NEGOTIATE:
891 	case SMBIOC_IOD_SSNSETUP:
892 	case SMBIOC_IOD_WORK:
893 	case SMBIOC_IOD_IDLE:
894 	case SMBIOC_IOD_RCFAIL:
895 		err = smb_usr_iod_ioctl(sdp, cmd, arg, flags, cr);
896 		break;
897 
898 	case SMBIOC_PK_ADD:
899 	case SMBIOC_PK_DEL:
900 	case SMBIOC_PK_CHK:
901 	case SMBIOC_PK_DEL_OWNER:
902 	case SMBIOC_PK_DEL_EVERYONE:
903 		err = smb_pkey_ioctl(cmd, arg, flags, cr);
904 		break;
905 
906 	default:
907 		err = ENOTTY;
908 		break;
909 	}
910 
911 	mutex_enter(&sdp->sd_lock);
912 	sdp->sd_flags &= ~NSMBFL_IOCTL;
913 	mutex_exit(&sdp->sd_lock);
914 
915 	return (err);
916 }
917