xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c (revision cd3e933325e68e23516a196a8fea7f49b1e497c3)
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 #include <netsmb/smb_osdep.h>
70 #include <netsmb/mchain.h>		/* for "htoles()" */
71 
72 #include <netsmb/smb.h>
73 #include <netsmb/smb_conn.h>
74 #include <netsmb/smb_subr.h>
75 #include <netsmb/smb_dev.h>
76 #include <netsmb/smb_pass.h>
77 
78 /* for version checks */
79 const uint32_t nsmb_version = NSMB_VERSION;
80 
81 /*
82  * Userland code loops through minor #s 0 to 1023, looking for one which opens.
83  * Intially we create minor 0 and leave it for anyone.  Minor zero will never
84  * actually get used - opening triggers creation of another (but private) minor,
85  * which userland code will get to and mark busy.
86  */
87 #define	SMBMINORS 1024
88 static void *statep;
89 static major_t nsmb_major;
90 static minor_t nsmb_minor = 1;
91 
92 #define	NSMB_MAX_MINOR  (1 << 8)
93 #define	NSMB_MIN_MINOR   (NSMB_MAX_MINOR + 1)
94 
95 #define	ILP32	1
96 #define	LP64	2
97 
98 static kmutex_t  dev_lck;
99 
100 /* Zone support */
101 zone_key_t nsmb_zone_key;
102 extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
103 extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
104 
105 /*
106  * cb_ops device operations.
107  */
108 static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
109 static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
110 static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
111 				cred_t *credp, int *rvalp);
112 static int nsmb_close2(smb_dev_t *sdp, cred_t *cr);
113 
114 /* smbfs cb_ops */
115 static struct cb_ops nsmb_cbops = {
116 	nsmb_open,	/* open */
117 	nsmb_close,	/* close */
118 	nodev,		/* strategy */
119 	nodev,		/* print */
120 	nodev,		/* dump */
121 	nodev,		/* read */
122 	nodev,		/* write */
123 	nsmb_ioctl,	/* ioctl */
124 	nodev,		/* devmap */
125 	nodev,		/* mmap */
126 	nodev,		/* segmap */
127 	nochpoll,	/* poll */
128 	ddi_prop_op,	/* prop_op */
129 	NULL,		/* stream */
130 	D_MP,		/* cb_flag */
131 	CB_REV,		/* rev */
132 	nodev,		/* int (*cb_aread)() */
133 	nodev		/* int (*cb_awrite)() */
134 };
135 
136 /*
137  * Device options
138  */
139 static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
140 static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
141 static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
142 	void *arg, void **result);
143 
144 static struct dev_ops nsmb_ops = {
145 	DEVO_REV,	/* devo_rev, */
146 	0,		/* refcnt  */
147 	nsmb_getinfo,	/* info */
148 	nulldev,	/* identify */
149 	nulldev,	/* probe */
150 	nsmb_attach,	/* attach */
151 	nsmb_detach,	/* detach */
152 	nodev,		/* reset */
153 	&nsmb_cbops,	/* driver ops - devctl interfaces */
154 	NULL,		/* bus operations */
155 	NULL,		/* power */
156 	ddi_quiesce_not_needed,	/* quiesce */
157 };
158 
159 /*
160  * Module linkage information.
161  */
162 
163 static struct modldrv nsmb_modldrv = {
164 	&mod_driverops,				/* Driver module */
165 	"SMBFS network driver",
166 	&nsmb_ops				/* Driver ops */
167 };
168 
169 static struct modlinkage nsmb_modlinkage = {
170 	MODREV_1,
171 	(void *)&nsmb_modldrv,
172 	NULL
173 };
174 
175 int
176 _init(void)
177 {
178 	int error;
179 
180 	(void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
181 
182 	/* Can initialize some mutexes also. */
183 	mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
184 	/*
185 	 * Create a major name and number.
186 	 */
187 	nsmb_major = ddi_name_to_major(NSMB_NAME);
188 	nsmb_minor = 0;
189 
190 	/* Connection data structures. */
191 	(void) smb_sm_init();
192 
193 	/* Initialize password Key chain DB. */
194 	smb_pkey_init();
195 
196 	/* Time conversion stuff. */
197 	smb_time_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 	/* Time conversion stuff. */
249 	smb_time_fini();
250 
251 	/* Destroy password Key chain DB. */
252 	smb_pkey_fini();
253 
254 	smb_sm_done();
255 
256 	mutex_destroy(&dev_lck);
257 	ddi_soft_state_fini(&statep);
258 
259 	return (status);
260 }
261 
262 int
263 _info(struct modinfo *modinfop)
264 {
265 	return (mod_info(&nsmb_modlinkage, modinfop));
266 }
267 
268 /*ARGSUSED*/
269 static int
270 nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
271 {
272 	int ret = DDI_SUCCESS;
273 
274 	switch (cmd) {
275 	case DDI_INFO_DEVT2DEVINFO:
276 		*result = 0;
277 		break;
278 	case DDI_INFO_DEVT2INSTANCE:
279 		*result = 0;
280 		break;
281 	default:
282 		ret = DDI_FAILURE;
283 	}
284 	return (ret);
285 }
286 
287 static int
288 nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
289 {
290 	smb_dev_t *sdp;
291 
292 	if (cmd != DDI_ATTACH)
293 		return (DDI_FAILURE);
294 	/*
295 	 * only one instance - but we clone using the open routine
296 	 */
297 	if (ddi_get_instance(dip) > 0)
298 		return (DDI_FAILURE);
299 
300 	mutex_enter(&dev_lck);
301 
302 	/*
303 	 * This is the Zero'th minor device which is created.
304 	 */
305 	if (ddi_soft_state_zalloc(statep, 0) == DDI_FAILURE) {
306 		cmn_err(CE_WARN, "nsmb_attach: soft state alloc");
307 		goto attach_failed;
308 	}
309 	if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
310 	    NULL) == DDI_FAILURE) {
311 		cmn_err(CE_WARN, "nsmb_attach: create minor");
312 		goto attach_failed;
313 	}
314 	if ((sdp = ddi_get_soft_state(statep, 0)) == NULL) {
315 		cmn_err(CE_WARN, "nsmb_attach: get soft state");
316 		ddi_remove_minor_node(dip, NULL);
317 		goto attach_failed;
318 	}
319 
320 	/*
321 	 * Need to see if this field is required.
322 	 * REVISIT
323 	 */
324 	sdp->smb_dip = dip;
325 	sdp->sd_seq = 0;
326 	sdp->sd_opened = 1;
327 
328 	mutex_exit(&dev_lck);
329 	ddi_report_dev(dip);
330 	return (DDI_SUCCESS);
331 
332 attach_failed:
333 	ddi_soft_state_free(statep, 0);
334 	mutex_exit(&dev_lck);
335 	return (DDI_FAILURE);
336 }
337 
338 /*ARGSUSED*/
339 static int
340 nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
341 {
342 
343 	if (cmd != DDI_DETACH)
344 		return (DDI_FAILURE);
345 	if (ddi_get_instance(dip) > 0)
346 		return (DDI_FAILURE);
347 
348 	ddi_soft_state_free(statep, 0);
349 	ddi_remove_minor_node(dip, NULL);
350 
351 	return (DDI_SUCCESS);
352 }
353 
354 /*ARGSUSED*/
355 static int
356 nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags,	/* model.h */
357 	cred_t *cr, int *rvalp)
358 {
359 	smb_dev_t *sdp;
360 	int err;
361 
362 	sdp = ddi_get_soft_state(statep, getminor(dev));
363 	if (sdp == NULL) {
364 		return (DDI_FAILURE);
365 	}
366 	if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
367 		return (EBADF);
368 	}
369 
370 	/*
371 	 * Dont give access if the zone id is not as the same as we
372 	 * set in the nsmb_open or dont belong to the global zone.
373 	 * Check if the user belongs to this zone..
374 	 */
375 	if (sdp->zoneid != getzoneid())
376 		return (EIO);
377 
378 	/*
379 	 * We have a zone_shutdown call back that kills all the VCs
380 	 * in a zone that's shutting down.  That action will cause
381 	 * all of these ioctls to fail on such VCs, so no need to
382 	 * check the zone status here on every ioctl call.
383 	 */
384 
385 	err = 0;
386 	switch (cmd) {
387 	case SMBIOC_GETVERS:
388 		(void) ddi_copyout(&nsmb_version, (void *)arg,
389 		    sizeof (nsmb_version), flags);
390 		break;
391 
392 	case SMBIOC_FLAGS2:
393 		err = smb_usr_get_flags2(sdp, arg, flags);
394 		break;
395 
396 	case SMBIOC_GETSSNKEY:
397 		err = smb_usr_get_ssnkey(sdp, arg, flags);
398 		break;
399 
400 	case SMBIOC_REQUEST:
401 		err = smb_usr_simplerq(sdp, arg, flags, cr);
402 		break;
403 
404 	case SMBIOC_T2RQ:
405 		err = smb_usr_t2request(sdp, arg, flags, cr);
406 		break;
407 
408 	case SMBIOC_READ:
409 	case SMBIOC_WRITE:
410 		err = smb_usr_rw(sdp, cmd, arg, flags, cr);
411 		break;
412 
413 	case SMBIOC_SSN_CREATE:
414 	case SMBIOC_SSN_FIND:
415 		err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr);
416 		break;
417 
418 	case SMBIOC_SSN_KILL:
419 	case SMBIOC_SSN_RELE:
420 		err = smb_usr_drop_ssn(sdp, cmd);
421 		break;
422 
423 	case SMBIOC_TREE_CONNECT:
424 	case SMBIOC_TREE_FIND:
425 		err = smb_usr_get_tree(sdp, cmd, arg, flags, cr);
426 		break;
427 
428 	case SMBIOC_TREE_KILL:
429 	case SMBIOC_TREE_RELE:
430 		err = smb_usr_drop_tree(sdp, cmd);
431 		break;
432 
433 	case SMBIOC_IOD_WORK:
434 		err = smb_usr_iod_work(sdp, arg, flags, cr);
435 		break;
436 
437 	case SMBIOC_IOD_IDLE:
438 	case SMBIOC_IOD_RCFAIL:
439 		err = smb_usr_iod_ioctl(sdp, cmd, arg, flags);
440 		break;
441 
442 	case SMBIOC_PK_ADD:
443 	case SMBIOC_PK_DEL:
444 	case SMBIOC_PK_CHK:
445 	case SMBIOC_PK_DEL_OWNER:
446 	case SMBIOC_PK_DEL_EVERYONE:
447 		err = smb_pkey_ioctl(cmd, arg, flags, cr);
448 		break;
449 
450 	default:
451 		err = ENOTTY;
452 		break;
453 	}
454 
455 	return (err);
456 }
457 
458 /*ARGSUSED*/
459 static int
460 nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
461 {
462 	major_t new_major;
463 	smb_dev_t *sdp, *sdv;
464 
465 	mutex_enter(&dev_lck);
466 	for (; ; ) {
467 		minor_t start = nsmb_minor;
468 		do {
469 			if (nsmb_minor >= MAXMIN32) {
470 				if (nsmb_major == getmajor(*dev))
471 					nsmb_minor = NSMB_MIN_MINOR;
472 				else
473 					nsmb_minor = 0;
474 			} else {
475 				nsmb_minor++;
476 			}
477 			sdv = ddi_get_soft_state(statep, nsmb_minor);
478 		} while ((sdv != NULL) && (nsmb_minor != start));
479 		if (nsmb_minor == start) {
480 			/*
481 			 * The condition we need to solve here is  all the
482 			 * MAXMIN32(~262000) minors numbers are reached. We
483 			 * need to create a new major number.
484 			 * zfs uses getudev() to create a new major number.
485 			 */
486 			if ((new_major = getudev()) == (major_t)-1) {
487 				cmn_err(CE_WARN,
488 				    "nsmb: Can't get unique major "
489 				    "device number.");
490 				mutex_exit(&dev_lck);
491 				return (-1);
492 			}
493 			nsmb_major = new_major;
494 			nsmb_minor = 0;
495 		} else {
496 			break;
497 		}
498 	}
499 
500 	/*
501 	 * This is called by mount or open call.
502 	 * The open() routine is passed a pointer to a device number so
503 	 * that  the  driver  can  change the minor number. This allows
504 	 * drivers to dynamically  create minor instances of  the  dev-
505 	 * ice.  An  example of this might be a  pseudo-terminal driver
506 	 * that creates a new pseudo-terminal whenever it   is  opened.
507 	 * A driver that chooses the minor number dynamically, normally
508 	 * creates only one  minor  device  node  in   attach(9E)  with
509 	 * ddi_create_minor_node(9F) then changes the minor number com-
510 	 * ponent of *devp using makedevice(9F)  and  getmajor(9F)  The
511 	 * driver needs to keep track of available minor numbers inter-
512 	 * nally.
513 	 * Stuff the structure smb_dev.
514 	 * return.
515 	 */
516 
517 	if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) {
518 		mutex_exit(&dev_lck);
519 		return (ENXIO);
520 	}
521 	if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) {
522 		mutex_exit(&dev_lck);
523 		return (ENXIO);
524 	}
525 
526 	sdp->sd_opened = 1;
527 	sdp->sd_seq = nsmb_minor;
528 	sdp->smb_cred = cr;
529 	sdp->sd_flags |= NSMBFL_OPEN;
530 	sdp->zoneid = crgetzoneid(cr);
531 	mutex_exit(&dev_lck);
532 
533 	*dev = makedevice(nsmb_major, nsmb_minor);
534 
535 	return (0);
536 }
537 
538 /*ARGSUSED*/
539 static int
540 nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
541 {
542 	minor_t inst = getminor(dev);
543 	smb_dev_t *sdp;
544 	int err;
545 
546 	mutex_enter(&dev_lck);
547 	/*
548 	 * 1. Check the validity of the minor number.
549 	 * 2. Release any shares/vc associated  with the connection.
550 	 * 3. Can close the minor number.
551 	 * 4. Deallocate any resources allocated in open() call.
552 	 */
553 
554 	sdp = ddi_get_soft_state(statep, inst);
555 	if (sdp != NULL)
556 		err = nsmb_close2(sdp, cr);
557 	else
558 		err = ENXIO;
559 
560 	/*
561 	 * Free the instance
562 	 */
563 	ddi_soft_state_free(statep, inst);
564 	mutex_exit(&dev_lck);
565 	return (err);
566 }
567 
568 static int
569 nsmb_close2(smb_dev_t *sdp, cred_t *cr)
570 {
571 	struct smb_vc *vcp;
572 	struct smb_share *ssp;
573 	struct smb_cred scred;
574 
575 	smb_credinit(&scred, cr);
576 	ssp = sdp->sd_share;
577 	if (ssp != NULL)
578 		smb_share_rele(ssp);
579 	vcp = sdp->sd_vc;
580 	if (vcp != NULL) {
581 		/*
582 		 * If this dev minor was opened by smbiod,
583 		 * mark this VC as "dead" because it now
584 		 * will have no IOD to service it.
585 		 */
586 		if (sdp->sd_flags & NSMBFL_IOD)
587 			smb_iod_disconnect(vcp);
588 		smb_vc_rele(vcp);
589 	}
590 
591 	smb_credrele(&scred);
592 	return (0);
593 }
594 
595 int
596 smb_dev2share(int fd, struct smb_share **sspp)
597 {
598 	file_t *fp = NULL;
599 	vnode_t *vp;
600 	smb_dev_t *sdp;
601 	smb_share_t *ssp;
602 	dev_t dev;
603 	int err;
604 
605 	if ((fp = getf(fd)) == NULL)
606 		return (EBADF);
607 
608 	vp = fp->f_vnode;
609 	dev = vp->v_rdev;
610 	if (dev == 0 || dev == NODEV ||
611 	    getmajor(dev) != nsmb_major) {
612 		err = EBADF;
613 		goto out;
614 	}
615 
616 	sdp = ddi_get_soft_state(statep, getminor(dev));
617 	if (sdp == NULL) {
618 		err = EINVAL;
619 		goto out;
620 	}
621 
622 	ssp = sdp->sd_share;
623 	if (ssp == NULL) {
624 		err = ENOTCONN;
625 		goto out;
626 	}
627 
628 	/*
629 	 * Our caller gains a ref. to this share.
630 	 */
631 	*sspp = ssp;
632 	smb_share_hold(ssp);
633 	err = 0;
634 
635 out:
636 	if (fp)
637 		releasef(fd);
638 	return (err);
639 }
640