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