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