14bff34e3Sthurlow /*
24bff34e3Sthurlow  * Copyright (c) 2000-2001 Boris Popov
34bff34e3Sthurlow  * All rights reserved.
44bff34e3Sthurlow  *
54bff34e3Sthurlow  * Redistribution and use in source and binary forms, with or without
64bff34e3Sthurlow  * modification, are permitted provided that the following conditions
74bff34e3Sthurlow  * are met:
84bff34e3Sthurlow  * 1. Redistributions of source code must retain the above copyright
94bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer.
104bff34e3Sthurlow  * 2. Redistributions in binary form must reproduce the above copyright
114bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer in the
124bff34e3Sthurlow  *    documentation and/or other materials provided with the distribution.
134bff34e3Sthurlow  * 3. All advertising materials mentioning features or use of this software
144bff34e3Sthurlow  *    must display the following acknowledgement:
154bff34e3Sthurlow  *    This product includes software developed by Boris Popov.
164bff34e3Sthurlow  * 4. Neither the name of the author nor the names of any co-contributors
174bff34e3Sthurlow  *    may be used to endorse or promote products derived from this software
184bff34e3Sthurlow  *    without specific prior written permission.
194bff34e3Sthurlow  *
204bff34e3Sthurlow  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
214bff34e3Sthurlow  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224bff34e3Sthurlow  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234bff34e3Sthurlow  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
244bff34e3Sthurlow  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254bff34e3Sthurlow  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264bff34e3Sthurlow  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274bff34e3Sthurlow  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284bff34e3Sthurlow  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294bff34e3Sthurlow  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304bff34e3Sthurlow  * SUCH DAMAGE.
314bff34e3Sthurlow  *
324bff34e3Sthurlow  * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
334bff34e3Sthurlow  */
344bff34e3Sthurlow 
359c9af259SGordon Ross /*
36613a2f6bSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
379c9af259SGordon Ross  * Use is subject to license terms.
3840c0e231SGordon Ross  *
39*e3a966feSGordon Ross  * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
409c9af259SGordon Ross  */
414bff34e3Sthurlow 
424bff34e3Sthurlow #include <sys/param.h>
434bff34e3Sthurlow #include <sys/kmem.h>
444bff34e3Sthurlow #include <sys/systm.h>
454bff34e3Sthurlow #include <sys/policy.h>
464bff34e3Sthurlow #include <sys/conf.h>
474bff34e3Sthurlow #include <sys/proc.h>
484bff34e3Sthurlow #include <sys/fcntl.h>
49613a2f6bSGordon Ross #include <sys/file.h>
504bff34e3Sthurlow #include <sys/socket.h>
51613a2f6bSGordon Ross #include <sys/sunddi.h>
524bff34e3Sthurlow #include <sys/cmn_err.h>
534bff34e3Sthurlow 
544bff34e3Sthurlow #include <netsmb/smb_osdep.h>
554bff34e3Sthurlow 
56adee6784SGordon Ross #include <smb/winioctl.h>
574bff34e3Sthurlow #include <netsmb/smb.h>
584bff34e3Sthurlow #include <netsmb/smb_conn.h>
594bff34e3Sthurlow #include <netsmb/smb_rq.h>
604bff34e3Sthurlow #include <netsmb/smb_subr.h>
614bff34e3Sthurlow #include <netsmb/smb_dev.h>
624bff34e3Sthurlow 
63613a2f6bSGordon Ross static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
64613a2f6bSGordon Ross 
654bff34e3Sthurlow /*
66613a2f6bSGordon Ross  * Ioctl function for SMBIOC_GETSSNKEY
67613a2f6bSGordon Ross  * Size copied out is SMBIOC_HASH_SZ.
68613a2f6bSGordon Ross  *
69613a2f6bSGordon Ross  * The RPC library needs this for encrypting things
70613a2f6bSGordon Ross  * like "set password" requests.  This is called
71613a2f6bSGordon Ross  * with an active RPC binding, so the connection
72613a2f6bSGordon Ross  * will already be active (but this checks).
734bff34e3Sthurlow  */
74613a2f6bSGordon Ross int
smb_usr_get_ssnkey(smb_dev_t * sdp,intptr_t arg,int flags)75613a2f6bSGordon Ross smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags)
764bff34e3Sthurlow {
77613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
78613a2f6bSGordon Ross 
79613a2f6bSGordon Ross 	/* This ioctl requires an active session. */
80613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
81613a2f6bSGordon Ross 		return (ENOTCONN);
82613a2f6bSGordon Ross 	if (vcp->vc_state != SMBIOD_ST_VCACTIVE)
83613a2f6bSGordon Ross 		return (ENOTCONN);
844bff34e3Sthurlow 
854bff34e3Sthurlow 	/*
86613a2f6bSGordon Ross 	 * Return the session key.
874bff34e3Sthurlow 	 */
8840c0e231SGordon Ross 	if (vcp->vc_ssnkey == NULL ||
8940c0e231SGordon Ross 	    vcp->vc_ssnkeylen < SMBIOC_HASH_SZ)
9040c0e231SGordon Ross 		return (EINVAL);
9140c0e231SGordon Ross 	if (ddi_copyout(vcp->vc_ssnkey, (void *)arg,
92613a2f6bSGordon Ross 	    SMBIOC_HASH_SZ, flags))
93613a2f6bSGordon Ross 		return (EFAULT);
94613a2f6bSGordon Ross 
95613a2f6bSGordon Ross 	return (0);
96613a2f6bSGordon Ross }
97613a2f6bSGordon Ross 
98613a2f6bSGordon Ross /*
9940c0e231SGordon Ross  * Ioctl function for SMBIOC_XACTNP (transact named pipe)
100613a2f6bSGordon Ross  */
101613a2f6bSGordon Ross int
smb_usr_xnp(smb_dev_t * sdp,intptr_t arg,int flags,cred_t * cr)10240c0e231SGordon Ross smb_usr_xnp(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
1034bff34e3Sthurlow {
104613a2f6bSGordon Ross 	struct smb_cred scred;
105613a2f6bSGordon Ross 	struct smb_share *ssp;
106adee6784SGordon Ross 	struct smb_fh *fhp;
10740c0e231SGordon Ross 	smbioc_xnp_t *ioc = NULL;
108adee6784SGordon Ross 	struct mbchain send_mb;
109adee6784SGordon Ross 	struct mdchain recv_md;
110adee6784SGordon Ross 	uint32_t rdlen;
111adee6784SGordon Ross 	int err, mbseg;
112613a2f6bSGordon Ross 
113adee6784SGordon Ross 	/* This ioctl requires a file handle. */
114adee6784SGordon Ross 	if ((fhp = sdp->sd_fh) == NULL)
115adee6784SGordon Ross 		return (EINVAL);
116adee6784SGordon Ross 	ssp = FHTOSS(fhp);
117adee6784SGordon Ross 
118adee6784SGordon Ross 	/* After reconnect, force close+reopen */
119adee6784SGordon Ross 	if (fhp->fh_vcgenid != ssp->ss_vcgenid)
120adee6784SGordon Ross 		return (ESTALE);
121adee6784SGordon Ross 
122adee6784SGordon Ross 	bzero(&send_mb, sizeof (send_mb));
123adee6784SGordon Ross 	bzero(&recv_md, sizeof (recv_md));
1244bff34e3Sthurlow 
125613a2f6bSGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
126613a2f6bSGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
127613a2f6bSGordon Ross 		err = EFAULT;
128613a2f6bSGordon Ross 		goto out;
1294bff34e3Sthurlow 	}
130613a2f6bSGordon Ross 
131430b4c46SGordon Ross 	/*
132adee6784SGordon Ross 	 * Copyin the send data, into an mbchain,
133adee6784SGordon Ross 	 * save output buffer size.
134430b4c46SGordon Ross 	 */
135adee6784SGordon Ross 	mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
136adee6784SGordon Ross 	err = smb_cpdatain(&send_mb, ioc->ioc_tdlen, ioc->ioc_tdata, mbseg);
137613a2f6bSGordon Ross 	if (err)
138613a2f6bSGordon Ross 		goto out;
139adee6784SGordon Ross 	rdlen = ioc->ioc_rdlen;
140613a2f6bSGordon Ross 
141adee6784SGordon Ross 	/*
142adee6784SGordon Ross 	 * Run the SMB2 ioctl or SMB1 trans2
143adee6784SGordon Ross 	 */
144adee6784SGordon Ross 	smb_credinit(&scred, cr);
145adee6784SGordon Ross 	if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
146adee6784SGordon Ross 		err = smb2_smb_ioctl(ssp, &fhp->fh_fid2,
147adee6784SGordon Ross 		    &send_mb, &recv_md, &rdlen,
148adee6784SGordon Ross 		    FSCTL_PIPE_TRANSCEIVE, &scred);
149adee6784SGordon Ross 	} else {
150adee6784SGordon Ross 		err = smb_t2_xnp(ssp, fhp->fh_fid1,
151adee6784SGordon Ross 		    &send_mb, &recv_md, &rdlen,
152adee6784SGordon Ross 		    &ioc->ioc_more, &scred);
153adee6784SGordon Ross 	}
154adee6784SGordon Ross 	smb_credrele(&scred);
155613a2f6bSGordon Ross 
156613a2f6bSGordon Ross 	/* Copyout returned data. */
157adee6784SGordon Ross 	if (err == 0 && recv_md.md_top != NULL) {
158adee6784SGordon Ross 		/* User's buffer large enough for copyout? */
159adee6784SGordon Ross 		size_t len = m_fixhdr(recv_md.md_top);
16040c0e231SGordon Ross 		if (len > ioc->ioc_rdlen) {
161613a2f6bSGordon Ross 			err = EMSGSIZE;
162613a2f6bSGordon Ross 			goto out;
163613a2f6bSGordon Ross 		}
164adee6784SGordon Ross 		err = md_get_mem(&recv_md, ioc->ioc_rdata, len, mbseg);
165613a2f6bSGordon Ross 		if (err)
166613a2f6bSGordon Ross 			goto out;
167613a2f6bSGordon Ross 	} else
16840c0e231SGordon Ross 		ioc->ioc_rdlen = 0;
169613a2f6bSGordon Ross 
170adee6784SGordon Ross 	/* Tell caller received length */
171adee6784SGordon Ross 	if (rdlen <= ioc->ioc_rdlen) {
172adee6784SGordon Ross 		/* Normal case */
173adee6784SGordon Ross 		ioc->ioc_rdlen = rdlen;
174adee6784SGordon Ross 	} else {
175adee6784SGordon Ross 		/* Buffer overlow. Leave ioc_rdlen */
17640c0e231SGordon Ross 		ioc->ioc_more = 1;
177adee6784SGordon Ross 	}
178613a2f6bSGordon Ross 
17902d09e03SGordon Ross 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
180613a2f6bSGordon Ross 
181613a2f6bSGordon Ross out:
182430b4c46SGordon Ross 	kmem_free(ioc, sizeof (*ioc));
183*e3a966feSGordon Ross 	md_done(&recv_md);
184*e3a966feSGordon Ross 	mb_done(&send_mb);
185613a2f6bSGordon Ross 
186613a2f6bSGordon Ross 	return (err);
1874bff34e3Sthurlow }
1884bff34e3Sthurlow 
189613a2f6bSGordon Ross /* helper for _t2request */
1904bff34e3Sthurlow static int
smb_cpdatain(struct mbchain * mbp,int len,char * data,int mbseg)191613a2f6bSGordon Ross smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
1924bff34e3Sthurlow {
193613a2f6bSGordon Ross 	int error;
194613a2f6bSGordon Ross 
195613a2f6bSGordon Ross 	if (len == 0)
196613a2f6bSGordon Ross 		return (0);
197613a2f6bSGordon Ross 	error = mb_init(mbp);
198613a2f6bSGordon Ross 	if (error)
199613a2f6bSGordon Ross 		return (error);
200613a2f6bSGordon Ross 	return (mb_put_mem(mbp, data, len, mbseg));
2014bff34e3Sthurlow }
2024bff34e3Sthurlow 
203613a2f6bSGordon Ross /*
204613a2f6bSGordon Ross  * Helper for nsmb_ioctl cases
205613a2f6bSGordon Ross  * SMBIOC_READ, SMBIOC_WRITE
206613a2f6bSGordon Ross  */
2071b34bc4aSbs int
smb_usr_rw(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)208613a2f6bSGordon Ross smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
2091b34bc4aSbs {
210613a2f6bSGordon Ross 	struct smb_cred scred;
211613a2f6bSGordon Ross 	struct smb_share *ssp;
212adee6784SGordon Ross 	struct smb_fh *fhp;
213613a2f6bSGordon Ross 	smbioc_rw_t *ioc = NULL;
214613a2f6bSGordon Ross 	struct iovec aiov[1];
215613a2f6bSGordon Ross 	struct uio  auio;
216613a2f6bSGordon Ross 	int err;
217613a2f6bSGordon Ross 	uio_rw_t rw;
2181b34bc4aSbs 
219adee6784SGordon Ross 	/* This ioctl requires a file handle. */
220adee6784SGordon Ross 	if ((fhp = sdp->sd_fh) == NULL)
221adee6784SGordon Ross 		return (EINVAL);
222adee6784SGordon Ross 	ssp = FHTOSS(fhp);
223613a2f6bSGordon Ross 
224613a2f6bSGordon Ross 	/* After reconnect, force close+reopen */
225adee6784SGordon Ross 	if (fhp->fh_vcgenid != ssp->ss_vcgenid)
226613a2f6bSGordon Ross 		return (ESTALE);
227613a2f6bSGordon Ross 
228613a2f6bSGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
229613a2f6bSGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
230613a2f6bSGordon Ross 		err = EFAULT;
2311b34bc4aSbs 		goto out;
232613a2f6bSGordon Ross 	}
233613a2f6bSGordon Ross 
234613a2f6bSGordon Ross 	switch (cmd) {
235613a2f6bSGordon Ross 	case SMBIOC_READ:
236613a2f6bSGordon Ross 		rw = UIO_READ;
237613a2f6bSGordon Ross 		break;
238613a2f6bSGordon Ross 	case SMBIOC_WRITE:
239613a2f6bSGordon Ross 		rw = UIO_WRITE;
240613a2f6bSGordon Ross 		break;
241613a2f6bSGordon Ross 	default:
242613a2f6bSGordon Ross 		err = ENODEV;
243613a2f6bSGordon Ross 		goto out;
244613a2f6bSGordon Ross 	}
245613a2f6bSGordon Ross 
246613a2f6bSGordon Ross 	aiov[0].iov_base = ioc->ioc_base;
247613a2f6bSGordon Ross 	aiov[0].iov_len = (size_t)ioc->ioc_cnt;
248613a2f6bSGordon Ross 
249613a2f6bSGordon Ross 	auio.uio_iov = aiov;
250613a2f6bSGordon Ross 	auio.uio_iovcnt = 1;
251613a2f6bSGordon Ross 	auio.uio_loffset = ioc->ioc_offset;
252613a2f6bSGordon Ross 	auio.uio_segflg = (flags & FKIOCTL) ?
253613a2f6bSGordon Ross 	    UIO_SYSSPACE : UIO_USERSPACE;
254613a2f6bSGordon Ross 	auio.uio_fmode = 0;
255613a2f6bSGordon Ross 	auio.uio_resid = (size_t)ioc->ioc_cnt;
256613a2f6bSGordon Ross 
257adee6784SGordon Ross 	smb_credinit(&scred, cr);
258adee6784SGordon Ross 	err = smb_rwuio(fhp, rw, &auio, &scred, 0);
259adee6784SGordon Ross 	smb_credrele(&scred);
260613a2f6bSGordon Ross 
261613a2f6bSGordon Ross 	/*
262613a2f6bSGordon Ross 	 * On return ioc_cnt holds the
263613a2f6bSGordon Ross 	 * number of bytes transferred.
264613a2f6bSGordon Ross 	 */
265613a2f6bSGordon Ross 	ioc->ioc_cnt -= auio.uio_resid;
266613a2f6bSGordon Ross 
26702d09e03SGordon Ross 	(void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
268613a2f6bSGordon Ross 
2691b34bc4aSbs out:
270430b4c46SGordon Ross 	kmem_free(ioc, sizeof (*ioc));
271430b4c46SGordon Ross 
272430b4c46SGordon Ross 	return (err);
273430b4c46SGordon Ross }
274430b4c46SGordon Ross 
275430b4c46SGordon Ross /*
276430b4c46SGordon Ross  * Helper for nsmb_ioctl case
277430b4c46SGordon Ross  * SMBIOC_NTCREATE
278430b4c46SGordon Ross  */
279430b4c46SGordon Ross int
smb_usr_ntcreate(smb_dev_t * sdp,intptr_t arg,int flags,cred_t * cr)280430b4c46SGordon Ross smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
281430b4c46SGordon Ross {
282430b4c46SGordon Ross 	struct smb_cred scred;
283430b4c46SGordon Ross 	struct mbchain name_mb;
284430b4c46SGordon Ross 	struct smb_share *ssp;
285adee6784SGordon Ross 	struct smb_fh *fhp = NULL;
286430b4c46SGordon Ross 	smbioc_ntcreate_t *ioc = NULL;
287430b4c46SGordon Ross 	int err, nmlen;
288430b4c46SGordon Ross 
289adee6784SGordon Ross 	mb_init(&name_mb);
290adee6784SGordon Ross 
291430b4c46SGordon Ross 	/* This ioctl requires a share. */
292430b4c46SGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
293430b4c46SGordon Ross 		return (ENOTCONN);
294430b4c46SGordon Ross 
295adee6784SGordon Ross 	/* Must not already have a file handle. */
296adee6784SGordon Ross 	if (sdp->sd_fh != NULL)
297430b4c46SGordon Ross 		return (EINVAL);
298430b4c46SGordon Ross 
299430b4c46SGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
300430b4c46SGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
301430b4c46SGordon Ross 		err = EFAULT;
302430b4c46SGordon Ross 		goto out;
303430b4c46SGordon Ross 	}
304430b4c46SGordon Ross 
305430b4c46SGordon Ross 	/* Build name_mb */
306430b4c46SGordon Ross 	ioc->ioc_name[SMBIOC_MAX_NAME-1] = '\0';
307430b4c46SGordon Ross 	nmlen = strnlen(ioc->ioc_name, SMBIOC_MAX_NAME-1);
308430b4c46SGordon Ross 	err = smb_put_dmem(&name_mb, SSTOVC(ssp),
309430b4c46SGordon Ross 	    ioc->ioc_name, nmlen,
310430b4c46SGordon Ross 	    SMB_CS_NONE, NULL);
311430b4c46SGordon Ross 	if (err != 0)
312430b4c46SGordon Ross 		goto out;
313430b4c46SGordon Ross 
314adee6784SGordon Ross 	err = smb_fh_create(ssp, &fhp);
315adee6784SGordon Ross 	if (err != 0)
316adee6784SGordon Ross 		goto out;
317adee6784SGordon Ross 
318adee6784SGordon Ross 	/*
319adee6784SGordon Ross 	 * Do the OtW open, save the FID.
320adee6784SGordon Ross 	 */
321adee6784SGordon Ross 	smb_credinit(&scred, cr);
322430b4c46SGordon Ross 	err = smb_smb_ntcreate(ssp, &name_mb,
323430b4c46SGordon Ross 	    0,	/* create flags */
324430b4c46SGordon Ross 	    ioc->ioc_req_acc,
325430b4c46SGordon Ross 	    ioc->ioc_efattr,
326430b4c46SGordon Ross 	    ioc->ioc_share_acc,
327430b4c46SGordon Ross 	    ioc->ioc_open_disp,
328430b4c46SGordon Ross 	    ioc->ioc_creat_opts,
329430b4c46SGordon Ross 	    NTCREATEX_IMPERSONATION_IMPERSONATION,
330430b4c46SGordon Ross 	    &scred,
331adee6784SGordon Ross 	    fhp,
332430b4c46SGordon Ross 	    NULL,
333430b4c46SGordon Ross 	    NULL);
334adee6784SGordon Ross 	smb_credrele(&scred);
335430b4c46SGordon Ross 	if (err != 0)
336430b4c46SGordon Ross 		goto out;
337430b4c46SGordon Ross 
338adee6784SGordon Ross 	fhp->fh_rights = ioc->ioc_req_acc;
339adee6784SGordon Ross 	smb_fh_opened(fhp);
340adee6784SGordon Ross 	sdp->sd_fh = fhp;
341adee6784SGordon Ross 	fhp = NULL;
342430b4c46SGordon Ross 
343430b4c46SGordon Ross out:
344adee6784SGordon Ross 	if (fhp != NULL)
345adee6784SGordon Ross 		smb_fh_rele(fhp);
346430b4c46SGordon Ross 	kmem_free(ioc, sizeof (*ioc));
347430b4c46SGordon Ross 	mb_done(&name_mb);
348430b4c46SGordon Ross 
349430b4c46SGordon Ross 	return (err);
350430b4c46SGordon Ross }
351430b4c46SGordon Ross 
352430b4c46SGordon Ross /*
353430b4c46SGordon Ross  * Helper for nsmb_ioctl case
354430b4c46SGordon Ross  * SMBIOC_PRINTJOB
355430b4c46SGordon Ross  */
356430b4c46SGordon Ross int
smb_usr_printjob(smb_dev_t * sdp,intptr_t arg,int flags,cred_t * cr)357430b4c46SGordon Ross smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
358430b4c46SGordon Ross {
359adee6784SGordon Ross 	static const char invalid_chars[] = SMB_FILENAME_INVALID_CHARS;
360430b4c46SGordon Ross 	struct smb_cred scred;
361adee6784SGordon Ross 	struct mbchain name_mb;
362430b4c46SGordon Ross 	struct smb_share *ssp;
363adee6784SGordon Ross 	struct smb_fh *fhp = NULL;
364430b4c46SGordon Ross 	smbioc_printjob_t *ioc = NULL;
365adee6784SGordon Ross 	int err, cklen, nmlen;
366adee6784SGordon Ross 	uint32_t access = SA_RIGHT_FILE_WRITE_DATA |
367adee6784SGordon Ross 	    SA_RIGHT_FILE_READ_ATTRIBUTES;
368adee6784SGordon Ross 
369adee6784SGordon Ross 	mb_init(&name_mb);
370430b4c46SGordon Ross 
371430b4c46SGordon Ross 	/* This ioctl requires a share. */
372430b4c46SGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
373430b4c46SGordon Ross 		return (ENOTCONN);
374430b4c46SGordon Ross 
375430b4c46SGordon Ross 	/* The share must be a print queue. */
376430b4c46SGordon Ross 	if (ssp->ss_type != STYPE_PRINTQ)
377430b4c46SGordon Ross 		return (EINVAL);
378430b4c46SGordon Ross 
379adee6784SGordon Ross 	/* Must not already have a file handle. */
380adee6784SGordon Ross 	if (sdp->sd_fh != NULL)
381430b4c46SGordon Ross 		return (EINVAL);
382430b4c46SGordon Ross 
383430b4c46SGordon Ross 	smb_credinit(&scred, cr);
384430b4c46SGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
385430b4c46SGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
386430b4c46SGordon Ross 		err = EFAULT;
387430b4c46SGordon Ross 		goto out;
388430b4c46SGordon Ross 	}
389adee6784SGordon Ross 
390adee6784SGordon Ross 	/*
391adee6784SGordon Ross 	 * Use the print job title as the file name to open, but
392adee6784SGordon Ross 	 * check for invalid characters first.  See the notes in
393adee6784SGordon Ross 	 * libsmbfs/smb/print.c about job name sanitizing.
394adee6784SGordon Ross 	 */
395430b4c46SGordon Ross 	ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0';
396adee6784SGordon Ross 	nmlen = strnlen(ioc->ioc_title, SMBIOC_MAX_NAME-1);
397adee6784SGordon Ross 	cklen = strcspn(ioc->ioc_title, invalid_chars);
398adee6784SGordon Ross 	if (cklen < nmlen) {
399adee6784SGordon Ross 		err = EINVAL;
400adee6784SGordon Ross 		goto out;
401adee6784SGordon Ross 	}
402430b4c46SGordon Ross 
403adee6784SGordon Ross 	/* Build name_mb */
404adee6784SGordon Ross 	err = smb_put_dmem(&name_mb, SSTOVC(ssp),
405adee6784SGordon Ross 	    ioc->ioc_title, nmlen,
406adee6784SGordon Ross 	    SMB_CS_NONE, NULL);
407430b4c46SGordon Ross 	if (err != 0)
408430b4c46SGordon Ross 		goto out;
409430b4c46SGordon Ross 
410adee6784SGordon Ross 	err = smb_fh_create(ssp, &fhp);
411adee6784SGordon Ross 	if (err != 0)
412adee6784SGordon Ross 		goto out;
413adee6784SGordon Ross 
414adee6784SGordon Ross 	/*
415adee6784SGordon Ross 	 * Do the OtW open, save the FID.
416adee6784SGordon Ross 	 */
417adee6784SGordon Ross 	smb_credinit(&scred, cr);
418adee6784SGordon Ross 	if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
419adee6784SGordon Ross 		err = smb2_smb_ntcreate(ssp, &name_mb,
420adee6784SGordon Ross 		    NULL, NULL, /* cctx in, out */
421adee6784SGordon Ross 		    0,	/* create flags */
422adee6784SGordon Ross 		    access,
423adee6784SGordon Ross 		    SMB_EFA_NORMAL,
424adee6784SGordon Ross 		    NTCREATEX_SHARE_ACCESS_NONE,
425adee6784SGordon Ross 		    NTCREATEX_DISP_CREATE,
426adee6784SGordon Ross 		    NTCREATEX_OPTIONS_NON_DIRECTORY_FILE,
427adee6784SGordon Ross 		    NTCREATEX_IMPERSONATION_IMPERSONATION,
428adee6784SGordon Ross 		    &scred,
429adee6784SGordon Ross 		    &fhp->fh_fid2,
430adee6784SGordon Ross 		    NULL,
431adee6784SGordon Ross 		    NULL);
432adee6784SGordon Ross 	} else {
433adee6784SGordon Ross 		err = smb_smb_open_prjob(ssp, ioc->ioc_title,
434adee6784SGordon Ross 		    ioc->ioc_setuplen, ioc->ioc_prmode,
435adee6784SGordon Ross 		    &scred, &fhp->fh_fid1);
436adee6784SGordon Ross 	}
437adee6784SGordon Ross 	smb_credrele(&scred);
438adee6784SGordon Ross 	if (err != 0)
439adee6784SGordon Ross 		goto out;
440adee6784SGordon Ross 
441adee6784SGordon Ross 	fhp->fh_rights = access;
442adee6784SGordon Ross 	smb_fh_opened(fhp);
443adee6784SGordon Ross 	sdp->sd_fh = fhp;
444adee6784SGordon Ross 	fhp = NULL;
445430b4c46SGordon Ross 
446430b4c46SGordon Ross out:
447adee6784SGordon Ross 	if (fhp != NULL)
448adee6784SGordon Ross 		smb_fh_rele(fhp);
449430b4c46SGordon Ross 	kmem_free(ioc, sizeof (*ioc));
450adee6784SGordon Ross 	mb_done(&name_mb);
451430b4c46SGordon Ross 
452430b4c46SGordon Ross 	return (err);
453430b4c46SGordon Ross }
454430b4c46SGordon Ross 
455430b4c46SGordon Ross /*
456430b4c46SGordon Ross  * Helper for nsmb_ioctl case
457430b4c46SGordon Ross  * SMBIOC_CLOSEFH
458430b4c46SGordon Ross  */
459adee6784SGordon Ross /*ARGSUSED*/
460430b4c46SGordon Ross int
smb_usr_closefh(smb_dev_t * sdp,cred_t * cr)461430b4c46SGordon Ross smb_usr_closefh(smb_dev_t *sdp, cred_t *cr)
462430b4c46SGordon Ross {
463adee6784SGordon Ross 	struct smb_fh *fhp;
464430b4c46SGordon Ross 
465adee6784SGordon Ross 	/* This ioctl requires a file handle. */
466adee6784SGordon Ross 	if ((fhp = sdp->sd_fh) == NULL)
467adee6784SGordon Ross 		return (EINVAL);
468adee6784SGordon Ross 	sdp->sd_fh = NULL;
469430b4c46SGordon Ross 
470adee6784SGordon Ross 	smb_fh_close(fhp);
471adee6784SGordon Ross 	smb_fh_rele(fhp);
472613a2f6bSGordon Ross 
473adee6784SGordon Ross 	return (0);
4741b34bc4aSbs }
4751b34bc4aSbs 
476613a2f6bSGordon Ross /*
477613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
478613a2f6bSGordon Ross  * Find or create a session (a.k.a. "VC" in here)
479613a2f6bSGordon Ross  */
4804bff34e3Sthurlow int
smb_usr_get_ssn(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)481613a2f6bSGordon Ross smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
4824bff34e3Sthurlow {
483613a2f6bSGordon Ross 	struct smb_cred scred;
484613a2f6bSGordon Ross 	smbioc_ossn_t *ossn = NULL;
4854bff34e3Sthurlow 	struct smb_vc *vcp = NULL;
4864bff34e3Sthurlow 	int error = 0;
487613a2f6bSGordon Ross 	uid_t realuid;
4884bff34e3Sthurlow 
489613a2f6bSGordon Ross 	/* Should be no VC */
490613a2f6bSGordon Ross 	if (sdp->sd_vc != NULL)
491613a2f6bSGordon Ross 		return (EISCONN);
492613a2f6bSGordon Ross 
493613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
494613a2f6bSGordon Ross 	ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
495613a2f6bSGordon Ross 	if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
496613a2f6bSGordon Ross 		error = EFAULT;
497613a2f6bSGordon Ross 		goto out;
4984bff34e3Sthurlow 	}
499613a2f6bSGordon Ross 
500613a2f6bSGordon Ross 	/*
501613a2f6bSGordon Ross 	 * Only superuser can specify a UID or GID.
502613a2f6bSGordon Ross 	 */
503613a2f6bSGordon Ross 	realuid = crgetruid(cr);
504613a2f6bSGordon Ross 	if (ossn->ssn_owner == SMBM_ANY_OWNER)
505613a2f6bSGordon Ross 		ossn->ssn_owner = realuid;
506613a2f6bSGordon Ross 	else {
5074bff34e3Sthurlow 		/*
508613a2f6bSGordon Ross 		 * Do we have the privilege to create with the
509613a2f6bSGordon Ross 		 * specified uid?  (does uid == cr->cr_uid, etc.)
5104bff34e3Sthurlow 		 */
511613a2f6bSGordon Ross 		if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
512613a2f6bSGordon Ross 			error = EPERM;
513613a2f6bSGordon Ross 			goto out;
5144bff34e3Sthurlow 		}
515613a2f6bSGordon Ross 		/* ossn->ssn_owner is OK */
5164bff34e3Sthurlow 	}
5174bff34e3Sthurlow 
518613a2f6bSGordon Ross 	/*
519613a2f6bSGordon Ross 	 * Make sure the strings are null terminated.
520613a2f6bSGordon Ross 	 */
521613a2f6bSGordon Ross 	ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
522613a2f6bSGordon Ross 	ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
523613a2f6bSGordon Ross 	ossn->ssn_id.id_user[   SMBIOC_MAX_NAME-1] = '\0';
5244bff34e3Sthurlow 
525613a2f6bSGordon Ross 	if (cmd == SMBIOC_SSN_CREATE)
526613a2f6bSGordon Ross 		ossn->ssn_vopt |= SMBVOPT_CREATE;
527613a2f6bSGordon Ross 	else /* FIND */
528613a2f6bSGordon Ross 		ossn->ssn_vopt &= ~SMBVOPT_CREATE;
5294bff34e3Sthurlow 
530613a2f6bSGordon Ross 	error = smb_vc_findcreate(ossn, &scred, &vcp);
5314bff34e3Sthurlow 	if (error)
5324bff34e3Sthurlow 		goto out;
533613a2f6bSGordon Ross 	ASSERT(vcp != NULL);
5344bff34e3Sthurlow 
5354bff34e3Sthurlow 	/*
536613a2f6bSGordon Ross 	 * We have a VC, held, but not locked.
537613a2f6bSGordon Ross 	 * If we're creating, mark this instance as
538613a2f6bSGordon Ross 	 * an open from IOD so close can do cleanup.
539613a2f6bSGordon Ross 	 *
540613a2f6bSGordon Ross 	 * XXX: Would be nice to have a back pointer
541613a2f6bSGordon Ross 	 * from the VC to this (IOD) sdp instance.
5424bff34e3Sthurlow 	 */
543613a2f6bSGordon Ross 	if (cmd == SMBIOC_SSN_CREATE) {
544613a2f6bSGordon Ross 		if (vcp->iod_thr != NULL) {
545613a2f6bSGordon Ross 			error = EEXIST;
546613a2f6bSGordon Ross 			goto out;
547613a2f6bSGordon Ross 		}
548613a2f6bSGordon Ross 		sdp->sd_flags |= NSMBFL_IOD;
549613a2f6bSGordon Ross 	} else {
550613a2f6bSGordon Ross 		/*
551613a2f6bSGordon Ross 		 * Wait for it to finish connecting
552613a2f6bSGordon Ross 		 * (or reconnect) if necessary.
553613a2f6bSGordon Ross 		 */
554613a2f6bSGordon Ross 		if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
555613a2f6bSGordon Ross 			error = smb_iod_reconnect(vcp);
556613a2f6bSGordon Ross 			if (error != 0)
557613a2f6bSGordon Ross 				goto out;
558613a2f6bSGordon Ross 		}
559613a2f6bSGordon Ross 	}
560613a2f6bSGordon Ross 
561613a2f6bSGordon Ross 	/*
562613a2f6bSGordon Ross 	 * The VC has a hold from _findvc
563613a2f6bSGordon Ross 	 * which we keep until _SSN_RELE
564613a2f6bSGordon Ross 	 * or nsmb_close().
565613a2f6bSGordon Ross 	 */
566613a2f6bSGordon Ross 	sdp->sd_level = SMBL_VC;
567613a2f6bSGordon Ross 	sdp->sd_vc = vcp;
568613a2f6bSGordon Ross 	vcp = NULL;
569613a2f6bSGordon Ross 	(void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
5704bff34e3Sthurlow 
5714bff34e3Sthurlow out:
572613a2f6bSGordon Ross 	if (vcp) {
573613a2f6bSGordon Ross 		/* Error path: rele hold from _findcreate */
574613a2f6bSGordon Ross 		smb_vc_rele(vcp);
575613a2f6bSGordon Ross 	}
576430b4c46SGordon Ross 	kmem_free(ossn, sizeof (*ossn));
577613a2f6bSGordon Ross 	smb_credrele(&scred);
578613a2f6bSGordon Ross 
5794bff34e3Sthurlow 	return (error);
5804bff34e3Sthurlow }
5814bff34e3Sthurlow 
582613a2f6bSGordon Ross /*
583613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
584613a2f6bSGordon Ross  * Release or kill the current session.
585613a2f6bSGordon Ross  */
5864bff34e3Sthurlow int
smb_usr_drop_ssn(smb_dev_t * sdp,int cmd)587613a2f6bSGordon Ross smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
5884bff34e3Sthurlow {
589613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
5904bff34e3Sthurlow 
591613a2f6bSGordon Ross 	/* Must have a VC. */
592613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
593613a2f6bSGordon Ross 		return (ENOTCONN);
5944bff34e3Sthurlow 
595613a2f6bSGordon Ross 	/* If we have a share ref, drop it too. */
596613a2f6bSGordon Ross 	if (sdp->sd_share) {
597613a2f6bSGordon Ross 		smb_share_rele(sdp->sd_share);
598613a2f6bSGordon Ross 		sdp->sd_share = NULL;
599613a2f6bSGordon Ross 		sdp->sd_level = SMBL_VC;
6004bff34e3Sthurlow 	}
6014bff34e3Sthurlow 
602613a2f6bSGordon Ross 	if (cmd == SMBIOC_SSN_KILL)
603613a2f6bSGordon Ross 		smb_vc_kill(vcp);
6044bff34e3Sthurlow 
605613a2f6bSGordon Ross 	/* Drop the VC ref. */
606613a2f6bSGordon Ross 	smb_vc_rele(vcp);
607613a2f6bSGordon Ross 	sdp->sd_vc = NULL;
608613a2f6bSGordon Ross 	sdp->sd_level = 0;
609613a2f6bSGordon Ross 
610613a2f6bSGordon Ross 	return (0);
6114bff34e3Sthurlow }
6124bff34e3Sthurlow 
6134bff34e3Sthurlow /*
614613a2f6bSGordon Ross  * Find or create a tree (connected share)
6154bff34e3Sthurlow  */
6164bff34e3Sthurlow int
smb_usr_get_tree(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)617613a2f6bSGordon Ross smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
6184bff34e3Sthurlow {
619613a2f6bSGordon Ross 	struct smb_cred scred;
620613a2f6bSGordon Ross 	smbioc_tcon_t *tcon = NULL;
621613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
622613a2f6bSGordon Ross 	struct smb_share *ssp = NULL;
623613a2f6bSGordon Ross 	int error = 0;
6244bff34e3Sthurlow 
625613a2f6bSGordon Ross 	/* Must have a VC. */
626613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
627613a2f6bSGordon Ross 		return (ENOTCONN);
628613a2f6bSGordon Ross 	/* Should not have a share. */
629613a2f6bSGordon Ross 	if (sdp->sd_share != NULL)
630613a2f6bSGordon Ross 		return (EISCONN);
631613a2f6bSGordon Ross 
632613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
633613a2f6bSGordon Ross 	tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
634613a2f6bSGordon Ross 	if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
635613a2f6bSGordon Ross 		error = EFAULT;
636613a2f6bSGordon Ross 		goto out;
6374bff34e3Sthurlow 	}
638613a2f6bSGordon Ross 
639613a2f6bSGordon Ross 	/*
640613a2f6bSGordon Ross 	 * Make sure the strings are null terminated.
641613a2f6bSGordon Ross 	 */
642613a2f6bSGordon Ross 	tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
643613a2f6bSGordon Ross 	tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
644613a2f6bSGordon Ross 
645613a2f6bSGordon Ross 	if (cmd == SMBIOC_TREE_CONNECT)
646613a2f6bSGordon Ross 		tcon->tc_opt |= SMBSOPT_CREATE;
647613a2f6bSGordon Ross 	else /* FIND */
648613a2f6bSGordon Ross 		tcon->tc_opt &= ~SMBSOPT_CREATE;
649613a2f6bSGordon Ross 
650613a2f6bSGordon Ross 	error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
6514bff34e3Sthurlow 	if (error)
652613a2f6bSGordon Ross 		goto out;
653613a2f6bSGordon Ross 	ASSERT(ssp != NULL);
654613a2f6bSGordon Ross 
655613a2f6bSGordon Ross 	/*
656613a2f6bSGordon Ross 	 * We have a share, held, but not locked.
657613a2f6bSGordon Ross 	 * If we're creating, do tree connect now,
658613a2f6bSGordon Ross 	 * otherwise let that wait for a request.
659613a2f6bSGordon Ross 	 */
660613a2f6bSGordon Ross 	if (cmd == SMBIOC_TREE_CONNECT) {
661613a2f6bSGordon Ross 		error = smb_share_tcon(ssp, &scred);
662613a2f6bSGordon Ross 		if (error)
663613a2f6bSGordon Ross 			goto out;
6644bff34e3Sthurlow 	}
665613a2f6bSGordon Ross 
666613a2f6bSGordon Ross 	/*
667613a2f6bSGordon Ross 	 * Give caller the real share type from
668613a2f6bSGordon Ross 	 * the tree connect response, so they can
669613a2f6bSGordon Ross 	 * see if they got the requested type.
670613a2f6bSGordon Ross 	 */
671430b4c46SGordon Ross 	tcon->tc_sh.sh_type = ssp->ss_type;
672613a2f6bSGordon Ross 
673613a2f6bSGordon Ross 	/*
674613a2f6bSGordon Ross 	 * The share has a hold from _tcon
675613a2f6bSGordon Ross 	 * which we keep until nsmb_close()
676613a2f6bSGordon Ross 	 * or the SMBIOC_TDIS below.
677613a2f6bSGordon Ross 	 */
678613a2f6bSGordon Ross 	sdp->sd_level = SMBL_SHARE;
679613a2f6bSGordon Ross 	sdp->sd_share = ssp;
680613a2f6bSGordon Ross 	ssp = NULL;
681613a2f6bSGordon Ross 	(void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
682613a2f6bSGordon Ross 
683613a2f6bSGordon Ross out:
684613a2f6bSGordon Ross 	if (ssp) {
685613a2f6bSGordon Ross 		/* Error path: rele hold from _findcreate */
686613a2f6bSGordon Ross 		smb_share_rele(ssp);
6874bff34e3Sthurlow 	}
688430b4c46SGordon Ross 	/*
689430b4c46SGordon Ross 	 * This structure may contain a
690430b4c46SGordon Ross 	 * cleartext password, so zap it.
691430b4c46SGordon Ross 	 */
692430b4c46SGordon Ross 	bzero(tcon, sizeof (*tcon));
693430b4c46SGordon Ross 	kmem_free(tcon, sizeof (*tcon));
694613a2f6bSGordon Ross 	smb_credrele(&scred);
6954bff34e3Sthurlow 
696613a2f6bSGordon Ross 	return (error);
6974bff34e3Sthurlow }
6984bff34e3Sthurlow 
699613a2f6bSGordon Ross /*
700613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
701613a2f6bSGordon Ross  * Release or kill the current tree
702613a2f6bSGordon Ross  */
703613a2f6bSGordon Ross int
smb_usr_drop_tree(smb_dev_t * sdp,int cmd)704613a2f6bSGordon Ross smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
7054bff34e3Sthurlow {
706613a2f6bSGordon Ross 	struct smb_share *ssp = NULL;
7074bff34e3Sthurlow 
708613a2f6bSGordon Ross 	/* Must have a VC and a share. */
709613a2f6bSGordon Ross 	if (sdp->sd_vc == NULL)
710613a2f6bSGordon Ross 		return (ENOTCONN);
711613a2f6bSGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
712613a2f6bSGordon Ross 		return (ENOTCONN);
713613a2f6bSGordon Ross 
714613a2f6bSGordon Ross 	if (cmd == SMBIOC_TREE_KILL)
715613a2f6bSGordon Ross 		smb_share_kill(ssp);
716613a2f6bSGordon Ross 
717613a2f6bSGordon Ross 	/* Drop the share ref. */
718613a2f6bSGordon Ross 	smb_share_rele(sdp->sd_share);
719613a2f6bSGordon Ross 	sdp->sd_share = NULL;
720613a2f6bSGordon Ross 	sdp->sd_level = SMBL_VC;
721613a2f6bSGordon Ross 
722613a2f6bSGordon Ross 	return (0);
7234bff34e3Sthurlow }
7244bff34e3Sthurlow 
725613a2f6bSGordon Ross /*
72640c0e231SGordon Ross  * Ioctl handler for all SMBIOC_IOD_...
727613a2f6bSGordon Ross  */
7284bff34e3Sthurlow int
smb_usr_iod_ioctl(smb_dev_t * sdp,int cmd,intptr_t arg,int flags,cred_t * cr)72940c0e231SGordon Ross smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
7304bff34e3Sthurlow {
73140c0e231SGordon Ross 	struct smb_vc *vcp;
732613a2f6bSGordon Ross 	int err = 0;
7334bff34e3Sthurlow 
73440c0e231SGordon Ross 	/* Must be the IOD. */
73540c0e231SGordon Ross 	if ((sdp->sd_flags & NSMBFL_IOD) == 0)
73640c0e231SGordon Ross 		return (EINVAL);
73740c0e231SGordon Ross 	/* Must have a VC and no share. */
738613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
7394bff34e3Sthurlow 		return (EINVAL);
74040c0e231SGordon Ross 	if (sdp->sd_share != NULL)
741613a2f6bSGordon Ross 		return (EINVAL);
742613a2f6bSGordon Ross 
743613a2f6bSGordon Ross 	/*
744613a2f6bSGordon Ross 	 * Is there already an IOD for this VC?
745613a2f6bSGordon Ross 	 * (Should never happen.)
746613a2f6bSGordon Ross 	 */
747613a2f6bSGordon Ross 	SMB_VC_LOCK(vcp);
748613a2f6bSGordon Ross 	if (vcp->iod_thr == NULL)
749613a2f6bSGordon Ross 		vcp->iod_thr = curthread;
750613a2f6bSGordon Ross 	else
751613a2f6bSGordon Ross 		err = EEXIST;
752613a2f6bSGordon Ross 	SMB_VC_UNLOCK(vcp);
753613a2f6bSGordon Ross 	if (err)
754613a2f6bSGordon Ross 		return (err);
755613a2f6bSGordon Ross 
756613a2f6bSGordon Ross 	/*
75740c0e231SGordon Ross 	 * Copy the "work" state, etc. into the VC,
75840c0e231SGordon Ross 	 * and back to the caller on the way out.
75940c0e231SGordon Ross 	 * Clear the "out only" part.
760613a2f6bSGordon Ross 	 */
761613a2f6bSGordon Ross 	if (ddi_copyin((void *)arg, &vcp->vc_work,
762613a2f6bSGordon Ross 	    sizeof (smbioc_ssn_work_t), flags)) {
763613a2f6bSGordon Ross 		err = EFAULT;
764613a2f6bSGordon Ross 		goto out;
7654bff34e3Sthurlow 	}
76640c0e231SGordon Ross 	vcp->vc_work.wk_out_state = 0;
767613a2f6bSGordon Ross 
76840c0e231SGordon Ross 	switch (cmd) {
769613a2f6bSGordon Ross 
77040c0e231SGordon Ross 	case SMBIOC_IOD_CONNECT:
771adee6784SGordon Ross 		err = nsmb_iod_connect(vcp, cr);
77240c0e231SGordon Ross 		break;
773613a2f6bSGordon Ross 
77440c0e231SGordon Ross 	case SMBIOC_IOD_NEGOTIATE:
77540c0e231SGordon Ross 		err = nsmb_iod_negotiate(vcp, cr);
77640c0e231SGordon Ross 		break;
777613a2f6bSGordon Ross 
77840c0e231SGordon Ross 	case SMBIOC_IOD_SSNSETUP:
77940c0e231SGordon Ross 		err = nsmb_iod_ssnsetup(vcp, cr);
78040c0e231SGordon Ross 		break;
78140c0e231SGordon Ross 
78240c0e231SGordon Ross 	case SMBIOC_IOD_WORK:
78340c0e231SGordon Ross 		err = smb_iod_vc_work(vcp, flags, cr);
78440c0e231SGordon Ross 		break;
78540c0e231SGordon Ross 
78640c0e231SGordon Ross 	case SMBIOC_IOD_IDLE:
78740c0e231SGordon Ross 		err = smb_iod_vc_idle(vcp);
78840c0e231SGordon Ross 		break;
78940c0e231