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