xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 2011 Nexenta Systems, Inc.  All rights reserved.
37  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
38  * Use is subject to license terms.
39  */
40 
41 #include <sys/param.h>
42 #include <sys/kmem.h>
43 #include <sys/systm.h>
44 #include <sys/policy.h>
45 #include <sys/conf.h>
46 #include <sys/proc.h>
47 #include <sys/fcntl.h>
48 #include <sys/file.h>
49 #include <sys/socket.h>
50 #include <sys/sunddi.h>
51 #include <sys/cmn_err.h>
52 
53 #include <netsmb/smb_osdep.h>
54 
55 #include <netsmb/smb.h>
56 #include <netsmb/smb_conn.h>
57 #include <netsmb/smb_rq.h>
58 #include <netsmb/smb_subr.h>
59 #include <netsmb/smb_dev.h>
60 
61 static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
62 
63 /*
64  * Ioctl function for SMBIOC_FLAGS2
65  */
66 int
67 smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags)
68 {
69 	struct smb_vc *vcp = NULL;
70 
71 	/* This ioctl requires a session. */
72 	if ((vcp = sdp->sd_vc) == NULL)
73 		return (ENOTCONN);
74 
75 	/*
76 	 * Return the flags2 value.
77 	 */
78 	if (ddi_copyout(&vcp->vc_hflags2, (void *)arg,
79 	    sizeof (u_int16_t), flags))
80 		return (EFAULT);
81 
82 	return (0);
83 }
84 
85 /*
86  * Ioctl function for SMBIOC_GETSSNKEY
87  * Size copied out is SMBIOC_HASH_SZ.
88  *
89  * The RPC library needs this for encrypting things
90  * like "set password" requests.  This is called
91  * with an active RPC binding, so the connection
92  * will already be active (but this checks).
93  */
94 int
95 smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags)
96 {
97 	struct smb_vc *vcp = NULL;
98 
99 	/* This ioctl requires an active session. */
100 	if ((vcp = sdp->sd_vc) == NULL)
101 		return (ENOTCONN);
102 	if (vcp->vc_state != SMBIOD_ST_VCACTIVE)
103 		return (ENOTCONN);
104 
105 	/*
106 	 * Return the session key.
107 	 */
108 	if (ddi_copyout(vcp->vc_ssn_key, (void *)arg,
109 	    SMBIOC_HASH_SZ, flags))
110 		return (EFAULT);
111 
112 	return (0);
113 }
114 
115 /*
116  * Ioctl function for SMBIOC_REQUEST
117  */
118 int
119 smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
120 {
121 	struct smb_cred scred;
122 	struct smb_share *ssp;
123 	smbioc_rq_t *ioc = NULL;
124 	struct smb_rq *rqp = NULL;
125 	struct mbchain *mbp;
126 	struct mdchain *mdp;
127 	uint32_t rsz;
128 	int err, mbseg;
129 
130 	/* This ioctl requires a share. */
131 	if ((ssp = sdp->sd_share) == NULL)
132 		return (ENOTCONN);
133 
134 	smb_credinit(&scred, cr);
135 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
136 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
137 		err = EFAULT;
138 		goto out;
139 	}
140 
141 	/* See ddi_copyin, ddi_copyout */
142 	mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
143 
144 	/*
145 	 * Lots of SMB commands could be safe, but
146 	 * these are the only ones used by libsmbfs.
147 	 */
148 	switch (ioc->ioc_cmd) {
149 		/* These are OK */
150 	case SMB_COM_CLOSE:
151 	case SMB_COM_FLUSH:
152 	case SMB_COM_NT_CREATE_ANDX:
153 	case SMB_COM_OPEN_PRINT_FILE:
154 	case SMB_COM_CLOSE_PRINT_FILE:
155 		break;
156 
157 	default:
158 		err = EPERM;
159 		goto out;
160 	}
161 
162 	err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp);
163 	if (err)
164 		goto out;
165 
166 	mbp = &rqp->sr_rq;
167 	err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg);
168 
169 	err = smb_rq_simple(rqp);
170 	if (err == 0) {
171 		/*
172 		 * This may have been an open, so save the
173 		 * generation ID of the share, which we
174 		 * check before trying read or write.
175 		 */
176 		sdp->sd_vcgenid = ssp->ss_vcgenid;
177 
178 		/*
179 		 * Have reply data. to copyout.
180 		 * SMB header already parsed.
181 		 */
182 		mdp = &rqp->sr_rp;
183 		rsz = msgdsize(mdp->md_top) - SMB_HDRLEN;
184 		if (ioc->ioc_rbufsz < rsz) {
185 			err = EOVERFLOW;
186 			goto out;
187 		}
188 		ioc->ioc_rbufsz = rsz;
189 		err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg);
190 		if (err)
191 			goto out;
192 
193 	}
194 
195 	ioc->ioc_errclass = rqp->sr_errclass;
196 	ioc->ioc_serror = rqp->sr_serror;
197 	ioc->ioc_error = rqp->sr_error;
198 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
199 
200 out:
201 	if (rqp != NULL)
202 		smb_rq_done(rqp); /* free rqp */
203 	kmem_free(ioc, sizeof (*ioc));
204 	smb_credrele(&scred);
205 
206 	return (err);
207 
208 }
209 
210 /*
211  * Ioctl function for SMBIOC_T2RQ
212  */
213 int
214 smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
215 {
216 	struct smb_cred scred;
217 	struct smb_share *ssp;
218 	smbioc_t2rq_t *ioc = NULL;
219 	struct smb_t2rq *t2p = NULL;
220 	struct mdchain *mdp;
221 	int err, len, mbseg;
222 
223 	/* This ioctl requires a share. */
224 	if ((ssp = sdp->sd_share) == NULL)
225 		return (ENOTCONN);
226 
227 	smb_credinit(&scred, cr);
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 	/* See ddi_copyin, ddi_copyout */
235 	mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
236 
237 	if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) {
238 		err = EINVAL;
239 		goto out;
240 	}
241 
242 	/*
243 	 * Fill in the FID for libsmbfs transact named pipe.
244 	 */
245 	if (ioc->ioc_setupcnt > 1 && ioc->ioc_setup[1] == 0xFFFF) {
246 		if (sdp->sd_vcgenid != ssp->ss_vcgenid) {
247 			err = ESTALE;
248 			goto out;
249 		}
250 		ioc->ioc_setup[1] = (uint16_t)sdp->sd_smbfid;
251 	}
252 
253 	t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP);
254 	err = smb_t2_init(t2p, SSTOCP(ssp),
255 	    ioc->ioc_setup, ioc->ioc_setupcnt, &scred);
256 	if (err)
257 		goto out;
258 	t2p->t2_setupcount = ioc->ioc_setupcnt;
259 	t2p->t2_setupdata  = ioc->ioc_setup;
260 
261 	/* This ioc member is a fixed-size array. */
262 	if (ioc->ioc_name[0]) {
263 		/* Get the name length - carefully! */
264 		ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0';
265 		t2p->t_name_len = strlen(ioc->ioc_name);
266 		t2p->t_name = ioc->ioc_name;
267 	}
268 	t2p->t2_maxscount = 0;
269 	t2p->t2_maxpcount = ioc->ioc_rparamcnt;
270 	t2p->t2_maxdcount = ioc->ioc_rdatacnt;
271 
272 	/* Transmit parameters */
273 	err = smb_cpdatain(&t2p->t2_tparam,
274 	    ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg);
275 	if (err)
276 		goto out;
277 
278 	/* Transmit data */
279 	err = smb_cpdatain(&t2p->t2_tdata,
280 	    ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg);
281 	if (err)
282 		goto out;
283 
284 	err = smb_t2_request(t2p);
285 
286 	/* Copyout returned parameters. */
287 	mdp = &t2p->t2_rparam;
288 	if (err == 0 && mdp->md_top != NULL) {
289 		/* User's buffer large enough? */
290 		len = m_fixhdr(mdp->md_top);
291 		if (len > ioc->ioc_rparamcnt) {
292 			err = EMSGSIZE;
293 			goto out;
294 		}
295 		ioc->ioc_rparamcnt = (ushort_t)len;
296 		err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg);
297 		if (err)
298 			goto out;
299 	} else
300 		ioc->ioc_rparamcnt = 0;
301 
302 	/* Copyout returned data. */
303 	mdp = &t2p->t2_rdata;
304 	if (err == 0 && mdp->md_top != NULL) {
305 		/* User's buffer large enough? */
306 		len = m_fixhdr(mdp->md_top);
307 		if (len > ioc->ioc_rdatacnt) {
308 			err = EMSGSIZE;
309 			goto out;
310 		}
311 		ioc->ioc_rdatacnt = (ushort_t)len;
312 		err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg);
313 		if (err)
314 			goto out;
315 	} else
316 		ioc->ioc_rdatacnt = 0;
317 
318 	ioc->ioc_errclass = t2p->t2_sr_errclass;
319 	ioc->ioc_serror = t2p->t2_sr_serror;
320 	ioc->ioc_error = t2p->t2_sr_error;
321 	ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2;
322 
323 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
324 
325 
326 out:
327 	if (t2p != NULL) {
328 		/* Note: t2p->t_name no longer allocated */
329 		smb_t2_done(t2p);
330 		kmem_free(t2p, sizeof (*t2p));
331 	}
332 	kmem_free(ioc, sizeof (*ioc));
333 	smb_credrele(&scred);
334 
335 	return (err);
336 }
337 
338 /* helper for _t2request */
339 static int
340 smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
341 {
342 	int error;
343 
344 	if (len == 0)
345 		return (0);
346 	error = mb_init(mbp);
347 	if (error)
348 		return (error);
349 	return (mb_put_mem(mbp, data, len, mbseg));
350 }
351 
352 /*
353  * Helper for nsmb_ioctl cases
354  * SMBIOC_READ, SMBIOC_WRITE
355  */
356 int
357 smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
358 {
359 	struct smb_cred scred;
360 	struct smb_share *ssp;
361 	smbioc_rw_t *ioc = NULL;
362 	struct iovec aiov[1];
363 	struct uio  auio;
364 	uint16_t fh;
365 	int err;
366 	uio_rw_t rw;
367 
368 	/* This ioctl requires a share. */
369 	if ((ssp = sdp->sd_share) == NULL)
370 		return (ENOTCONN);
371 
372 	/* After reconnect, force close+reopen */
373 	if (sdp->sd_vcgenid != ssp->ss_vcgenid)
374 		return (ESTALE);
375 
376 	smb_credinit(&scred, cr);
377 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
378 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
379 		err = EFAULT;
380 		goto out;
381 	}
382 
383 	switch (cmd) {
384 	case SMBIOC_READ:
385 		rw = UIO_READ;
386 		break;
387 	case SMBIOC_WRITE:
388 		rw = UIO_WRITE;
389 		break;
390 	default:
391 		err = ENODEV;
392 		goto out;
393 	}
394 
395 	/*
396 	 * If caller passes -1 in ioc_fh, then
397 	 * use the FID from SMBIOC_NTCREATE.
398 	 */
399 	if (ioc->ioc_fh == -1)
400 		fh = (uint16_t)sdp->sd_smbfid;
401 	else
402 		fh = (uint16_t)ioc->ioc_fh;
403 
404 	aiov[0].iov_base = ioc->ioc_base;
405 	aiov[0].iov_len = (size_t)ioc->ioc_cnt;
406 
407 	auio.uio_iov = aiov;
408 	auio.uio_iovcnt = 1;
409 	auio.uio_loffset = ioc->ioc_offset;
410 	auio.uio_segflg = (flags & FKIOCTL) ?
411 	    UIO_SYSSPACE : UIO_USERSPACE;
412 	auio.uio_fmode = 0;
413 	auio.uio_resid = (size_t)ioc->ioc_cnt;
414 
415 	err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0);
416 
417 	/*
418 	 * On return ioc_cnt holds the
419 	 * number of bytes transferred.
420 	 */
421 	ioc->ioc_cnt -= auio.uio_resid;
422 
423 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
424 
425 out:
426 	kmem_free(ioc, sizeof (*ioc));
427 	smb_credrele(&scred);
428 
429 	return (err);
430 }
431 
432 /*
433  * Helper for nsmb_ioctl case
434  * SMBIOC_NTCREATE
435  */
436 int
437 smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
438 {
439 	struct smb_cred scred;
440 	struct mbchain name_mb;
441 	struct smb_share *ssp;
442 	smbioc_ntcreate_t *ioc = NULL;
443 	uint16_t fid;
444 	int err, nmlen;
445 
446 	/* This ioctl requires a share. */
447 	if ((ssp = sdp->sd_share) == NULL)
448 		return (ENOTCONN);
449 
450 	/* Must not be already open. */
451 	if (sdp->sd_smbfid != -1)
452 		return (EINVAL);
453 
454 	mb_init(&name_mb);
455 	smb_credinit(&scred, cr);
456 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
457 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
458 		err = EFAULT;
459 		goto out;
460 	}
461 
462 	/* Build name_mb */
463 	ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0';
464 	nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1);
465 	err = smb_put_dmem(&name_mb, SSTOVC(ssp),
466 	    ioc->ioc_name, nmlen,
467 	    SMB_CS_NONE, NULL);
468 	if (err != 0)
469 		goto out;
470 
471 	/* Do the OtW open, save the FID. */
472 	err = smb_smb_ntcreate(ssp, &name_mb,
473 	    0,	/* create flags */
474 	    ioc->ioc_req_acc,
475 	    ioc->ioc_efattr,
476 	    ioc->ioc_share_acc,
477 	    ioc->ioc_open_disp,
478 	    ioc->ioc_creat_opts,
479 	    NTCREATEX_IMPERSONATION_IMPERSONATION,
480 	    &scred,
481 	    &fid,
482 	    NULL,
483 	    NULL);
484 	if (err != 0)
485 		goto out;
486 
487 	sdp->sd_smbfid = fid;
488 	sdp->sd_vcgenid = ssp->ss_vcgenid;
489 
490 out:
491 	kmem_free(ioc, sizeof (*ioc));
492 	smb_credrele(&scred);
493 	mb_done(&name_mb);
494 
495 	return (err);
496 }
497 
498 /*
499  * Helper for nsmb_ioctl case
500  * SMBIOC_PRINTJOB
501  */
502 int
503 smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
504 {
505 	struct smb_cred scred;
506 	struct smb_share *ssp;
507 	smbioc_printjob_t *ioc = NULL;
508 	uint16_t fid;
509 	int err;
510 
511 	/* This ioctl requires a share. */
512 	if ((ssp = sdp->sd_share) == NULL)
513 		return (ENOTCONN);
514 
515 	/* The share must be a print queue. */
516 	if (ssp->ss_type != STYPE_PRINTQ)
517 		return (EINVAL);
518 
519 	/* Must not be already open. */
520 	if (sdp->sd_smbfid != -1)
521 		return (EINVAL);
522 
523 	smb_credinit(&scred, cr);
524 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
525 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
526 		err = EFAULT;
527 		goto out;
528 	}
529 	ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
530 
531 	/* Do the OtW open, save the FID. */
532 	err = smb_smb_open_prjob(ssp, ioc->ioc_title,
533 	    ioc->ioc_setuplen, ioc->ioc_prmode,
534 	    &scred, &fid);
535 	if (err != 0)
536 		goto out;
537 
538 	sdp->sd_smbfid = fid;
539 	sdp->sd_vcgenid = ssp->ss_vcgenid;
540 
541 out:
542 	kmem_free(ioc, sizeof (*ioc));
543 	smb_credrele(&scred);
544 
545 	return (err);
546 }
547 
548 /*
549  * Helper for nsmb_ioctl case
550  * SMBIOC_CLOSEFH
551  */
552 int
553 smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
554 {
555 	struct smb_cred scred;
556 	struct smb_share *ssp;
557 	uint16_t fid;
558 	int err;
559 
560 	/* This ioctl requires a share. */
561 	if ((ssp = sdp->sd_share) == NULL)
562 		return (ENOTCONN);
563 
564 	if (sdp->sd_smbfid == -1)
565 		return (0);
566 	fid = (uint16_t)sdp->sd_smbfid;
567 	sdp->sd_smbfid = -1;
568 
569 	smb_credinit(&scred, cr);
570 	if (ssp->ss_type == STYPE_PRINTQ)
571 		err = smb_smb_close_prjob(ssp, fid, &scred);
572 	else
573 		err = smb_smb_close(ssp, fid, NULL, &scred);
574 	smb_credrele(&scred);
575 
576 	return (err);
577 }
578 
579 /*
580  * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
581  * Find or create a session (a.k.a. "VC" in here)
582  */
583 int
584 smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
585 {
586 	struct smb_cred scred;
587 	smbioc_ossn_t *ossn = NULL;
588 	struct smb_vc *vcp = NULL;
589 	int error = 0;
590 	uid_t realuid;
591 
592 	/* Should be no VC */
593 	if (sdp->sd_vc != NULL)
594 		return (EISCONN);
595 
596 	smb_credinit(&scred, cr);
597 	ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
598 	if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
599 		error = EFAULT;
600 		goto out;
601 	}
602 
603 	/*
604 	 * Only superuser can specify a UID or GID.
605 	 */
606 	realuid = crgetruid(cr);
607 	if (ossn->ssn_owner == SMBM_ANY_OWNER)
608 		ossn->ssn_owner = realuid;
609 	else {
610 		/*
611 		 * Do we have the privilege to create with the
612 		 * specified uid?  (does uid == cr->cr_uid, etc.)
613 		 */
614 		if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
615 			error = EPERM;
616 			goto out;
617 		}
618 		/* ossn->ssn_owner is OK */
619 	}
620 
621 	/*
622 	 * Make sure the strings are null terminated.
623 	 */
624 	ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
625 	ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
626 	ossn->ssn_id.id_user[   SMBIOC_MAX_NAME-1] = '\0';
627 
628 	if (cmd == SMBIOC_SSN_CREATE)
629 		ossn->ssn_vopt |= SMBVOPT_CREATE;
630 	else /* FIND */
631 		ossn->ssn_vopt &= ~SMBVOPT_CREATE;
632 
633 	error = smb_vc_findcreate(ossn, &scred, &vcp);
634 	if (error)
635 		goto out;
636 	ASSERT(vcp != NULL);
637 
638 	/*
639 	 * We have a VC, held, but not locked.
640 	 * If we're creating, mark this instance as
641 	 * an open from IOD so close can do cleanup.
642 	 *
643 	 * XXX: Would be nice to have a back pointer
644 	 * from the VC to this (IOD) sdp instance.
645 	 */
646 	if (cmd == SMBIOC_SSN_CREATE) {
647 		if (vcp->iod_thr != NULL) {
648 			error = EEXIST;
649 			goto out;
650 		}
651 		sdp->sd_flags |= NSMBFL_IOD;
652 	} else {
653 		/*
654 		 * Wait for it to finish connecting
655 		 * (or reconnect) if necessary.
656 		 */
657 		if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
658 			error = smb_iod_reconnect(vcp);
659 			if (error != 0)
660 				goto out;
661 		}
662 	}
663 
664 	/*
665 	 * The VC has a hold from _findvc
666 	 * which we keep until _SSN_RELE
667 	 * or nsmb_close().
668 	 */
669 	sdp->sd_level = SMBL_VC;
670 	sdp->sd_vc = vcp;
671 	vcp = NULL;
672 	(void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
673 
674 out:
675 	if (vcp) {
676 		/* Error path: rele hold from _findcreate */
677 		smb_vc_rele(vcp);
678 	}
679 	kmem_free(ossn, sizeof (*ossn));
680 	smb_credrele(&scred);
681 
682 	return (error);
683 }
684 
685 /*
686  * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
687  * Release or kill the current session.
688  */
689 int
690 smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
691 {
692 	struct smb_vc *vcp = NULL;
693 
694 	/* Must have a VC. */
695 	if ((vcp = sdp->sd_vc) == NULL)
696 		return (ENOTCONN);
697 
698 	/* If we have a share ref, drop it too. */
699 	if (sdp->sd_share) {
700 		smb_share_rele(sdp->sd_share);
701 		sdp->sd_share = NULL;
702 		sdp->sd_level = SMBL_VC;
703 	}
704 
705 	if (cmd == SMBIOC_SSN_KILL)
706 		smb_vc_kill(vcp);
707 
708 	/* Drop the VC ref. */
709 	smb_vc_rele(vcp);
710 	sdp->sd_vc = NULL;
711 	sdp->sd_level = 0;
712 
713 	return (0);
714 }
715 
716 /*
717  * Find or create a tree (connected share)
718  */
719 int
720 smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
721 {
722 	struct smb_cred scred;
723 	smbioc_tcon_t *tcon = NULL;
724 	struct smb_vc *vcp = NULL;
725 	struct smb_share *ssp = NULL;
726 	int error = 0;
727 
728 	/* Must have a VC. */
729 	if ((vcp = sdp->sd_vc) == NULL)
730 		return (ENOTCONN);
731 	/* Should not have a share. */
732 	if (sdp->sd_share != NULL)
733 		return (EISCONN);
734 
735 	smb_credinit(&scred, cr);
736 	tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
737 	if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
738 		error = EFAULT;
739 		goto out;
740 	}
741 
742 	/*
743 	 * Make sure the strings are null terminated.
744 	 */
745 	tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
746 	tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
747 
748 	if (cmd == SMBIOC_TREE_CONNECT)
749 		tcon->tc_opt |= SMBSOPT_CREATE;
750 	else /* FIND */
751 		tcon->tc_opt &= ~SMBSOPT_CREATE;
752 
753 	error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
754 	if (error)
755 		goto out;
756 	ASSERT(ssp != NULL);
757 
758 	/*
759 	 * We have a share, held, but not locked.
760 	 * If we're creating, do tree connect now,
761 	 * otherwise let that wait for a request.
762 	 */
763 	if (cmd == SMBIOC_TREE_CONNECT) {
764 		error = smb_share_tcon(ssp, &scred);
765 		if (error)
766 			goto out;
767 	}
768 
769 	/*
770 	 * Give caller the real share type from
771 	 * the tree connect response, so they can
772 	 * see if they got the requested type.
773 	 */
774 	tcon->tc_sh.sh_type = ssp->ss_type;
775 
776 	/*
777 	 * The share has a hold from _tcon
778 	 * which we keep until nsmb_close()
779 	 * or the SMBIOC_TDIS below.
780 	 */
781 	sdp->sd_level = SMBL_SHARE;
782 	sdp->sd_share = ssp;
783 	ssp = NULL;
784 	(void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
785 
786 out:
787 	if (ssp) {
788 		/* Error path: rele hold from _findcreate */
789 		smb_share_rele(ssp);
790 	}
791 	/*
792 	 * This structure may contain a
793 	 * cleartext password, so zap it.
794 	 */
795 	bzero(tcon, sizeof (*tcon));
796 	kmem_free(tcon, sizeof (*tcon));
797 	smb_credrele(&scred);
798 
799 	return (error);
800 }
801 
802 /*
803  * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
804  * Release or kill the current tree
805  */
806 int
807 smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
808 {
809 	struct smb_share *ssp = NULL;
810 
811 	/* Must have a VC and a share. */
812 	if (sdp->sd_vc == NULL)
813 		return (ENOTCONN);
814 	if ((ssp = sdp->sd_share) == NULL)
815 		return (ENOTCONN);
816 
817 	if (cmd == SMBIOC_TREE_KILL)
818 		smb_share_kill(ssp);
819 
820 	/* Drop the share ref. */
821 	smb_share_rele(sdp->sd_share);
822 	sdp->sd_share = NULL;
823 	sdp->sd_level = SMBL_VC;
824 
825 	return (0);
826 }
827 
828 
829 /*
830  * Ioctl function: SMBIOC_IOD_WORK
831  *
832  * Become the reader (IOD) thread, until either the connection is
833  * reset by the server, or until the connection is idle longer than
834  * some max time. (max idle time not yet implemented)
835  */
836 int
837 smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
838 {
839 	struct smb_vc *vcp = NULL;
840 	int err = 0;
841 
842 	/* Must have a valid session. */
843 	if ((vcp = sdp->sd_vc) == NULL)
844 		return (EINVAL);
845 	if (vcp->vc_flags & SMBV_GONE)
846 		return (EINVAL);
847 
848 	/*
849 	 * Is there already an IOD for this VC?
850 	 * (Should never happen.)
851 	 */
852 	SMB_VC_LOCK(vcp);
853 	if (vcp->iod_thr == NULL)
854 		vcp->iod_thr = curthread;
855 	else
856 		err = EEXIST;
857 	SMB_VC_UNLOCK(vcp);
858 	if (err)
859 		return (err);
860 
861 	/*
862 	 * Copy the "work" state, etc. into the VC
863 	 * The MAC key is copied separately.
864 	 */
865 	if (ddi_copyin((void *)arg, &vcp->vc_work,
866 	    sizeof (smbioc_ssn_work_t), flags)) {
867 		err = EFAULT;
868 		goto out;
869 	}
870 	if (vcp->vc_u_maclen) {
871 		vcp->vc_mackeylen = vcp->vc_u_maclen;
872 		vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP);
873 		if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey,
874 		    vcp->vc_mackeylen, flags)) {
875 			err = EFAULT;
876 			goto out;
877 		}
878 	}
879 
880 	err = smb_iod_vc_work(vcp, cr);
881 
882 	/* Caller wants state here. */
883 	vcp->vc_work.wk_out_state = vcp->vc_state;
884 
885 	(void) ddi_copyout(&vcp->vc_work, (void *)arg,
886 	    sizeof (smbioc_ssn_work_t), flags);
887 
888 out:
889 	if (vcp->vc_mackey) {
890 		kmem_free(vcp->vc_mackey, vcp->vc_mackeylen);
891 		vcp->vc_mackey = NULL;
892 		vcp->vc_mackeylen = 0;
893 	}
894 
895 	/*
896 	 * The IOD thread is leaving the driver.  Clear iod_thr,
897 	 * and wake up anybody waiting for us to quit.
898 	 */
899 	SMB_VC_LOCK(vcp);
900 	vcp->iod_thr = NULL;
901 	cv_broadcast(&vcp->vc_statechg);
902 	SMB_VC_UNLOCK(vcp);
903 
904 	return (err);
905 }
906 
907 /*
908  * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL
909  *
910  * Wait for user-level requests to be enqueued on this session,
911  * and then return to the user-space helper, which will then
912  * initiate a reconnect, etc.
913  */
914 int
915 smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags)
916 {
917 	struct smb_vc *vcp = NULL;
918 	int err = 0;
919 
920 	/* Must have a valid session. */
921 	if ((vcp = sdp->sd_vc) == NULL)
922 		return (EINVAL);
923 	if (vcp->vc_flags & SMBV_GONE)
924 		return (EINVAL);
925 
926 	/*
927 	 * Is there already an IOD for this VC?
928 	 * (Should never happen.)
929 	 */
930 	SMB_VC_LOCK(vcp);
931 	if (vcp->iod_thr == NULL)
932 		vcp->iod_thr = curthread;
933 	else
934 		err = EEXIST;
935 	SMB_VC_UNLOCK(vcp);
936 	if (err)
937 		return (err);
938 
939 	/* nothing to copyin */
940 
941 	switch (cmd) {
942 	case SMBIOC_IOD_IDLE:
943 		err = smb_iod_vc_idle(vcp);
944 		break;
945 
946 	case SMBIOC_IOD_RCFAIL:
947 		err = smb_iod_vc_rcfail(vcp);
948 		break;
949 
950 	default:
951 		err = ENOTTY;
952 		goto out;
953 	}
954 
955 	/* Both of these ioctls copy out the new state. */
956 	(void) ddi_copyout(&vcp->vc_state, (void *)arg,
957 	    sizeof (int), flags);
958 
959 out:
960 	/*
961 	 * The IOD thread is leaving the driver.  Clear iod_thr,
962 	 * and wake up anybody waiting for us to quit.
963 	 */
964 	SMB_VC_LOCK(vcp);
965 	vcp->iod_thr = NULL;
966 	cv_broadcast(&vcp->vc_statechg);
967 	SMB_VC_UNLOCK(vcp);
968 
969 	return (err);
970 }
971