Home | History | Annotate | Download | only in netsmb
      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