xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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 2009 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 static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
118 
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 	ddi_quiesce_not_needed,	/* quiesce */
162 };
163 
164 /*
165  * Module linkage information.
166  */
167 
168 static struct modldrv nsmb_modldrv = {
169 	&mod_driverops,				/* Driver module */
170 	"SMBFS network driver",
171 	&nsmb_ops				/* Driver ops */
172 };
173 
174 static struct modlinkage nsmb_modlinkage = {
175 	MODREV_1,
176 	(void *)&nsmb_modldrv,
177 	NULL
178 };
179 
180 int
181 _init(void)
182 {
183 	int error;
184 
185 	ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
186 
187 	/* Can initialize some mutexes also. */
188 	mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
189 	/*
190 	 * Create a major name and number.
191 	 */
192 	nsmb_major = ddi_name_to_major(NSMB_NAME);
193 	nsmb_minor = 0;
194 
195 	/* Connection data structures. */
196 	(void) smb_sm_init();
197 
198 	/* Initialize password Key chain DB. */
199 	smb_pkey_init();
200 
201 	/* Initialize crypto mechanisms. */
202 	smb_crypto_mech_init();
203 
204 	zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
205 	    nsmb_zone_destroy);
206 
207 	/*
208 	 * Install the module.  Do this after other init,
209 	 * to prevent entrances before we're ready.
210 	 */
211 	if ((error = mod_install((&nsmb_modlinkage))) != 0) {
212 
213 		/* Same as 2nd half of _fini */
214 		(void) zone_key_delete(nsmb_zone_key);
215 		smb_pkey_fini();
216 		smb_sm_done();
217 		mutex_destroy(&dev_lck);
218 		ddi_soft_state_fini(&statep);
219 
220 		return (error);
221 	}
222 
223 	return (0);
224 }
225 
226 int
227 _fini(void)
228 {
229 	int status;
230 
231 	/*
232 	 * Prevent unload if we have active VCs
233 	 * or stored passwords
234 	 */
235 	if ((status = smb_sm_idle()) != 0)
236 		return (status);
237 	if ((status = smb_pkey_idle()) != 0)
238 		return (status);
239 
240 	/*
241 	 * Remove the module.  Do this before destroying things,
242 	 * to prevent new entrances while we're destorying.
243 	 */
244 	if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
245 		return (status);
246 	}
247 
248 	(void) zone_key_delete(nsmb_zone_key);
249 
250 	/* Destroy password Key chain DB. */
251 	smb_pkey_fini();
252 
253 	smb_sm_done();
254 
255 	mutex_destroy(&dev_lck);
256 	ddi_soft_state_fini(&statep);
257 
258 	return (status);
259 }
260 
261 int
262 _info(struct modinfo *modinfop)
263 {
264 	return (mod_info(&nsmb_modlinkage, modinfop));
265 }
266 
267 /*ARGSUSED*/
268 static int
269 nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
270 {
271 	int ret = DDI_SUCCESS;
272 
273 	switch (cmd) {
274 	case DDI_INFO_DEVT2DEVINFO:
275 		*result = 0;
276 		break;
277 	case DDI_INFO_DEVT2INSTANCE:
278 		*result = 0;
279 		break;
280 	default:
281 		ret = DDI_FAILURE;
282 	}
283 	return (ret);
284 }
285 
286 static int
287 nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
288 {
289 	smb_dev_t *sdp;
290 
291 	if (cmd != DDI_ATTACH)
292 		return (DDI_FAILURE);
293 	/*
294 	 * only one instance - but we clone using the open routine
295 	 */
296 	if (ddi_get_instance(dip) > 0)
297 		return (DDI_FAILURE);
298 
299 	mutex_enter(&dev_lck);
300 
301 	/*
302 	 * This is the Zero'th minor device which is created.
303 	 */
304 	if (ddi_soft_state_zalloc(statep, 0) == DDI_FAILURE) {
305 		cmn_err(CE_WARN, "nsmb_attach: soft state alloc");
306 		goto attach_failed;
307 	}
308 	if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
309 	    NULL) == DDI_FAILURE) {
310 		cmn_err(CE_WARN, "nsmb_attach: create minor");
311 		goto attach_failed;
312 	}
313 	if ((sdp = ddi_get_soft_state(statep, 0)) == NULL) {
314 		cmn_err(CE_WARN, "nsmb_attach: get soft state");
315 		ddi_remove_minor_node(dip, NULL);
316 		goto attach_failed;
317 	}
318 
319 	/*
320 	 * Need to see if this field is required.
321 	 * REVISIT
322 	 */
323 	sdp->smb_dip = dip;
324 	sdp->sd_seq = 0;
325 	sdp->sd_opened = 1;
326 
327 	mutex_exit(&dev_lck);
328 	ddi_report_dev(dip);
329 	return (DDI_SUCCESS);
330 
331 attach_failed:
332 	ddi_soft_state_free(statep, 0);
333 	mutex_exit(&dev_lck);
334 	return (DDI_FAILURE);
335 }
336 
337 /*ARGSUSED*/
338 static int
339 nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
340 {
341 
342 	if (cmd != DDI_DETACH)
343 		return (DDI_FAILURE);
344 	if (ddi_get_instance(dip) > 0)
345 		return (DDI_FAILURE);
346 
347 	ddi_soft_state_free(statep, 0);
348 	ddi_remove_minor_node(dip, NULL);
349 
350 	return (DDI_SUCCESS);
351 }
352 
353 /*ARGSUSED*/
354 static int
355 nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags,	/* model.h */
356 	cred_t *cr, int *rvalp)
357 {
358 	smb_dev_t *sdp;
359 	int err;
360 
361 	sdp = ddi_get_soft_state(statep, getminor(dev));
362 	if (sdp == NULL) {
363 		return (DDI_FAILURE);
364 	}
365 	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
366 		return (EBADF);
367 	}
368 
369 	/*
370 	 * Dont give access if the zone id is not as the same as we
371 	 * set in the nsmb_open or dont belong to the global zone.
372 	 * Check if the user belongs to this zone..
373 	 */
374 	if (sdp->zoneid != getzoneid())
375 		return (EIO);
376 
377 	/*
378 	 * We have a zone_shutdown call back that kills all the VCs
379 	 * in a zone that's shutting down.  That action will cause
380 	 * all of these ioctls to fail on such VCs, so no need to
381 	 * check the zone status here on every ioctl call.
382 	 */
383 
384 	err = 0;
385 	switch (cmd) {
386 	case SMBIOC_GETVERS:
387 		ddi_copyout(&nsmb_version, (void *)arg,
388 		    sizeof (nsmb_version), flags);
389 		break;
390 
391 	case SMBIOC_FLAGS2:
392 		err = smb_usr_get_flags2(sdp, arg, flags);
393 		break;
394 
395 	case SMBIOC_GETSSNKEY:
396 		err = smb_usr_get_ssnkey(sdp, arg, flags);
397 		break;
398 
399 	case SMBIOC_REQUEST:
400 		err = smb_usr_simplerq(sdp, arg, flags, cr);
401 		break;
402 
403 	case SMBIOC_T2RQ:
404 		err = smb_usr_t2request(sdp, arg, flags, cr);
405 		break;
406 
407 	case SMBIOC_READ:
408 	case SMBIOC_WRITE:
409 		err = smb_usr_rw(sdp, cmd, arg, flags, cr);
410 		break;
411 
412 	case SMBIOC_SSN_CREATE:
413 	case SMBIOC_SSN_FIND:
414 		err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
415 		break;
416 
417 	case SMBIOC_SSN_KILL:
418 	case SMBIOC_SSN_RELE:
419 		err = smb_usr_drop_ssn(sdp, cmd);
420 		break;
421 
422 	case SMBIOC_TREE_CONNECT:
423 	case SMBIOC_TREE_FIND:
424 		err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
425 		break;
426 
427 	case SMBIOC_TREE_KILL:
428 	case SMBIOC_TREE_RELE:
429 		err = smb_usr_drop_tree(sdp, cmd);
430 		break;
431 
432 	case SMBIOC_IOD_WORK:
433 		err = smb_usr_iod_work(sdp, arg, flags, cr);
434 		break;
435 
436 	case SMBIOC_IOD_IDLE:
437 	case SMBIOC_IOD_RCFAIL:
438 		err = smb_usr_iod_ioctl(sdp, cmd, arg, flags);
439 		break;
440 
441 	case SMBIOC_PK_ADD:
442 	case SMBIOC_PK_DEL:
443 	case SMBIOC_PK_CHK:
444 	case SMBIOC_PK_DEL_OWNER:
445 	case SMBIOC_PK_DEL_EVERYONE:
446 		err = smb_pkey_ioctl(cmd, arg, flags, cr);
447 		break;
448 
449 	default:
450 		err = ENOTTY;
451 		break;
452 	}
453 
454 	return (err);
455 }
456 
457 /*ARGSUSED*/
458 static int
459 nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
460 {
461 	major_t new_major;
462 	smb_dev_t *sdp, *sdv;
463 
464 	mutex_enter(&dev_lck);
465 	for (; ; ) {
466 		minor_t start = nsmb_minor;
467 		do {
468 			if (nsmb_minor >= MAXMIN32) {
469 				if (nsmb_major == getmajor(*dev))
470 					nsmb_minor = NSMB_MIN_MINOR;
471 				else
472 					nsmb_minor = 0;
473 			} else {
474 				nsmb_minor++;
475 			}
476 			sdv = ddi_get_soft_state(statep, nsmb_minor);
477 		} while ((sdv != NULL) && (nsmb_minor != start));
478 		if (nsmb_minor == start) {
479 			/*
480 			 * The condition we need to solve here is  all the
481 			 * MAXMIN32(~262000) minors numbers are reached. We
482 			 * need to create a new major number.
483 			 * zfs uses getudev() to create a new major number.
484 			 */
485 			if ((new_major = getudev()) == (major_t)-1) {
486 				cmn_err(CE_WARN,
487 				    "nsmb: Can't get unique major "
488 				    "device number.");
489 				mutex_exit(&dev_lck);
490 				return (-1);
491 			}
492 			nsmb_major = new_major;
493 			nsmb_minor = 0;
494 		} else {
495 			break;
496 		}
497 	}
498 
499 	/*
500 	 * This is called by mount or open call.
501 	 * The open() routine is passed a pointer to a device number so
502 	 * that  the  driver  can  change the minor number. This allows
503 	 * drivers to dynamically  create minor instances of  the  dev-
504 	 * ice.  An  example of this might be a  pseudo-terminal driver
505 	 * that creates a new pseudo-terminal whenever it   is  opened.
506 	 * A driver that chooses the minor number dynamically, normally
507 	 * creates only one  minor  device  node  in   attach(9E)  with
508 	 * ddi_create_minor_node(9F) then changes the minor number com-
509 	 * ponent of *devp using makedevice(9F)  and  getmajor(9F)  The
510 	 * driver needs to keep track of available minor numbers inter-
511 	 * nally.
512 	 * Stuff the structure smb_dev.
513 	 * return.
514 	 */
515 
516 	if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) {
517 		mutex_exit(&dev_lck);
518 		return (ENXIO);
519 	}
520 	if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) {
521 		mutex_exit(&dev_lck);
522 		return (ENXIO);
523 	}
524 
525 	sdp->sd_opened = 1;
526 	sdp->sd_seq = nsmb_minor;
527 	sdp->smb_cred = cr;
528 	sdp->sd_flags |= NSMBFL_OPEN;
529 	sdp->zoneid = crgetzoneid(cr);
530 	mutex_exit(&dev_lck);
531 
532 	*dev = makedevice(nsmb_major, nsmb_minor);
533 
534 	return (0);
535 }
536 
537 /*ARGSUSED*/
538 static int
539 nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
540 {
541 	minor_t inst = getminor(dev);
542 	smb_dev_t *sdp;
543 	int err;
544 
545 	mutex_enter(&dev_lck);
546 	/*
547 	 * 1. Check the validity of the minor number.
548 	 * 2. Release any shares/vc associated  with the connection.
549 	 * 3. Can close the minor number.
550 	 * 4. Deallocate any resources allocated in open() call.
551 	 */
552 
553 	sdp = ddi_get_soft_state(statep, inst);
554 	if (sdp != NULL)
555 		err = nsmb_close2(sdp, cr);
556 	else
557 		err = ENXIO;
558 
559 	/*
560 	 * Free the instance
561 	 */
562 	ddi_soft_state_free(statep, inst);
563 	mutex_exit(&dev_lck);
564 	return (err);
565 }
566 
567 static int
568 nsmb_close2(smb_dev_t *sdp, cred_t *cr)
569 {
570 	struct smb_vc *vcp;
571 	struct smb_share *ssp;
572 	struct smb_cred scred;
573 
574 	smb_credinit(&scred, cr);
575 	ssp = sdp->sd_share;
576 	if (ssp != NULL)
577 		smb_share_rele(ssp);
578 	vcp = sdp->sd_vc;
579 	if (vcp != NULL) {
580 		/*
581 		 * If this dev minor was opened by smbiod,
582 		 * mark this VC as "dead" because it now
583 		 * will have no IOD to service it.
584 		 */
585 		if (sdp->sd_flags & NSMBFL_IOD)
586 			smb_iod_disconnect(vcp);
587 		smb_vc_rele(vcp);
588 	}
589 
590 	smb_credrele(&scred);
591 	return (0);
592 }
593 
594 int
595 smb_dev2share(int fd, struct smb_share **sspp)
596 {
597 	file_t *fp = NULL;
598 	vnode_t *vp;
599 	smb_dev_t *sdp;
600 	smb_share_t *ssp;
601 	dev_t dev;
602 	int err;
603 
604 	if ((fp = getf(fd)) == NULL)
605 		return (EBADF);
606 
607 	vp = fp->f_vnode;
608 	dev = vp->v_rdev;
609 	if (dev == 0 || dev == NODEV ||
610 	    getmajor(dev) != nsmb_major) {
611 		err = EBADF;
612 		goto out;
613 	}
614 
615 	sdp = ddi_get_soft_state(statep, getminor(dev));
616 	if (sdp == NULL) {
617 		err = EINVAL;
618 		goto out;
619 	}
620 
621 	ssp = sdp->sd_share;
622 	if (ssp == NULL) {
623 		err = ENOTCONN;
624 		goto out;
625 	}
626 
627 	/*
628 	 * Our caller gains a ref. to this share.
629 	 */
630 	*sspp = ssp;
631 	smb_share_hold(ssp);
632 	err = 0;
633 
634 out:
635 	if (fp)
636 		releasef(fd);
637 	return (err);
638 }
639