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