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