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
63static 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 */
74int
75smb_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 */
101int
102smb_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
181out:
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 */
190static int
191smb_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 */
207int
208smb_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
269out:
270	kmem_free(ioc, sizeof (*ioc));
271
272	return (err);
273}
274
275/*
276 * Helper for nsmb_ioctl case
277 * SMBIOC_NTCREATE
278 */
279int
280smb_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
343out:
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 */
356int
357smb_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
446out:
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*/
460int
461smb_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 */
480int
481smb_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
571out:
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 */
586int
587smb_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 */
616int
617smb_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
683out:
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 */
703int
704smb_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 */
728int
729smb_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
799out:
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
816int
817smb_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