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