xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c (revision 2c5ec7a875dcd76853e6618614e990f1e8cdd56d)
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 
33 /*
34  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
35  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
36  * Use is subject to license terms.
37  */
38 
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/sysmacros.h>
43 #include <sys/uio.h>
44 #include <sys/buf.h>
45 #include <sys/modctl.h>
46 #include <sys/open.h>
47 #include <sys/file.h>
48 #include <sys/kmem.h>
49 #include <sys/conf.h>
50 #include <sys/cmn_err.h>
51 #include <sys/stat.h>
52 #include <sys/ddi.h>
53 #include <sys/sunddi.h>
54 #include <sys/sunldi.h>
55 #include <sys/policy.h>
56 #include <sys/zone.h>
57 #include <sys/pathname.h>
58 #include <sys/mount.h>
59 #include <sys/sdt.h>
60 #include <fs/fs_subr.h>
61 #include <sys/modctl.h>
62 #include <sys/devops.h>
63 #include <sys/thread.h>
64 #include <sys/types.h>
65 #include <sys/zone.h>
66 
67 #include <netsmb/smb_osdep.h>
68 #include <netsmb/mchain.h>		/* for "htoles()" */
69 
70 #include <netsmb/smb.h>
71 #include <netsmb/smb_conn.h>
72 #include <netsmb/smb_subr.h>
73 #include <netsmb/smb_dev.h>
74 #include <netsmb/smb_pass.h>
75 
76 #define	NSMB_MIN_MINOR	1
77 #define	NSMB_MAX_MINOR	L_MAXMIN32
78 
79 /* for version checks */
80 const uint32_t nsmb_version = NSMB_VERSION;
81 
82 static void *statep;
83 static major_t nsmb_major;
84 static minor_t last_minor = NSMB_MIN_MINOR;
85 static dev_info_t *nsmb_dip;
86 static kmutex_t  dev_lck;
87 
88 /* Zone support */
89 zone_key_t nsmb_zone_key;
90 extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
91 extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
92 
93 /*
94  * cb_ops device operations.
95  */
96 static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
97 static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
98 static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
99 				cred_t *credp, int *rvalp);
100 static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
101 
102 /* smbfs cb_ops */
103 static struct cb_ops nsmb_cbops = {
104 	nsmb_open,	/* open */
105 	nsmb_close,	/* close */
106 	nodev,		/* strategy */
107 	nodev,		/* print */
108 	nodev,		/* dump */
109 	nodev,		/* read */
110 	nodev,		/* write */
111 	nsmb_ioctl,	/* ioctl */
112 	nodev,		/* devmap */
113 	nodev,		/* mmap */
114 	nodev,		/* segmap */
115 	nochpoll,	/* poll */
116 	ddi_prop_op,	/* prop_op */
117 	NULL,		/* stream */
118 	D_MP,		/* cb_flag */
119 	CB_REV,		/* rev */
120 	nodev,		/* int (*cb_aread)() */
121 	nodev		/* int (*cb_awrite)() */
122 };
123 
124 /*
125  * Device options
126  */
127 static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
128 static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
129 static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
130 	void *arg, void **result);
131 
132 static struct dev_ops nsmb_ops = {
133 	DEVO_REV,	/* devo_rev, */
134 	0,		/* refcnt  */
135 	nsmb_getinfo,	/* info */
136 	nulldev,	/* identify */
137 	nulldev,	/* probe */
138 	nsmb_attach,	/* attach */
139 	nsmb_detach,	/* detach */
140 	nodev,		/* reset */
141 	&nsmb_cbops,	/* driver ops - devctl interfaces */
142 	NULL,		/* bus operations */
143 	NULL,		/* power */
144 	ddi_quiesce_not_needed,	/* quiesce */
145 };
146 
147 /*
148  * Module linkage information.
149  */
150 
151 static struct modldrv nsmb_modldrv = {
152 	&mod_driverops,				/* Driver module */
153 	"SMBFS network driver",
154 	&nsmb_ops				/* Driver ops */
155 };
156 
157 static struct modlinkage nsmb_modlinkage = {
158 	MODREV_1,
159 	(void *)&nsmb_modldrv,
160 	NULL
161 };
162 
163 int
164 _init(void)
165 {
166 	int error;
167 
168 	(void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
169 
170 	/* Can initialize some mutexes also. */
171 	mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
172 
173 	/* Connection data structures. */
174 	(void) smb_sm_init();
175 
176 	/* Initialize password Key chain DB. */
177 	smb_pkey_init();
178 
179 	/* Time conversion stuff. */
180 	smb_time_init();
181 
182 	/* Initialize crypto mechanisms. */
183 	smb_crypto_mech_init();
184 
185 	zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
186 	    nsmb_zone_destroy);
187 
188 	/*
189 	 * Install the module.  Do this after other init,
190 	 * to prevent entrances before we're ready.
191 	 */
192 	if ((error = mod_install((&nsmb_modlinkage))) != 0) {
193 
194 		/* Same as 2nd half of _fini */
195 		(void) zone_key_delete(nsmb_zone_key);
196 		smb_pkey_fini();
197 		smb_sm_done();
198 		mutex_destroy(&dev_lck);
199 		ddi_soft_state_fini(&statep);
200 
201 		return (error);
202 	}
203 
204 	return (0);
205 }
206 
207 int
208 _fini(void)
209 {
210 	int status;
211 
212 	/*
213 	 * Prevent unload if we have active VCs
214 	 * or stored passwords
215 	 */
216 	if ((status = smb_sm_idle()) != 0)
217 		return (status);
218 	if ((status = smb_pkey_idle()) != 0)
219 		return (status);
220 
221 	/*
222 	 * Remove the module.  Do this before destroying things,
223 	 * to prevent new entrances while we're destorying.
224 	 */
225 	if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
226 		return (status);
227 	}
228 
229 	(void) zone_key_delete(nsmb_zone_key);
230 
231 	/* Time conversion stuff. */
232 	smb_time_fini();
233 
234 	/* Destroy password Key chain DB. */
235 	smb_pkey_fini();
236 
237 	smb_sm_done();
238 
239 	mutex_destroy(&dev_lck);
240 	ddi_soft_state_fini(&statep);
241 
242 	return (status);
243 }
244 
245 int
246 _info(struct modinfo *modinfop)
247 {
248 	return (mod_info(&nsmb_modlinkage, modinfop));
249 }
250 
251 /*ARGSUSED*/
252 static int
253 nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
254 {
255 	int ret = DDI_SUCCESS;
256 
257 	switch (cmd) {
258 	case DDI_INFO_DEVT2DEVINFO:
259 		*result = nsmb_dip;
260 		break;
261 	case DDI_INFO_DEVT2INSTANCE:
262 		*result = NULL;
263 		break;
264 	default:
265 		ret = DDI_FAILURE;
266 	}
267 	return (ret);
268 }
269 
270 static int
271 nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
272 {
273 
274 	if (cmd != DDI_ATTACH)
275 		return (DDI_FAILURE);
276 
277 	/*
278 	 * We only support only one "instance".  Note that
279 	 * "instances" are different from minor units.
280 	 * We get one (unique) minor unit per open.
281 	 */
282 	if (ddi_get_instance(dip) > 0)
283 		return (DDI_FAILURE);
284 
285 	if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
286 	    NULL) == DDI_FAILURE) {
287 		cmn_err(CE_WARN, "nsmb_attach: create minor");
288 		return (DDI_FAILURE);
289 	}
290 
291 	/*
292 	 * We need the major number a couple places,
293 	 * i.e. in smb_dev2share()
294 	 */
295 	nsmb_major = ddi_name_to_major(NSMB_NAME);
296 
297 	nsmb_dip = dip;
298 	ddi_report_dev(dip);
299 	return (DDI_SUCCESS);
300 }
301 
302 /*ARGSUSED*/
303 static int
304 nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
305 {
306 
307 	if (cmd != DDI_DETACH)
308 		return (DDI_FAILURE);
309 	if (ddi_get_instance(dip) > 0)
310 		return (DDI_FAILURE);
311 
312 	nsmb_dip = NULL;
313 	ddi_remove_minor_node(dip, NULL);
314 
315 	return (DDI_SUCCESS);
316 }
317 
318 /*ARGSUSED*/
319 static int
320 nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags,	/* model.h */
321     cred_t *cr, int *rvalp)
322 {
323 	smb_dev_t *sdp;
324 	int err;
325 
326 	sdp = ddi_get_soft_state(statep, getminor(dev));
327 	if (sdp == NULL) {
328 		return (DDI_FAILURE);
329 	}
330 	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
331 		return (EBADF);
332 	}
333 
334 	/*
335 	 * Dont give access if the zone id is not as the same as we
336 	 * set in the nsmb_open or dont belong to the global zone.
337 	 * Check if the user belongs to this zone..
338 	 */
339 	if (sdp->zoneid != getzoneid())
340 		return (EIO);
341 
342 	/*
343 	 * We have a zone_shutdown call back that kills all the VCs
344 	 * in a zone that's shutting down.  That action will cause
345 	 * all of these ioctls to fail on such VCs, so no need to
346 	 * check the zone status here on every ioctl call.
347 	 */
348 
349 	/*
350 	 * Serialize ioctl calls.  The smb_usr_... functions
351 	 * don't expect concurrent calls on a given sdp.
352 	 */
353 	mutex_enter(&sdp->sd_lock);
354 	if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) {
355 		mutex_exit(&sdp->sd_lock);
356 		return (EBUSY);
357 	}
358 	sdp->sd_flags |= NSMBFL_IOCTL;
359 	mutex_exit(&sdp->sd_lock);
360 
361 	err = 0;
362 	switch (cmd) {
363 	case SMBIOC_GETVERS:
364 		(void) ddi_copyout(&nsmb_version, (void *)arg,
365 		    sizeof (nsmb_version), flags);
366 		break;
367 
368 	case SMBIOC_FLAGS2:
369 		err = smb_usr_get_flags2(sdp, arg, flags);
370 		break;
371 
372 	case SMBIOC_GETSSNKEY:
373 		err = smb_usr_get_ssnkey(sdp, arg, flags);
374 		break;
375 
376 	case SMBIOC_DUP_DEV:
377 		err = smb_usr_dup_dev(sdp, arg, flags);
378 		break;
379 
380 	case SMBIOC_REQUEST:
381 		err = smb_usr_simplerq(sdp, arg, flags, cr);
382 		break;
383 
384 	case SMBIOC_T2RQ:
385 		err = smb_usr_t2request(sdp, arg, flags, cr);
386 		break;
387 
388 	case SMBIOC_READ:
389 	case SMBIOC_WRITE:
390 		err = smb_usr_rw(sdp, cmd, arg, flags, cr);
391 		break;
392 
393 	case SMBIOC_NTCREATE:
394 		err = smb_usr_ntcreate(sdp, arg, flags, cr);
395 		break;
396 
397 	case SMBIOC_PRINTJOB:
398 		err = smb_usr_printjob(sdp, arg, flags, cr);
399 		break;
400 
401 	case SMBIOC_CLOSEFH:
402 		err = smb_usr_closefh(sdp, cr);
403 		break;
404 
405 	case SMBIOC_SSN_CREATE:
406 	case SMBIOC_SSN_FIND:
407 		err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
408 		break;
409 
410 	case SMBIOC_SSN_KILL:
411 	case SMBIOC_SSN_RELE:
412 		err = smb_usr_drop_ssn(sdp, cmd);
413 		break;
414 
415 	case SMBIOC_TREE_CONNECT:
416 	case SMBIOC_TREE_FIND:
417 		err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
418 		break;
419 
420 	case SMBIOC_TREE_KILL:
421 	case SMBIOC_TREE_RELE:
422 		err = smb_usr_drop_tree(sdp, cmd);
423 		break;
424 
425 	case SMBIOC_IOD_WORK:
426 		err = smb_usr_iod_work(sdp, arg, flags, cr);
427 		break;
428 
429 	case SMBIOC_IOD_IDLE:
430 	case SMBIOC_IOD_RCFAIL:
431 		err = smb_usr_iod_ioctl(sdp, cmd, arg, flags);
432 		break;
433 
434 	case SMBIOC_PK_ADD:
435 	case SMBIOC_PK_DEL:
436 	case SMBIOC_PK_CHK:
437 	case SMBIOC_PK_DEL_OWNER:
438 	case SMBIOC_PK_DEL_EVERYONE:
439 		err = smb_pkey_ioctl(cmd, arg, flags, cr);
440 		break;
441 
442 	default:
443 		err = ENOTTY;
444 		break;
445 	}
446 
447 	mutex_enter(&sdp->sd_lock);
448 	sdp->sd_flags &= ~NSMBFL_IOCTL;
449 	mutex_exit(&sdp->sd_lock);
450 
451 	return (err);
452 }
453 
454 /*
455  * This does "clone" open, meaning it automatically
456  * assigns an available minor unit for each open.
457  */
458 /*ARGSUSED*/
459 static int
460 nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
461 {
462 	smb_dev_t *sdp;
463 	minor_t m;
464 
465 	mutex_enter(&dev_lck);
466 
467 	for (m = last_minor + 1; m != last_minor; m++) {
468 		if (m > NSMB_MAX_MINOR)
469 			m = NSMB_MIN_MINOR;
470 
471 		if (ddi_get_soft_state(statep, m) == NULL) {
472 			last_minor = m;
473 			goto found;
474 		}
475 	}
476 
477 	/* No available minor units. */
478 	mutex_exit(&dev_lck);
479 	return (ENXIO);
480 
481 found:
482 	/* NB: dev_lck still held */
483 	if (ddi_soft_state_zalloc(statep, m) == DDI_FAILURE) {
484 		mutex_exit(&dev_lck);
485 		return (ENXIO);
486 	}
487 	if ((sdp = ddi_get_soft_state(statep, m)) == NULL) {
488 		mutex_exit(&dev_lck);
489 		return (ENXIO);
490 	}
491 	*dev = makedevice(nsmb_major, m);
492 	mutex_exit(&dev_lck);
493 
494 	sdp->sd_smbfid = -1;
495 	sdp->sd_flags |= NSMBFL_OPEN;
496 	sdp->zoneid = crgetzoneid(cr);
497 	mutex_init(&sdp->sd_lock, NULL, MUTEX_DRIVER, NULL);
498 
499 	return (0);
500 }
501 
502 /*ARGSUSED*/
503 static int
504 nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
505 {
506 	minor_t inst = getminor(dev);
507 	smb_dev_t *sdp;
508 	int err;
509 
510 	/*
511 	 * 1. Check the validity of the minor number.
512 	 * 2. Release any shares/vc associated  with the connection.
513 	 * 3. Can close the minor number.
514 	 * 4. Deallocate any resources allocated in open() call.
515 	 */
516 
517 	sdp = ddi_get_soft_state(statep, inst);
518 	if (sdp != NULL)
519 		err = nsmb_close2(sdp, cr);
520 	else
521 		err = ENXIO;
522 
523 	/*
524 	 * Free the instance
525 	 */
526 	mutex_enter(&dev_lck);
527 	ddi_soft_state_free(statep, inst);
528 	mutex_exit(&dev_lck);
529 	return (err);
530 }
531 
532 static int
533 nsmb_close2(smb_dev_t *sdp, cred_t *cr)
534 {
535 	struct smb_vc *vcp;
536 	struct smb_share *ssp;
537 
538 	if (sdp->sd_smbfid != -1)
539 		(void) smb_usr_closefh(sdp, cr);
540 
541 	ssp = sdp->sd_share;
542 	if (ssp != NULL)
543 		smb_share_rele(ssp);
544 
545 	vcp = sdp->sd_vc;
546 	if (vcp != NULL) {
547 		/*
548 		 * If this dev minor was opened by smbiod,
549 		 * mark this VC as "dead" because it now
550 		 * will have no IOD to service it.
551 		 */
552 		if (sdp->sd_flags & NSMBFL_IOD)
553 			smb_iod_disconnect(vcp);
554 		smb_vc_rele(vcp);
555 	}
556 	mutex_destroy(&sdp->sd_lock);
557 
558 	return (0);
559 }
560 
561 /*
562  * Helper for SMBIOC_DUP_DEV
563  * Duplicate state from the FD @arg ("from") onto
564  * the FD for this device instance.
565  */
566 int
567 smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags)
568 {
569 	file_t *fp = NULL;
570 	vnode_t *vp;
571 	smb_dev_t *from_sdp;
572 	dev_t dev;
573 	int32_t ufd;
574 	int err;
575 
576 	/* Should be no VC */
577 	if (sdp->sd_vc != NULL)
578 		return (EISCONN);
579 
580 	/*
581 	 * Get from_sdp (what we will duplicate)
582 	 */
583 	if (ddi_copyin((void *) arg, &ufd, sizeof (ufd), flags))
584 		return (EFAULT);
585 	if ((fp = getf(ufd)) == NULL)
586 		return (EBADF);
587 	/* rele fp below */
588 	vp = fp->f_vnode;
589 	dev = vp->v_rdev;
590 	if (dev == 0 || dev == NODEV ||
591 	    getmajor(dev) != nsmb_major) {
592 		err = EINVAL;
593 		goto out;
594 	}
595 	from_sdp = ddi_get_soft_state(statep, getminor(dev));
596 	if (from_sdp == NULL) {
597 		err = EINVAL;
598 		goto out;
599 	}
600 
601 	/*
602 	 * Duplicate VC and share references onto this FD.
603 	 */
604 	if ((sdp->sd_vc = from_sdp->sd_vc) != NULL)
605 		smb_vc_hold(sdp->sd_vc);
606 	if ((sdp->sd_share = from_sdp->sd_share) != NULL)
607 		smb_share_hold(sdp->sd_share);
608 	sdp->sd_level = from_sdp->sd_level;
609 	err = 0;
610 
611 out:
612 	if (fp)
613 		releasef(ufd);
614 	return (err);
615 }
616 
617 
618 /*
619  * Helper used by smbfs_mount
620  */
621 int
622 smb_dev2share(int fd, struct smb_share **sspp)
623 {
624 	file_t *fp = NULL;
625 	vnode_t *vp;
626 	smb_dev_t *sdp;
627 	smb_share_t *ssp;
628 	dev_t dev;
629 	int err;
630 
631 	if ((fp = getf(fd)) == NULL)
632 		return (EBADF);
633 	/* rele fp below */
634 
635 	vp = fp->f_vnode;
636 	dev = vp->v_rdev;
637 	if (dev == 0 || dev == NODEV ||
638 	    getmajor(dev) != nsmb_major) {
639 		err = EINVAL;
640 		goto out;
641 	}
642 
643 	sdp = ddi_get_soft_state(statep, getminor(dev));
644 	if (sdp == NULL) {
645 		err = EINVAL;
646 		goto out;
647 	}
648 
649 	ssp = sdp->sd_share;
650 	if (ssp == NULL) {
651 		err = ENOTCONN;
652 		goto out;
653 	}
654 
655 	/*
656 	 * Our caller gains a ref. to this share.
657 	 */
658 	*sspp = ssp;
659 	smb_share_hold(ssp);
660 	err = 0;
661 
662 out:
663 	if (fp)
664 		releasef(fd);
665 	return (err);
666 }
667