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