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 <netsmb/smb.h>
57 #include <netsmb/smb_conn.h>
58 #include <netsmb/smb_rq.h>
59 #include <netsmb/smb_subr.h>
60 #include <netsmb/smb_dev.h>
61 
62 static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
63 
64 /*
65  * Ioctl function for SMBIOC_GETSSNKEY
66  * Size copied out is SMBIOC_HASH_SZ.
67  *
68  * The RPC library needs this for encrypting things
69  * like "set password" requests.  This is called
70  * with an active RPC binding, so the connection
71  * will already be active (but this checks).
72  */
73 int
74 smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags)
75 {
76 	struct smb_vc *vcp = NULL;
77 
78 	/* This ioctl requires an active session. */
79 	if ((vcp = sdp->sd_vc) == NULL)
80 		return (ENOTCONN);
81 	if (vcp->vc_state != SMBIOD_ST_VCACTIVE)
82 		return (ENOTCONN);
83 
84 	/*
85 	 * Return the session key.
86 	 */
87 	if (vcp->vc_ssnkey == NULL ||
88 	    vcp->vc_ssnkeylen < SMBIOC_HASH_SZ)
89 		return (EINVAL);
90 	if (ddi_copyout(vcp->vc_ssnkey, (void *)arg,
91 	    SMBIOC_HASH_SZ, flags))
92 		return (EFAULT);
93 
94 	return (0);
95 }
96 
97 /*
98  * Ioctl function for SMBIOC_XACTNP (transact named pipe)
99  */
100 int
101 smb_usr_xnp(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
102 {
103 	struct smb_cred scred;
104 	struct smb_share *ssp;
105 	smbioc_xnp_t *ioc = NULL;
106 	struct smb_t2rq *t2p = NULL;
107 	struct mdchain *mdp;
108 	int err, len, mbseg;
109 	uint16_t setup[2];
110 
111 	/* This ioctl requires a share. */
112 	if ((ssp = sdp->sd_share) == NULL)
113 		return (ENOTCONN);
114 
115 	smb_credinit(&scred, cr);
116 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
117 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
118 		err = EFAULT;
119 		goto out;
120 	}
121 
122 	/* See ddi_copyin, ddi_copyout */
123 	mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
124 
125 	/*
126 	 * Fill in the FID for libsmbfs transact named pipe.
127 	 */
128 	if (ioc->ioc_fh == -1) {
129 		if (sdp->sd_vcgenid != ssp->ss_vcgenid) {
130 			err = ESTALE;
131 			goto out;
132 		}
133 		ioc->ioc_fh = sdp->sd_smbfid;
134 	}
135 
136 	setup[0] = TRANS_TRANSACT_NAMED_PIPE;
137 	setup[1] = (uint16_t)ioc->ioc_fh;
138 
139 	t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP);
140 	err = smb_t2_init(t2p, SSTOCP(ssp), setup, 2, &scred);
141 	if (err)
142 		goto out;
143 	t2p->t2_setupcount = 2;
144 	t2p->t2_setupdata  = setup;
145 
146 	t2p->t_name = "\\PIPE\\";
147 	t2p->t_name_len = 6;
148 
149 	t2p->t2_maxscount = 0;
150 	t2p->t2_maxpcount = 0;
151 	t2p->t2_maxdcount = ioc->ioc_rdlen;
152 
153 	/* Transmit parameters (none) */
154 
155 	/* Transmit data */
156 	err = smb_cpdatain(&t2p->t2_tdata,
157 	    ioc->ioc_tdlen, ioc->ioc_tdata, mbseg);
158 	if (err)
159 		goto out;
160 
161 	err = smb_t2_request(t2p);
162 
163 	/* No returned parameters. */
164 
165 	/* Copyout returned data. */
166 	mdp = &t2p->t2_rdata;
167 	if (err == 0 && mdp->md_top != NULL) {
168 		/* User's buffer large enough? */
169 		len = m_fixhdr(mdp->md_top);
170 		if (len > ioc->ioc_rdlen) {
171 			err = EMSGSIZE;
172 			goto out;
173 		}
174 		ioc->ioc_rdlen = (ushort_t)len;
175 		err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg);
176 		if (err)
177 			goto out;
178 	} else
179 		ioc->ioc_rdlen = 0;
180 
181 	if (t2p->t2_sr_error == NT_STATUS_BUFFER_OVERFLOW)
182 		ioc->ioc_more = 1;
183 
184 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
185 
186 
187 out:
188 	if (t2p != NULL) {
189 		/* Note: t2p->t_name no longer allocated */
190 		smb_t2_done(t2p);
191 		kmem_free(t2p, sizeof (*t2p));
192 	}
193 	kmem_free(ioc, sizeof (*ioc));
194 	smb_credrele(&scred);
195 
196 	return (err);
197 }
198 
199 /* helper for _t2request */
200 static int
201 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
202 {
203 	int error;
204 
205 	if (len == 0)
206 		return (0);
207 	error = mb_init(mbp);
208 	if (error)
209 		return (error);
210 	return (mb_put_mem(mbp, data, len, mbseg));
211 }
212 
213 /*
214  * Helper for nsmb_ioctl cases
215  * SMBIOC_READ, SMBIOC_WRITE
216  */
217 int
218 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
219 {
220 	struct smb_cred scred;
221 	struct smb_share *ssp;
222 	smbioc_rw_t *ioc = NULL;
223 	struct iovec aiov[1];
224 	struct uio  auio;
225 	uint16_t fh;
226 	int err;
227 	uio_rw_t rw;
228 
229 	/* This ioctl requires a share. */
230 	if ((ssp = sdp->sd_share) == NULL)
231 		return (ENOTCONN);
232 
233 	/* After reconnect, force close+reopen */
234 	if (sdp->sd_vcgenid != ssp->ss_vcgenid)
235 		return (ESTALE);
236 
237 	smb_credinit(&scred, cr);
238 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
239 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
240 		err = EFAULT;
241 		goto out;
242 	}
243 
244 	switch (cmd) {
245 	case SMBIOC_READ:
246 		rw = UIO_READ;
247 		break;
248 	case SMBIOC_WRITE:
249 		rw = UIO_WRITE;
250 		break;
251 	default:
252 		err = ENODEV;
253 		goto out;
254 	}
255 
256 	/*
257 	 * If caller passes -1 in ioc_fh, then
258 	 * use the FID from SMBIOC_NTCREATE.
259 	 */
260 	if (ioc->ioc_fh == -1)
261 		fh = (uint16_t)sdp->sd_smbfid;
262 	else
263 		fh = (uint16_t)ioc->ioc_fh;
264 
265 	aiov[0].iov_base = ioc->ioc_base;
266 	aiov[0].iov_len = (size_t)ioc->ioc_cnt;
267 
268 	auio.uio_iov = aiov;
269 	auio.uio_iovcnt = 1;
270 	auio.uio_loffset = ioc->ioc_offset;
271 	auio.uio_segflg = (flags & FKIOCTL) ?
272 	    UIO_SYSSPACE : UIO_USERSPACE;
273 	auio.uio_fmode = 0;
274 	auio.uio_resid = (size_t)ioc->ioc_cnt;
275 
276 	err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0);
277 
278 	/*
279 	 * On return ioc_cnt holds the
280 	 * number of bytes transferred.
281 	 */
282 	ioc->ioc_cnt -= auio.uio_resid;
283 
284 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
285 
286 out:
287 	kmem_free(ioc, sizeof (*ioc));
288 	smb_credrele(&scred);
289 
290 	return (err);
291 }
292 
293 /*
294  * Helper for nsmb_ioctl case
295  * SMBIOC_NTCREATE
296  */
297 int
298 smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
299 {
300 	struct smb_cred scred;
301 	struct mbchain name_mb;
302 	struct smb_share *ssp;
303 	smbioc_ntcreate_t *ioc = NULL;
304 	uint16_t fid;
305 	int err, nmlen;
306 
307 	/* This ioctl requires a share. */
308 	if ((ssp = sdp->sd_share) == NULL)
309 		return (ENOTCONN);
310 
311 	/* Must not be already open. */
312 	if (sdp->sd_smbfid != -1)
313 		return (EINVAL);
314 
315 	mb_init(&name_mb);
316 	smb_credinit(&scred, cr);
317 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
318 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
319 		err = EFAULT;
320 		goto out;
321 	}
322 
323 	/* Build name_mb */
324 	ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0';
325 	nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1);
326 	err = smb_put_dmem(&name_mb, SSTOVC(ssp),
327 	    ioc->ioc_name, nmlen,
328 	    SMB_CS_NONE, NULL);
329 	if (err != 0)
330 		goto out;
331 
332 	/* Do the OtW open, save the FID. */
333 	err = smb_smb_ntcreate(ssp, &name_mb,
334 	    0,	/* create flags */
335 	    ioc->ioc_req_acc,
336 	    ioc->ioc_efattr,
337 	    ioc->ioc_share_acc,
338 	    ioc->ioc_open_disp,
339 	    ioc->ioc_creat_opts,
340 	    NTCREATEX_IMPERSONATION_IMPERSONATION,
341 	    &scred,
342 	    &fid,
343 	    NULL,
344 	    NULL);
345 	if (err != 0)
346 		goto out;
347 
348 	sdp->sd_smbfid = fid;
349 	sdp->sd_vcgenid = ssp->ss_vcgenid;
350 
351 out:
352 	kmem_free(ioc, sizeof (*ioc));
353 	smb_credrele(&scred);
354 	mb_done(&name_mb);
355 
356 	return (err);
357 }
358 
359 /*
360  * Helper for nsmb_ioctl case
361  * SMBIOC_PRINTJOB
362  */
363 int
364 smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
365 {
366 	struct smb_cred scred;
367 	struct smb_share *ssp;
368 	smbioc_printjob_t *ioc = NULL;
369 	uint16_t fid;
370 	int err;
371 
372 	/* This ioctl requires a share. */
373 	if ((ssp = sdp->sd_share) == NULL)
374 		return (ENOTCONN);
375 
376 	/* The share must be a print queue. */
377 	if (ssp->ss_type != STYPE_PRINTQ)
378 		return (EINVAL);
379 
380 	/* Must not be already open. */
381 	if (sdp->sd_smbfid != -1)
382 		return (EINVAL);
383 
384 	smb_credinit(&scred, cr);
385 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
386 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
387 		err = EFAULT;
388 		goto out;
389 	}
390 	ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
391 
392 	/* Do the OtW open, save the FID. */
393 	err = smb_smb_open_prjob(ssp, ioc->ioc_title,
394 	    ioc->ioc_setuplen, ioc->ioc_prmode,
395 	    &scred, &fid);
396 	if (err != 0)
397 		goto out;
398 
399 	sdp->sd_smbfid = fid;
400 	sdp->sd_vcgenid = ssp->ss_vcgenid;
401 
402 out:
403 	kmem_free(ioc, sizeof (*ioc));
404 	smb_credrele(&scred);
405 
406 	return (err);
407 }
408 
409 /*
410  * Helper for nsmb_ioctl case
411  * SMBIOC_CLOSEFH
412  */
413 int
414 smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
415 {
416 	struct smb_cred scred;
417 	struct smb_share *ssp;
418 	uint16_t fid;
419 	int err;
420 
421 	/* This ioctl requires a share. */
422 	if ((ssp = sdp->sd_share) == NULL)
423 		return (ENOTCONN);
424 
425 	if (sdp->sd_smbfid == -1)
426 		return (0);
427 	fid = (uint16_t)sdp->sd_smbfid;
428 	sdp->sd_smbfid = -1;
429 
430 	smb_credinit(&scred, cr);
431 	if (ssp->ss_type == STYPE_PRINTQ)
432 		err = smb_smb_close_prjob(ssp, fid, &scred);
433 	else
434 		err = smb_smb_close(ssp, fid, NULL, &scred);
435 	smb_credrele(&scred);
436 
437 	return (err);
438 }
439 
440 /*
441  * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
442  * Find or create a session (a.k.a. "VC" in here)
443  */
444 int
445 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
446 {
447 	struct smb_cred scred;
448 	smbioc_ossn_t *ossn = NULL;
449 	struct smb_vc *vcp = NULL;
450 	int error = 0;
451 	uid_t realuid;
452 
453 	/* Should be no VC */
454 	if (sdp->sd_vc != NULL)
455 		return (EISCONN);
456 
457 	smb_credinit(&scred, cr);
458 	ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
459 	if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
460 		error = EFAULT;
461 		goto out;
462 	}
463 
464 	/*
465 	 * Only superuser can specify a UID or GID.
466 	 */
467 	realuid = crgetruid(cr);
468 	if (ossn->ssn_owner == SMBM_ANY_OWNER)
469 		ossn->ssn_owner = realuid;
470 	else {
471 		/*
472 		 * Do we have the privilege to create with the
473 		 * specified uid?  (does uid == cr->cr_uid, etc.)
474 		 */
475 		if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
476 			error = EPERM;
477 			goto out;
478 		}
479 		/* ossn->ssn_owner is OK */
480 	}
481 
482 	/*
483 	 * Make sure the strings are null terminated.
484 	 */
485 	ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
486 	ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
487 	ossn->ssn_id.id_user[   SMBIOC_MAX_NAME-1] = '\0';
488 
489 	if (cmd == SMBIOC_SSN_CREATE)
490 		ossn->ssn_vopt |= SMBVOPT_CREATE;
491 	else /* FIND */
492 		ossn->ssn_vopt &= ~SMBVOPT_CREATE;
493 
494 	error = smb_vc_findcreate(ossn, &scred, &vcp);
495 	if (error)
496 		goto out;
497 	ASSERT(vcp != NULL);
498 
499 	/*
500 	 * We have a VC, held, but not locked.
501 	 * If we're creating, mark this instance as
502 	 * an open from IOD so close can do cleanup.
503 	 *
504 	 * XXX: Would be nice to have a back pointer
505 	 * from the VC to this (IOD) sdp instance.
506 	 */
507 	if (cmd == SMBIOC_SSN_CREATE) {
508 		if (vcp->iod_thr != NULL) {
509 			error = EEXIST;
510 			goto out;
511 		}
512 		sdp->sd_flags |= NSMBFL_IOD;
513 	} else {
514 		/*
515 		 * Wait for it to finish connecting
516 		 * (or reconnect) if necessary.
517 		 */
518 		if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
519 			error = smb_iod_reconnect(vcp);
520 			if (error != 0)
521 				goto out;
522 		}
523 	}
524 
525 	/*
526 	 * The VC has a hold from _findvc
527 	 * which we keep until _SSN_RELE
528 	 * or nsmb_close().
529 	 */
530 	sdp->sd_level = SMBL_VC;
531 	sdp->sd_vc = vcp;
532 	vcp = NULL;
533 	(void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
534 
535 out:
536 	if (vcp) {
537 		/* Error path: rele hold from _findcreate */
538 		smb_vc_rele(vcp);
539 	}
540 	kmem_free(ossn, sizeof (*ossn));
541 	smb_credrele(&scred);
542 
543 	return (error);
544 }
545 
546 /*
547  * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
548  * Release or kill the current session.
549  */
550 int
551 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
552 {
553 	struct smb_vc *vcp = NULL;
554 
555 	/* Must have a VC. */
556 	if ((vcp = sdp->sd_vc) == NULL)
557 		return (ENOTCONN);
558 
559 	/* If we have a share ref, drop it too. */
560 	if (sdp->sd_share) {
561 		smb_share_rele(sdp->sd_share);
562 		sdp->sd_share = NULL;
563 		sdp->sd_level = SMBL_VC;
564 	}
565 
566 	if (cmd == SMBIOC_SSN_KILL)
567 		smb_vc_kill(vcp);
568 
569 	/* Drop the VC ref. */
570 	smb_vc_rele(vcp);
571 	sdp->sd_vc = NULL;
572 	sdp->sd_level = 0;
573 
574 	return (0);
575 }
576 
577 /*
578  * Find or create a tree (connected share)
579  */
580 int
581 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
582 {
583 	struct smb_cred scred;
584 	smbioc_tcon_t *tcon = NULL;
585 	struct smb_vc *vcp = NULL;
586 	struct smb_share *ssp = NULL;
587 	int error = 0;
588 
589 	/* Must have a VC. */
590 	if ((vcp = sdp->sd_vc) == NULL)
591 		return (ENOTCONN);
592 	/* Should not have a share. */
593 	if (sdp->sd_share != NULL)
594 		return (EISCONN);
595 
596 	smb_credinit(&scred, cr);
597 	tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
598 	if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
599 		error = EFAULT;
600 		goto out;
601 	}
602 
603 	/*
604 	 * Make sure the strings are null terminated.
605 	 */
606 	tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
607 	tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
608 
609 	if (cmd == SMBIOC_TREE_CONNECT)
610 		tcon->tc_opt |= SMBSOPT_CREATE;
611 	else /* FIND */
612 		tcon->tc_opt &= ~SMBSOPT_CREATE;
613 
614 	error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
615 	if (error)
616 		goto out;
617 	ASSERT(ssp != NULL);
618 
619 	/*
620 	 * We have a share, held, but not locked.
621 	 * If we're creating, do tree connect now,
622 	 * otherwise let that wait for a request.
623 	 */
624 	if (cmd == SMBIOC_TREE_CONNECT) {
625 		error = smb_share_tcon(ssp, &scred);
626 		if (error)
627 			goto out;
628 	}
629 
630 	/*
631 	 * Give caller the real share type from
632 	 * the tree connect response, so they can
633 	 * see if they got the requested type.
634 	 */
635 	tcon->tc_sh.sh_type = ssp->ss_type;
636 
637 	/*
638 	 * The share has a hold from _tcon
639 	 * which we keep until nsmb_close()
640 	 * or the SMBIOC_TDIS below.
641 	 */
642 	sdp->sd_level = SMBL_SHARE;
643 	sdp->sd_share = ssp;
644 	ssp = NULL;
645 	(void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
646 
647 out:
648 	if (ssp) {
649 		/* Error path: rele hold from _findcreate */
650 		smb_share_rele(ssp);
651 	}
652 	/*
653 	 * This structure may contain a
654 	 * cleartext password, so zap it.
655 	 */
656 	bzero(tcon, sizeof (*tcon));
657 	kmem_free(tcon, sizeof (*tcon));
658 	smb_credrele(&scred);
659 
660 	return (error);
661 }
662 
663 /*
664  * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
665  * Release or kill the current tree
666  */
667 int
668 smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
669 {
670 	struct smb_share *ssp = NULL;
671 
672 	/* Must have a VC and a share. */
673 	if (sdp->sd_vc == NULL)
674 		return (ENOTCONN);
675 	if ((ssp = sdp->sd_share) == NULL)
676 		return (ENOTCONN);
677 
678 	if (cmd == SMBIOC_TREE_KILL)
679 		smb_share_kill(ssp);
680 
681 	/* Drop the share ref. */
682 	smb_share_rele(sdp->sd_share);
683 	sdp->sd_share = NULL;
684 	sdp->sd_level = SMBL_VC;
685 
686 	return (0);
687 }
688 
689 /*
690  * Ioctl handler for all SMBIOC_IOD_...
691  */
692 int
693 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
694 {
695 	struct smb_vc *vcp;
696 	int err = 0;
697 
698 	/* Must be the IOD. */
699 	if ((sdp->sd_flags & NSMBFL_IOD) == 0)
700 		return (EINVAL);
701 	/* Must have a VC and no share. */
702 	if ((vcp = sdp->sd_vc) == NULL)
703 		return (EINVAL);
704 	if (sdp->sd_share != NULL)
705 		return (EINVAL);
706 
707 	/*
708 	 * Is there already an IOD for this VC?
709 	 * (Should never happen.)
710 	 */
711 	SMB_VC_LOCK(vcp);
712 	if (vcp->iod_thr == NULL)
713 		vcp->iod_thr = curthread;
714 	else
715 		err = EEXIST;
716 	SMB_VC_UNLOCK(vcp);
717 	if (err)
718 		return (err);
719 
720 	/*
721 	 * Copy the "work" state, etc. into the VC,
722 	 * and back to the caller on the way out.
723 	 * Clear the "out only" part.
724 	 */
725 	if (ddi_copyin((void *)arg, &vcp->vc_work,
726 	    sizeof (smbioc_ssn_work_t), flags)) {
727 		err = EFAULT;
728 		goto out;
729 	}
730 	vcp->vc_work.wk_out_state = 0;
731 
732 	switch (cmd) {
733 
734 	case SMBIOC_IOD_CONNECT:
735 		err = nsmb_iod_connect(vcp);
736 		break;
737 
738 	case SMBIOC_IOD_NEGOTIATE:
739 		err = nsmb_iod_negotiate(vcp, cr);
740 		break;
741 
742 	case SMBIOC_IOD_SSNSETUP:
743 		err = nsmb_iod_ssnsetup(vcp, cr);
744 		break;
745 
746 	case SMBIOC_IOD_WORK:
747 		err = smb_iod_vc_work(vcp, flags, cr);
748 		break;
749 
750 	case SMBIOC_IOD_IDLE:
751 		err = smb_iod_vc_idle(vcp);
752 		break;
753 
754 	case SMBIOC_IOD_RCFAIL:
755 		err = smb_iod_vc_rcfail(vcp);
756 		break;
757 
758 	default:
759 		err = ENOTTY;
760 		break;
761 	}
762 
763 out:
764 	vcp->vc_work.wk_out_state = vcp->vc_state;
765 	(void) ddi_copyout(&vcp->vc_work, (void *)arg,
766 	    sizeof (smbioc_ssn_work_t), flags);
767 
768 	/*
769 	 * The IOD thread is leaving the driver.  Clear iod_thr,
770 	 * and wake up anybody waiting for us to quit.
771 	 */
772 	SMB_VC_LOCK(vcp);
773 	vcp->iod_thr = NULL;
774 	cv_broadcast(&vcp->vc_statechg);
775 	SMB_VC_UNLOCK(vcp);
776 
777 	return (err);
778 }
779 
780 int
781 smb_usr_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
782 {
783 	int err;
784 
785 	/*
786 	 * Serialize ioctl calls.  The smb_usr_... functions
787 	 * don't expect concurrent calls on a given sdp.
788 	 */
789 	mutex_enter(&sdp->sd_lock);
790 	if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) {
791 		mutex_exit(&sdp->sd_lock);
792 		return (EBUSY);
793 	}
794 	sdp->sd_flags |= NSMBFL_IOCTL;
795 	mutex_exit(&sdp->sd_lock);
796 
797 	err = 0;
798 	switch (cmd) {
799 	case SMBIOC_GETVERS:
800 		(void) ddi_copyout(&nsmb_version, (void *)arg,
801 		    sizeof (nsmb_version), flags);
802 		break;
803 
804 	case SMBIOC_GETSSNKEY:
805 		err = smb_usr_get_ssnkey(sdp, arg, flags);
806 		break;
807 
808 	case SMBIOC_DUP_DEV:
809 		err = smb_usr_dup_dev(sdp, arg, flags);
810 		break;
811 
812 	case SMBIOC_XACTNP:
813 		err = smb_usr_xnp(sdp, arg, flags, cr);
814 		break;
815 
816 	case SMBIOC_READ:
817 	case SMBIOC_WRITE:
818 		err = smb_usr_rw(sdp, cmd, arg, flags, cr);
819 		break;
820 
821 	case SMBIOC_NTCREATE:
822 		err = smb_usr_ntcreate(sdp, arg, flags, cr);
823 		break;
824 
825 	case SMBIOC_PRINTJOB:
826 		err = smb_usr_printjob(sdp, arg, flags, cr);
827 		break;
828 
829 	case SMBIOC_CLOSEFH:
830 		err = smb_usr_closefh(sdp, cr);
831 		break;
832 
833 	case SMBIOC_SSN_CREATE:
834 	case SMBIOC_SSN_FIND:
835 		err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
836 		break;
837 
838 	case SMBIOC_SSN_KILL:
839 	case SMBIOC_SSN_RELE:
840 		err = smb_usr_drop_ssn(sdp, cmd);
841 		break;
842 
843 	case SMBIOC_TREE_CONNECT:
844 	case SMBIOC_TREE_FIND:
845 		err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
846 		break;
847 
848 	case SMBIOC_TREE_KILL:
849 	case SMBIOC_TREE_RELE:
850 		err = smb_usr_drop_tree(sdp, cmd);
851 		break;
852 
853 	case SMBIOC_IOD_CONNECT:
854 	case SMBIOC_IOD_NEGOTIATE:
855 	case SMBIOC_IOD_SSNSETUP:
856 	case SMBIOC_IOD_WORK:
857 	case SMBIOC_IOD_IDLE:
858 	case SMBIOC_IOD_RCFAIL:
859 		err = smb_usr_iod_ioctl(sdp, cmd, arg, flags, cr);
860 		break;
861 
862 	case SMBIOC_PK_ADD:
863 	case SMBIOC_PK_DEL:
864 	case SMBIOC_PK_CHK:
865 	case SMBIOC_PK_DEL_OWNER:
866 	case SMBIOC_PK_DEL_EVERYONE:
867 		err = smb_pkey_ioctl(cmd, arg, flags, cr);
868 		break;
869 
870 	default:
871 		err = ENOTTY;
872 		break;
873 	}
874 
875 	mutex_enter(&sdp->sd_lock);
876 	sdp->sd_flags &= ~NSMBFL_IOCTL;
877 	mutex_exit(&sdp->sd_lock);
878 
879 	return (err);
880 }
881