xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c (revision 656d76456fa4392118fe5f8dcb500fd7c1637d84)
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_dev.c,v 1.21 2004/12/13 00:25:18 lindak Exp $
33  */
34 
35 /*
36  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37  * Use is subject to license terms.
38  */
39 
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/errno.h>
43 #include <sys/sysmacros.h>
44 #include <sys/uio.h>
45 #include <sys/buf.h>
46 #include <sys/modctl.h>
47 #include <sys/open.h>
48 #include <sys/file.h>
49 #include <sys/kmem.h>
50 #include <sys/conf.h>
51 #include <sys/cmn_err.h>
52 #include <sys/stat.h>
53 #include <sys/ddi.h>
54 #include <sys/sunddi.h>
55 #include <sys/sunldi.h>
56 #include <sys/policy.h>
57 #include <sys/zone.h>
58 #include <sys/pathname.h>
59 #include <sys/mount.h>
60 #include <sys/sdt.h>
61 #include <fs/fs_subr.h>
62 #include <sys/modctl.h>
63 #include <sys/devops.h>
64 #include <sys/thread.h>
65 #include <sys/mkdev.h>
66 #include <sys/types.h>
67 #include <sys/zone.h>
68 
69 #ifdef APPLE
70 #include <sys/smb_apple.h>
71 #else
72 #include <netsmb/smb_osdep.h>
73 #endif
74 
75 #include <netsmb/mchain.h>		/* for "htoles()" */
76 
77 #include <netsmb/smb.h>
78 #include <netsmb/smb_conn.h>
79 #include <netsmb/smb_subr.h>
80 #include <netsmb/smb_dev.h>
81 #include <netsmb/smb_pass.h>
82 
83 /* for version checks */
84 const uint32_t nsmb_version = NSMB_VERSION;
85 
86 /*
87  * Userland code loops through minor #s 0 to 1023, looking for one which opens.
88  * Intially we create minor 0 and leave it for anyone.  Minor zero will never
89  * actually get used - opening triggers creation of another (but private) minor,
90  * which userland code will get to and mark busy.
91  */
92 #define	SMBMINORS 1024
93 static void *statep;
94 static major_t nsmb_major;
95 static minor_t nsmb_minor = 1;
96 
97 #define	NSMB_MAX_MINOR  (1 << 8)
98 #define	NSMB_MIN_MINOR   (NSMB_MAX_MINOR + 1)
99 
100 #define	ILP32	1
101 #define	LP64	2
102 
103 static kmutex_t  dev_lck;
104 
105 /* Zone support */
106 zone_key_t nsmb_zone_key;
107 extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
108 extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
109 
110 /*
111  * cb_ops device operations.
112  */
113 static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
114 static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
115 static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
116 				cred_t *credp, int *rvalp);
117 /* smbfs cb_ops */
118 static struct cb_ops nsmb_cbops = {
119 	nsmb_open,	/* open */
120 	nsmb_close,	/* close */
121 	nodev,		/* strategy */
122 	nodev,		/* print */
123 	nodev,		/* dump */
124 	nodev,		/* read */
125 	nodev,		/* write */
126 	nsmb_ioctl,	/* ioctl */
127 	nodev,		/* devmap */
128 	nodev,		/* mmap */
129 	nodev,		/* segmap */
130 	nochpoll,	/* poll */
131 	ddi_prop_op,	/* prop_op */
132 	NULL,		/* stream */
133 	D_MP,		/* cb_flag */
134 	CB_REV,		/* rev */
135 	nodev,		/* int (*cb_aread)() */
136 	nodev		/* int (*cb_awrite)() */
137 };
138 
139 /*
140  * Device options
141  */
142 static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
143 static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
144 static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
145 	void *arg, void **result);
146 
147 static struct dev_ops nsmb_ops = {
148 	DEVO_REV,	/* devo_rev, */
149 	0,		/* refcnt  */
150 	nsmb_getinfo,	/* info */
151 	nulldev,	/* identify */
152 	nulldev,	/* probe */
153 	nsmb_attach,	/* attach */
154 	nsmb_detach,	/* detach */
155 	nodev,		/* reset */
156 	&nsmb_cbops,	/* driver ops - devctl interfaces */
157 	NULL,		/* bus operations */
158 	NULL		/* power */
159 };
160 
161 /*
162  * Module linkage information.
163  */
164 
165 static struct modldrv nsmb_modldrv = {
166 	&mod_driverops,				/* Driver module */
167 	"SMBFS network driver v" NSMB_VER_STR,
168 	&nsmb_ops				/* Driver ops */
169 };
170 
171 static struct modlinkage nsmb_modlinkage = {
172 	MODREV_1,
173 	(void *)&nsmb_modldrv,
174 	NULL
175 };
176 
177 int
178 _init(void)
179 {
180 	int error;
181 
182 	ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
183 
184 	/* Can initialize some mutexes also. */
185 	mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
186 	/*
187 	 * Create a major name and number.
188 	 */
189 	nsmb_major = ddi_name_to_major(NSMB_NAME);
190 	nsmb_minor = 0;
191 
192 	/* Connection data structures. */
193 	(void) smb_sm_init();
194 
195 	/* Initialize password Key chain DB. */
196 	smb_pkey_init();
197 
198 	zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
199 	    nsmb_zone_destroy);
200 
201 	/*
202 	 * Install the module.  Do this after other init,
203 	 * to prevent entrances before we're ready.
204 	 */
205 	if ((error = mod_install((&nsmb_modlinkage))) != 0) {
206 
207 		/* Same as 2nd half of _fini */
208 		(void) zone_key_delete(nsmb_zone_key);
209 		smb_pkey_fini();
210 		smb_sm_done();
211 		mutex_destroy(&dev_lck);
212 		ddi_soft_state_fini(&statep);
213 
214 		return (error);
215 	}
216 
217 	return (0);
218 }
219 
220 int
221 _fini(void)
222 {
223 	int status;
224 
225 	/*
226 	 * Prevent unload if we have active VCs
227 	 * or stored passwords
228 	 */
229 	if ((status = smb_sm_idle()) != 0)
230 		return (status);
231 	if ((status = smb_pkey_idle()) != 0)
232 		return (status);
233 
234 	/*
235 	 * Remove the module.  Do this before destroying things,
236 	 * to prevent new entrances while we're destorying.
237 	 */
238 	if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
239 		return (status);
240 	}
241 
242 	(void) zone_key_delete(nsmb_zone_key);
243 
244 	/* Destroy password Key chain DB. */
245 	smb_pkey_fini();
246 
247 	smb_sm_done();
248 
249 	mutex_destroy(&dev_lck);
250 	ddi_soft_state_fini(&statep);
251 
252 	return (status);
253 }
254 
255 int
256 _info(struct modinfo *modinfop)
257 {
258 	return (mod_info(&nsmb_modlinkage, modinfop));
259 }
260 
261 /*ARGSUSED*/
262 static int
263 nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
264 {
265 	int ret = DDI_SUCCESS;
266 
267 	switch (cmd) {
268 	case DDI_INFO_DEVT2DEVINFO:
269 		*result = 0;
270 		break;
271 	case DDI_INFO_DEVT2INSTANCE:
272 		*result = 0;
273 		break;
274 	default:
275 		ret = DDI_FAILURE;
276 	}
277 	return (ret);
278 }
279 
280 static int
281 nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
282 {
283 	smb_dev_t *sdp;
284 
285 	if (cmd != DDI_ATTACH)
286 		return (DDI_FAILURE);
287 	/*
288 	 * only one instance - but we clone using the open routine
289 	 */
290 	if (ddi_get_instance(dip) > 0)
291 		return (DDI_FAILURE);
292 
293 	mutex_enter(&dev_lck);
294 
295 	/*
296 	 * This is the Zero'th minor device which is created.
297 	 */
298 	if (ddi_soft_state_zalloc(statep, 0) == DDI_FAILURE) {
299 		cmn_err(CE_WARN, "nsmb_attach: soft state alloc");
300 		goto attach_failed;
301 	}
302 	if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
303 	    NULL) == DDI_FAILURE) {
304 		cmn_err(CE_WARN, "nsmb_attach: create minor");
305 		goto attach_failed;
306 	}
307 	if ((sdp = ddi_get_soft_state(statep, 0)) == NULL) {
308 		cmn_err(CE_WARN, "nsmb_attach: get soft state");
309 		ddi_remove_minor_node(dip, NULL);
310 		goto attach_failed;
311 	}
312 
313 	/*
314 	 * Need to see if this field is required.
315 	 * REVISIT
316 	 */
317 	sdp->smb_dip = dip;
318 	sdp->sd_seq = 0;
319 	sdp->sd_opened = 1;
320 
321 	mutex_exit(&dev_lck);
322 	ddi_report_dev(dip);
323 	return (DDI_SUCCESS);
324 
325 attach_failed:
326 	ddi_soft_state_free(statep, 0);
327 	mutex_exit(&dev_lck);
328 	return (DDI_FAILURE);
329 }
330 
331 /*ARGSUSED*/
332 static int
333 nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
334 {
335 
336 	if (cmd != DDI_DETACH)
337 		return (DDI_FAILURE);
338 	if (ddi_get_instance(dip) > 0)
339 		return (DDI_FAILURE);
340 
341 	ddi_soft_state_free(statep, 0);
342 	ddi_remove_minor_node(dip, NULL);
343 
344 	return (DDI_SUCCESS);
345 }
346 
347 /*ARGSUSED*/
348 static int
349 nsmb_ioctl(dev_t dev,
350 	    int cmd,
351 	    intptr_t arg,
352 	    int mode,
353 	    cred_t *credp,
354 	    int *rvalp)
355 {
356 	smb_dev_t *sdp;
357 	struct smb_vc *vcp = NULL;
358 	struct smb_share *ssp = NULL;
359 	struct smb_cred scred;
360 	int err, error;
361 	uid_t uid;
362 
363 	/* Free any+all of these at end of switch. */
364 	smbioc_lookup_t *sioc = NULL;
365 	smbioc_rq_t *srq = NULL;
366 	smbioc_rw_t *rwrq = NULL;
367 	smbioc_t2rq_t *strq = NULL;
368 	smbioc_pk_t  *pk = NULL;
369 
370 	sdp = ddi_get_soft_state(statep, getminor(dev));
371 	if (sdp == NULL) {
372 		return (DDI_FAILURE);
373 	}
374 	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
375 		return (EBADF);
376 	}
377 
378 	/*
379 	 * Dont give access if the zone id is not as the same as we
380 	 * set in the nsmb_open or dont belong to the global zone.
381 	 * Check if the user belongs to this zone..
382 	 */
383 	if (sdp->zoneid != getzoneid())
384 		return (EIO);
385 	if (cmd != SMBIOC_TDIS &&
386 	    zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
387 		return (EIO);
388 
389 
390 	error = 0;
391 	smb_credinit(&scred, curproc, credp);
392 	switch (cmd) {
393 		case SMBIOC_GETVERS:
394 			ddi_copyout(&nsmb_version, (void *)arg,
395 			    sizeof (nsmb_version), mode);
396 			break;
397 
398 		case SMBIOC_REQUEST:
399 			if (sdp->sd_share == NULL) {
400 				error = ENOTCONN;
401 				break;
402 			}
403 			srq = kmem_alloc(sizeof (*srq), KM_SLEEP);
404 			if (ddi_copyin((void *) arg, srq,
405 			    sizeof (*srq), mode)) {
406 				error = EFAULT;
407 				break;
408 			}
409 			error = smb_usr_simplerequest(sdp->sd_share,
410 			    srq, &scred);
411 			ddi_copyout(srq, (void *)arg,
412 			    SMBIOC_RQ_COPYOUT_SIZE, mode);
413 			break;
414 
415 		case SMBIOC_T2RQ:
416 			if (sdp->sd_share == NULL) {
417 				error = ENOTCONN;
418 				break;
419 			}
420 			strq = kmem_alloc(sizeof (*strq), KM_SLEEP);
421 			if (ddi_copyin((void *)arg, strq,
422 			    sizeof (*strq), mode)) {
423 				error = EFAULT;
424 				break;
425 			}
426 			error = smb_usr_t2request(sdp->sd_share, strq, &scred);
427 			ddi_copyout(strq, (void *)arg,
428 			    SMBIOC_T2RQ_COPYOUT_SIZE, mode);
429 			break;
430 
431 		case SMBIOC_READ:
432 		case SMBIOC_WRITE:
433 			if ((ssp = sdp->sd_share) == NULL) {
434 				error = ENOTCONN;
435 				break;
436 			}
437 			rwrq = kmem_alloc(sizeof (*rwrq), KM_SLEEP);
438 			if (ddi_copyin((void *)arg, rwrq,
439 			    sizeof (*rwrq), mode)) {
440 				error = EFAULT;
441 				break;
442 			}
443 			error = smb_usr_rw(ssp, rwrq, cmd, &scred);
444 			ddi_copyout(rwrq, (void *)arg,
445 			    SMBIOC_RW_COPYOUT_SIZE, mode);
446 			break;
447 
448 		case SMBIOC_FINDVC:
449 			/* Should be no VC and no share */
450 			if (sdp->sd_vc || sdp->sd_share) {
451 				error = EISCONN;
452 				break;
453 			}
454 			sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
455 			if (ddi_copyin((void *)arg, sioc,
456 			    sizeof (*sioc), mode)) {
457 				error = EFAULT;
458 				break;
459 			}
460 			vcp = NULL;
461 			ssp = NULL;
462 			error = smb_usr_findvc(sioc, &scred, &vcp);
463 			if (error)
464 				break;
465 			if (vcp) {
466 				/*
467 				 * The VC has a hold from _findvc
468 				 * which we keep until nsmb_close().
469 				 */
470 				sdp->sd_level = SMBL_VC;
471 				sdp->sd_vc = vcp;
472 			}
473 			(void) ddi_copyout(sioc, (void *)arg,
474 			    SMBIOC_LOOK_COPYOUT_SIZE, mode);
475 
476 			break;
477 
478 		case SMBIOC_NEGOTIATE:
479 			/* Should be no VC (and no share) */
480 			if (sdp->sd_vc || sdp->sd_share) {
481 				error = EISCONN;
482 				break;
483 			}
484 			sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
485 			if (ddi_copyin((void *)arg, sioc,
486 			    sizeof (*sioc), mode)) {
487 				error = EFAULT;
488 				break;
489 			}
490 			vcp = NULL;
491 			ssp = NULL;
492 			error = smb_usr_negotiate(sioc, &scred, &vcp);
493 			if (error)
494 				break;
495 			if (vcp) {
496 				/*
497 				 * The VC has a hold from _negotiate
498 				 * which we keep until nsmb_close().
499 				 */
500 				sdp->sd_level = SMBL_VC;
501 				sdp->sd_vc = vcp;
502 				/*
503 				 * If we just created this VC, and
504 				 * this minor is doing the setup,
505 				 * keep track of that fact here.
506 				 */
507 				if (vcp->vc_state < SMBIOD_ST_VCACTIVE)
508 					sdp->sd_flags |= NSMBFL_NEWVC;
509 
510 			}
511 			/*
512 			 * Copyout the "out token" (security blob).
513 			 *
514 			 * This code used to be near the end of
515 			 * smb_usr_negotiate().  Moved the copyout
516 			 * calls here so we know the "mode"
517 			 */
518 			if (vcp->vc_outtok) {
519 				/*
520 				 * Note: will copyout sioc below
521 				 * including sioc.vc_outtoklen,
522 				 * so we no longer put the length
523 				 * at the start of the outtok data.
524 				 */
525 				sioc->ioc_ssn.ioc_outtoklen =
526 				    vcp->vc_outtoklen;
527 				err = ddi_copyout(
528 				    vcp->vc_outtok,
529 				    sioc->ioc_ssn.ioc_outtok,
530 				    vcp->vc_outtoklen, mode);
531 				if (err) {
532 					error = EFAULT;
533 					break;
534 				}
535 				/*
536 				 * Save this blob in vc_negtok.
537 				 * We need it in case we have to
538 				 * reconnect.
539 				 *
540 				 * Set vc_negtok = vc_outtok
541 				 * but free vc_negtok first.
542 				 */
543 				if (vcp->vc_negtok) {
544 					kmem_free(
545 					    vcp->vc_negtok,
546 					    vcp->vc_negtoklen);
547 					vcp->vc_negtok = NULL;
548 					vcp->vc_negtoklen = 0;
549 				}
550 				vcp->vc_negtok    = vcp->vc_outtok;
551 				vcp->vc_negtoklen = vcp->vc_outtoklen;
552 				vcp->vc_outtok = NULL;
553 				vcp->vc_outtoklen = 0;
554 			}
555 			/*
556 			 * Added copyout here of (almost)
557 			 * the whole struct, even though
558 			 * the lib only needs _outtoklen.
559 			 * We may put other things in this
560 			 * struct that user-land needs.
561 			 */
562 			err = ddi_copyout(sioc, (void *)arg,
563 			    SMBIOC_LOOK_COPYOUT_SIZE, mode);
564 			if (err)
565 				error = EFAULT;
566 			break;
567 
568 		case SMBIOC_SSNSETUP:
569 			/* Must have a VC, but no share. */
570 			if (sdp->sd_share) {
571 				error = EISCONN;
572 				break;
573 			}
574 			if (!sdp->sd_vc) {
575 				error = ENOTCONN;
576 				break;
577 			}
578 			sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
579 			if (ddi_copyin((void *)arg, sioc,
580 			    sizeof (*sioc), mode)) {
581 				error = EFAULT;
582 				break;
583 			}
584 			vcp = sdp->sd_vc;
585 			ssp = NULL;
586 			error = smb_usr_ssnsetup(sioc, &scred, vcp);
587 			if (error)
588 				break;
589 			/*
590 			 * If this minor has finished ssn setup,
591 			 * turn off the NEWVC flag, otherwise we
592 			 * will kill this VC when we close.
593 			 */
594 			if (vcp->vc_state == SMBIOD_ST_VCACTIVE)
595 				sdp->sd_flags &= ~NSMBFL_NEWVC;
596 			/*
597 			 * Copyout the "out token" (security blob).
598 			 *
599 			 * This code used to be near the end of
600 			 * smb_usr_ssnsetup().  Moved the copyout
601 			 * calls here so we know the "mode"
602 			 */
603 			if (vcp->vc_outtok) {
604 				/*
605 				 * Note: will copyout sioc below
606 				 * including sioc.vc_outtoklen,
607 				 * so we no longer put the length
608 				 * at the start of the outtok data.
609 				 */
610 				sioc->ioc_ssn.ioc_outtoklen =
611 				    vcp->vc_outtoklen;
612 				err = ddi_copyout(
613 				    vcp->vc_outtok,
614 				    sioc->ioc_ssn.ioc_outtok,
615 				    vcp->vc_outtoklen, mode);
616 				if (err) {
617 					error = EFAULT;
618 					break;
619 				}
620 				/*
621 				 * Done with vc_outtok.  Similar,
622 				 * but NOT the same as after the
623 				 * smb_usr_negotiate call above.
624 				 */
625 				kmem_free(
626 				    vcp->vc_outtok,
627 				    vcp->vc_outtoklen);
628 				vcp->vc_outtok = NULL;
629 				vcp->vc_outtoklen = 0;
630 			}
631 			/* Added copyout here... (see above) */
632 			err = ddi_copyout(sioc, (void *)arg,
633 			    SMBIOC_LOOK_COPYOUT_SIZE, mode);
634 			if (err)
635 				error = EFAULT;
636 			break;
637 
638 		case SMBIOC_TCON:
639 			/* Must have a VC, but no share. */
640 			if (sdp->sd_share) {
641 				error = EISCONN;
642 				break;
643 			}
644 			if (!sdp->sd_vc) {
645 				error = ENOTCONN;
646 				break;
647 			}
648 			sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
649 			if (ddi_copyin((void *)arg, sioc,
650 			    sizeof (*sioc), mode)) {
651 				error = EFAULT;
652 				break;
653 			}
654 			vcp = sdp->sd_vc;
655 			ssp = NULL;
656 			error = smb_usr_tcon(sioc, &scred, vcp, &ssp);
657 			if (error)
658 				break;
659 			if (ssp) {
660 				/*
661 				 * The share has a hold from _tcon
662 				 * which we keep until nsmb_close()
663 				 * or the SMBIOC_TDIS below.
664 				 */
665 				sdp->sd_share = ssp;
666 				sdp->sd_level = SMBL_SHARE;
667 			}
668 			/* No need for copyout here. */
669 			break;
670 
671 		case SMBIOC_TDIS:
672 			if (sdp->sd_share == NULL) {
673 				error = ENOTCONN;
674 				break;
675 			}
676 			smb_share_rele(sdp->sd_share);
677 			sdp->sd_share = NULL;
678 			sdp->sd_level = SMBL_VC;
679 			break;
680 		case SMBIOC_FLAGS2:
681 			if (sdp->sd_share == NULL) {
682 				error = ENOTCONN;
683 				break;
684 			}
685 			if (!sdp->sd_vc) {
686 				error = ENOTCONN;
687 				break;
688 			}
689 			vcp = sdp->sd_vc;
690 			/*
691 			 * Return the flags2 value.
692 			 */
693 			ddi_copyout(&vcp->vc_hflags2, (void *)arg,
694 			    sizeof (u_int16_t), mode);
695 			break;
696 
697 		case SMBIOC_PK_ADD:
698 			pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
699 			if (ddi_copyin((void *)arg, pk,
700 			    sizeof (*pk), mode)) {
701 				error = EFAULT;
702 				break;
703 			}
704 			error = smb_pkey_add(pk, credp);
705 			break;
706 
707 		case SMBIOC_PK_DEL:
708 			pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
709 			if (ddi_copyin((void *)arg, pk,
710 			    sizeof (*pk), mode)) {
711 				error = EFAULT;
712 				break;
713 			}
714 			error = smb_pkey_del(pk, credp);
715 			break;
716 
717 		case SMBIOC_PK_CHK:
718 			pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
719 			if (ddi_copyin((void *)arg, pk,
720 			    sizeof (*pk), mode)) {
721 				error = EFAULT;
722 				break;
723 			}
724 			error = smb_pkey_check(pk, credp);
725 			/*
726 			 * Note: Intentionally DO NOT copyout
727 			 * the pasword here.  It can only be
728 			 * retrieved by internal calls.  This
729 			 * ioctl only tells the caller if the
730 			 * keychain entry exists.
731 			 */
732 			break;
733 
734 		case SMBIOC_PK_DEL_OWNER:
735 			uid = crgetruid(credp);
736 			error = smb_pkey_deluid(uid, credp);
737 			break;
738 
739 		case SMBIOC_PK_DEL_EVERYONE:
740 			uid = (uid_t)-1;
741 			error = smb_pkey_deluid(uid, credp);
742 			break;
743 
744 		default:
745 			error = ENODEV;
746 	}
747 
748 	/*
749 	 * Let's just do all the kmem_free stuff HERE,
750 	 * instead of at every switch break.
751 	 */
752 
753 	/* SMBIOC_REQUEST */
754 	if (srq)
755 		kmem_free(srq, sizeof (*srq));
756 
757 	/* SMBIOC_T2RQ */
758 	if (strq)
759 		kmem_free(strq, sizeof (*strq));
760 
761 	/* SMBIOC_READ */
762 	/* SMBIOC_WRITE */
763 	if (rwrq)
764 		kmem_free(rwrq, sizeof (*rwrq));
765 
766 	/* SMBIOC_FINDVC */
767 	/* SMBIOC_NEGOTIATE */
768 	/* SMBIOC_SSNSETUP */
769 	/* SMBIOC_TCON */
770 	if (sioc) {
771 		/*
772 		 * This data structure may contain
773 		 * cleartext passwords, so zap it.
774 		 */
775 		bzero(sioc, sizeof (*sioc));
776 		kmem_free(sioc, sizeof (*sioc));
777 	}
778 
779 	/* SMBIOC_PK_... */
780 	if (pk) {
781 		/*
782 		 * This data structure may contain
783 		 * cleartext passwords, so zap it.
784 		 */
785 		bzero(pk, sizeof (*pk));
786 		kmem_free(pk, sizeof (*pk));
787 	}
788 
789 	smb_credrele(&scred);
790 
791 	return (error);
792 }
793 
794 /*ARGSUSED*/
795 static int
796 nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
797 {
798 	major_t new_major;
799 	smb_dev_t *sdp, *sdv;
800 
801 	mutex_enter(&dev_lck);
802 	for (; ; ) {
803 		minor_t start = nsmb_minor;
804 		do {
805 			if (nsmb_minor >= MAXMIN32) {
806 				if (nsmb_major == getmajor(*dev))
807 					nsmb_minor = NSMB_MIN_MINOR;
808 				else
809 					nsmb_minor = 0;
810 			} else {
811 				nsmb_minor++;
812 			}
813 			sdv = ddi_get_soft_state(statep, nsmb_minor);
814 		} while ((sdv != NULL) && (nsmb_minor != start));
815 		if (nsmb_minor == start) {
816 			/*
817 			 * The condition we need to solve here is  all the
818 			 * MAXMIN32(~262000) minors numbers are reached. We
819 			 * need to create a new major number.
820 			 * zfs uses getudev() to create a new major number.
821 			 */
822 			if ((new_major = getudev()) == (major_t)-1) {
823 				cmn_err(CE_WARN,
824 				    "nsmb: Can't get unique major "
825 				    "device number.");
826 				mutex_exit(&dev_lck);
827 				return (-1);
828 			}
829 			nsmb_major = new_major;
830 			nsmb_minor = 0;
831 		} else {
832 			break;
833 		}
834 	}
835 
836 	/*
837 	 * This is called by mount or open call.
838 	 * The open() routine is passed a pointer to a device number so
839 	 * that  the  driver  can  change the minor number. This allows
840 	 * drivers to dynamically  create minor instances of  the  dev-
841 	 * ice.  An  example of this might be a  pseudo-terminal driver
842 	 * that creates a new pseudo-terminal whenever it   is  opened.
843 	 * A driver that chooses the minor number dynamically, normally
844 	 * creates only one  minor  device  node  in   attach(9E)  with
845 	 * ddi_create_minor_node(9F) then changes the minor number com-
846 	 * ponent of *devp using makedevice(9F)  and  getmajor(9F)  The
847 	 * driver needs to keep track of available minor numbers inter-
848 	 * nally.
849 	 * Stuff the structure smb_dev.
850 	 * return.
851 	 */
852 
853 	if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) {
854 		mutex_exit(&dev_lck);
855 		return (ENXIO);
856 	}
857 	if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) {
858 		mutex_exit(&dev_lck);
859 		return (ENXIO);
860 	}
861 
862 	sdp->sd_opened = 1;
863 	sdp->sd_seq = nsmb_minor;
864 	sdp->smb_cred = cr;
865 	sdp->sd_flags |= NSMBFL_OPEN;
866 	sdp->zoneid = crgetzoneid(cr);
867 	mutex_exit(&dev_lck);
868 
869 	*dev = makedevice(nsmb_major, nsmb_minor);
870 
871 	return (0);
872 }
873 
874 /*ARGSUSED*/
875 static int
876 nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
877 {
878 	struct smb_vc *vcp;
879 	struct smb_share *ssp;
880 	struct smb_cred scred;
881 	minor_t inst = getminor(dev);
882 	smb_dev_t *sdp;
883 
884 	mutex_enter(&dev_lck);
885 	/*
886 	 * 1. Check the validity of the minor number.
887 	 * 2. Release any shares/vc associated  with the connection.
888 	 * 3. Can close the minor number.
889 	 * 4. Deallocate any resources allocated in open() call.
890 	 */
891 	smb_credinit(&scred, curproc, cr);
892 
893 	sdp = ddi_get_soft_state(statep, inst);
894 
895 	/*
896 	 * time to call ddi_get_soft_state()
897 	 */
898 	ssp = sdp->sd_share;
899 	if (ssp != NULL)
900 		smb_share_rele(ssp);
901 	vcp = sdp->sd_vc;
902 	if (vcp != NULL) {
903 		/*
904 		 * If this dev minor was doing session setup
905 		 * and failed to authenticate (or whatever)
906 		 * then we need to put the VC in a state that
907 		 * allows later commands to try again.
908 		 */
909 		if (sdp->sd_flags & NSMBFL_NEWVC)
910 			smb_iod_disconnect(vcp);
911 		smb_vc_rele(vcp);
912 	}
913 	smb_credrele(&scred);
914 
915 	/*
916 	 * Free the instance
917 	 */
918 	ddi_soft_state_free(statep, inst);
919 	mutex_exit(&dev_lck);
920 	return (0);
921 }
922 
923 int
924 smb_dev2share(int fd, struct smb_share **sspp)
925 {
926 	register vnode_t *vp;
927 	smb_dev_t *sdp;
928 	struct smb_share *ssp;
929 	dev_t dev;
930 	file_t *fp;
931 
932 	if ((fp = getf(fd)) == NULL)
933 		return (set_errno(EBADF));
934 	vp = fp->f_vnode;
935 	dev = vp->v_rdev;
936 	if (dev == NULL) {
937 		releasef(fd);
938 		return (EBADF);
939 	}
940 	sdp = ddi_get_soft_state(statep, getminor(dev));
941 	if (sdp == NULL) {
942 		releasef(fd);
943 		return (DDI_FAILURE);
944 	}
945 	ssp = sdp->sd_share;
946 	if (ssp == NULL) {
947 		releasef(fd);
948 		return (ENOTCONN);
949 	}
950 	/*
951 	 * The share is already locked and referenced by the TCON ioctl
952 	 * We NULL to hand off share to caller (mount)
953 	 * This allows further ioctls against connection, for instance
954 	 * another tree connect and mount, in the automounter case
955 	 *
956 	 * We're effectively giving our reference to the mount.
957 	 *
958 	 * XXX: I'm not sure I like this.  I'd rather see the ioctl
959 	 * caller do something explicit to give up this reference,
960 	 * (i.e. SMBIOC_TDIS above) and increment the hold here.
961 	 */
962 	sdp->sd_share = NULL;
963 	releasef(fd);
964 	*sspp = ssp;
965 	return (0);
966 }
967