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