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_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $ 33 */ 34 /* 35 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 36 * Use is subject to license terms. 37 */ 38 39 /* 40 * Connection engine. 41 */ 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/kmem.h> 46 #include <sys/proc.h> 47 #include <sys/lock.h> 48 #include <sys/vnode.h> 49 #include <sys/stream.h> 50 #include <sys/stropts.h> 51 #include <sys/socketvar.h> 52 #include <sys/cred.h> 53 #include <sys/cred_impl.h> 54 #include <netinet/in.h> 55 #include <inet/ip.h> 56 #include <inet/ip6.h> 57 #include <sys/cmn_err.h> 58 #include <sys/thread.h> 59 #include <sys/atomic.h> 60 61 #ifdef APPLE 62 #include <sys/smb_apple.h> 63 #include <sys/smb_iconv.h> 64 #else 65 #include <netsmb/smb_osdep.h> 66 #endif 67 68 #include <netsmb/smb.h> 69 #include <netsmb/smb_conn.h> 70 #include <netsmb/smb_subr.h> 71 #include <netsmb/smb_tran.h> 72 #include <netsmb/smb_pass.h> 73 74 static struct smb_connobj smb_vclist; 75 static uint_t smb_vcnext = 0; /* next unique id for VC */ 76 77 void smb_co_init(struct smb_connobj *cp, int level, char *objname); 78 void smb_co_done(struct smb_connobj *cp); 79 void smb_co_hold(struct smb_connobj *cp); 80 void smb_co_rele(struct smb_connobj *cp); 81 void smb_co_kill(struct smb_connobj *cp); 82 83 #ifdef APPLE 84 static void smb_sm_lockvclist(void); 85 static void smb_sm_unlockvclist(void); 86 #endif 87 88 static void smb_vc_free(struct smb_connobj *cp); 89 static void smb_vc_gone(struct smb_connobj *cp); 90 91 static void smb_share_free(struct smb_connobj *cp); 92 static void smb_share_gone(struct smb_connobj *cp); 93 94 /* smb_dup_sockaddr moved to smb_tran.c */ 95 96 int 97 smb_sm_init(void) 98 { 99 smb_co_init(&smb_vclist, SMBL_SM, "smbsm"); 100 return (0); 101 } 102 103 int 104 smb_sm_idle(void) 105 { 106 int error = 0; 107 SMB_CO_LOCK(&smb_vclist); 108 if (smb_vclist.co_usecount > 1) { 109 SMBSDEBUG("%d connections still active\n", 110 smb_vclist.co_usecount - 1); 111 error = EBUSY; 112 } 113 SMB_CO_UNLOCK(&smb_vclist); 114 return (error); 115 } 116 117 void 118 smb_sm_done(void) 119 { 120 /* 121 * XXX Q4BP why are we not iterating on smb_vclist here? 122 * Because the caller has just called smb_sm_idle() to 123 * make sure we have no VCs before calling this. 124 */ 125 smb_co_done(&smb_vclist); 126 } 127 128 /* 129 * Find a VC identified by the info in vcspec, 130 * and return it with a "hold", but not locked. 131 */ 132 /*ARGSUSED*/ 133 static int 134 smb_sm_lookupvc( 135 struct smb_vcspec *vcspec, 136 struct smb_cred *scred, 137 struct smb_vc **vcpp) 138 { 139 struct smb_connobj *co; 140 struct smb_vc *vcp; 141 zoneid_t zoneid = getzoneid(); 142 143 ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); 144 145 /* var, head, next_field */ 146 SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { 147 vcp = CPTOVC(co); 148 149 /* 150 * Some things we can check without 151 * holding the lock (those that are 152 * set at creation and never change). 153 */ 154 155 /* VCs in other zones are invisibile. */ 156 if (vcp->vc_zoneid != zoneid) 157 continue; 158 159 /* Also segregate by owner. */ 160 if (vcp->vc_uid != vcspec->owner) 161 continue; 162 163 /* XXX: we ignore the group. Remove vc_gid? */ 164 165 /* server */ 166 if (smb_cmp_sockaddr(vcp->vc_paddr, vcspec->sap)) 167 continue; 168 169 /* domain+user */ 170 if (strcmp(vcp->vc_domain, vcspec->domain)) 171 continue; 172 if (strcmp(vcp->vc_username, vcspec->username)) 173 continue; 174 175 SMB_VC_LOCK(vcp); 176 177 /* No new references allowed when _GONE is set */ 178 if (vcp->vc_flags & SMBV_GONE) 179 goto unlock_continue; 180 181 if (vcp->vc_vopt & SMBVOPT_PRIVATE) 182 goto unlock_continue; 183 184 found: 185 /* 186 * Success! (Found one we can use) 187 * Return with it held, unlocked. 188 * In-line smb_vc_hold here. 189 */ 190 co->co_usecount++; 191 SMB_VC_UNLOCK(vcp); 192 *vcpp = vcp; 193 return (0); 194 195 unlock_continue: 196 SMB_VC_UNLOCK(vcp); 197 /* keep looking. */ 198 } 199 200 return (ENOENT); 201 } 202 203 int 204 smb_sm_findvc( 205 struct smb_vcspec *vcspec, 206 struct smb_cred *scred, 207 struct smb_vc **vcpp) 208 { 209 struct smb_vc *vcp; 210 int error; 211 212 *vcpp = vcp = NULL; 213 214 SMB_CO_LOCK(&smb_vclist); 215 error = smb_sm_lookupvc(vcspec, scred, &vcp); 216 SMB_CO_UNLOCK(&smb_vclist); 217 218 /* Return if smb_sm_lookupvc fails */ 219 if (error != 0) 220 return (error); 221 222 /* Ingore any VC that's not active. */ 223 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { 224 smb_vc_rele(vcp); 225 return (ENOENT); 226 } 227 228 /* Active VC. Return it held. */ 229 *vcpp = vcp; 230 return (error); 231 } 232 233 int 234 smb_sm_negotiate( 235 struct smb_vcspec *vcspec, 236 struct smb_cred *scred, 237 struct smb_vc **vcpp) 238 { 239 struct smb_vc *vcp; 240 clock_t tmo; 241 int created, error; 242 243 top: 244 *vcpp = vcp = NULL; 245 246 SMB_CO_LOCK(&smb_vclist); 247 error = smb_sm_lookupvc(vcspec, scred, &vcp); 248 if (error) { 249 /* The VC was not found. Create? */ 250 if ((vcspec->optflags & SMBVOPT_CREATE) == 0) { 251 SMB_CO_UNLOCK(&smb_vclist); 252 return (error); 253 } 254 error = smb_vc_create(vcspec, scred, &vcp); 255 if (error) { 256 /* Could not create? Unusual. */ 257 SMB_CO_UNLOCK(&smb_vclist); 258 return (error); 259 } 260 /* Note: co_usecount == 1 */ 261 created = 1; 262 } else 263 created = 0; 264 SMB_CO_UNLOCK(&smb_vclist); 265 266 if (created == 0) { 267 /* 268 * Found an existing VC. Reuse it, but first, 269 * wait for any other thread doing setup, etc. 270 * Note: We hold a reference on the VC. 271 */ 272 error = 0; 273 SMB_VC_LOCK(vcp); 274 while (vcp->vc_state < SMBIOD_ST_VCACTIVE) { 275 if (vcp->vc_flags & SMBV_GONE) 276 break; 277 tmo = lbolt + SEC_TO_TICK(2); 278 tmo = cv_timedwait_sig(&vcp->vc_statechg, 279 &vcp->vc_lock, tmo); 280 if (tmo == 0) { 281 error = EINTR; 282 break; 283 } 284 } 285 SMB_VC_UNLOCK(vcp); 286 287 /* Interrupted? */ 288 if (error) 289 goto out; 290 291 /* 292 * Was there a vc_kill while we waited? 293 * If so, this VC is gone. Start over. 294 */ 295 if (vcp->vc_flags & SMBV_GONE) { 296 smb_vc_rele(vcp); 297 goto top; 298 } 299 300 /* 301 * The possible states here are: 302 * SMBIOD_ST_VCACTIVE, SMBIOD_ST_DEAD 303 * 304 * SMBIOD_ST_VCACTIVE is the normal case, 305 * where found a connection ready to use. 306 * 307 * We may find vc_state == SMBIOD_ST_DEAD 308 * if a previous session has disconnected. 309 * In this case, we'd like to reconnect, 310 * so take over setting up this VC as if 311 * this thread had created it. 312 */ 313 SMB_VC_LOCK(vcp); 314 if (vcp->vc_state == SMBIOD_ST_DEAD) { 315 vcp->vc_state = SMBIOD_ST_NOTCONN; 316 created = 1; 317 /* Will signal vc_statechg below */ 318 } 319 SMB_VC_UNLOCK(vcp); 320 } 321 322 if (created) { 323 /* 324 * We have a NEW VC, held, but not locked. 325 */ 326 327 SMBIODEBUG("vc_state=%d\n", vcp->vc_state); 328 switch (vcp->vc_state) { 329 330 case SMBIOD_ST_NOTCONN: 331 (void) smb_vc_setup(vcspec, scred, vcp, 0); 332 vcp->vc_genid++; 333 /* XXX: Save credentials of caller here? */ 334 vcp->vc_state = SMBIOD_ST_RECONNECT; 335 /* FALLTHROUGH */ 336 337 case SMBIOD_ST_RECONNECT: 338 error = smb_iod_connect(vcp); 339 if (error) 340 break; 341 vcp->vc_state = SMBIOD_ST_TRANACTIVE; 342 /* FALLTHROUGH */ 343 344 case SMBIOD_ST_TRANACTIVE: 345 /* XXX: Just pass vcspec instead? */ 346 vcp->vc_intok = vcspec->tok; 347 vcp->vc_intoklen = vcspec->toklen; 348 error = smb_smb_negotiate(vcp, &vcp->vc_scred); 349 vcp->vc_intok = NULL; 350 vcp->vc_intoklen = 0; 351 if (error) 352 break; 353 vcp->vc_state = SMBIOD_ST_NEGOACTIVE; 354 /* FALLTHROUGH */ 355 356 case SMBIOD_ST_NEGOACTIVE: 357 case SMBIOD_ST_SSNSETUP: 358 case SMBIOD_ST_VCACTIVE: 359 /* We can (re)use this VC. */ 360 error = 0; 361 break; 362 363 default: 364 error = EINVAL; 365 break; 366 } 367 368 if (error) { 369 /* 370 * Leave the VC in a state that allows the 371 * next open to attempt a new connection. 372 * This call does the cv_broadcast too, 373 * so that's in the else part. 374 */ 375 smb_iod_disconnect(vcp); 376 } else { 377 SMB_VC_LOCK(vcp); 378 cv_broadcast(&vcp->vc_statechg); 379 SMB_VC_UNLOCK(vcp); 380 } 381 } 382 383 out: 384 if (error) { 385 /* 386 * Undo the hold from lookupvc, 387 * or destroy if from vc_create. 388 */ 389 smb_vc_rele(vcp); 390 } else { 391 /* Return it held. */ 392 *vcpp = vcp; 393 } 394 395 return (error); 396 } 397 398 399 int 400 smb_sm_ssnsetup( 401 struct smb_vcspec *vcspec, 402 struct smb_cred *scred, 403 struct smb_vc *vcp) 404 { 405 int error; 406 407 /* 408 * We have a VC, held, but not locked. 409 * 410 * Code from smb_iod_ssnsetup, 411 * with lots of rework. 412 */ 413 414 SMBIODEBUG("vc_state=%d\n", vcp->vc_state); 415 switch (vcp->vc_state) { 416 417 case SMBIOD_ST_NEGOACTIVE: 418 /* 419 * This is the state we normally find. 420 * Calling _setup AGAIN to update the 421 * flags, security info, etc. 422 */ 423 error = smb_vc_setup(vcspec, scred, vcp, 1); 424 if (error) 425 break; 426 vcp->vc_state = SMBIOD_ST_SSNSETUP; 427 /* FALLTHROUGH */ 428 429 case SMBIOD_ST_SSNSETUP: 430 /* XXX: Just pass vcspec instead? */ 431 vcp->vc_intok = vcspec->tok; 432 vcp->vc_intoklen = vcspec->toklen; 433 error = smb_smb_ssnsetup(vcp, &vcp->vc_scred); 434 vcp->vc_intok = NULL; 435 vcp->vc_intoklen = 0; 436 if (error) 437 break; 438 /* OK, start the reader thread... */ 439 error = smb_iod_create(vcp); 440 if (error) 441 break; 442 vcp->vc_state = SMBIOD_ST_VCACTIVE; 443 /* FALLTHROUGH */ 444 445 case SMBIOD_ST_VCACTIVE: 446 /* We can (re)use this VC. */ 447 error = 0; 448 break; 449 450 default: 451 error = EINVAL; 452 break; 453 } 454 455 SMB_VC_LOCK(vcp); 456 cv_broadcast(&vcp->vc_statechg); 457 SMB_VC_UNLOCK(vcp); 458 459 return (error); 460 } 461 462 int 463 smb_sm_tcon( 464 struct smb_sharespec *shspec, 465 struct smb_cred *scred, 466 struct smb_vc *vcp, 467 struct smb_share **sspp) 468 { 469 struct smb_share *ssp; 470 int error; 471 472 *sspp = ssp = NULL; 473 474 if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { 475 /* 476 * The wait for vc_state in smb_sm_negotiate 477 * _should_ get us a VC in the right state. 478 */ 479 SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); 480 return (ENOTCONN); 481 } 482 483 SMB_VC_LOCK(vcp); 484 error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); 485 if (error) { 486 /* The share was not found. Create? */ 487 if ((shspec->optflags & SMBVOPT_CREATE) == 0) { 488 SMB_VC_UNLOCK(vcp); 489 return (error); 490 } 491 error = smb_share_create(vcp, shspec, scred, &ssp); 492 if (error) { 493 /* Could not create? Unusual. */ 494 SMB_VC_UNLOCK(vcp); 495 return (error); 496 } 497 /* Note: co_usecount == 1 */ 498 } 499 SMB_VC_UNLOCK(vcp); 500 501 /* 502 * We have a share, held, but not locked. 503 * Make it connected... 504 */ 505 SMB_SS_LOCK(ssp); 506 if (!smb_share_valid(ssp)) 507 error = smb_share_tcon(ssp); 508 SMB_SS_UNLOCK(ssp); 509 510 if (error) { 511 /* 512 * Undo hold from lookupshare, 513 * or destroy if from _create. 514 */ 515 smb_share_rele(ssp); 516 } else { 517 /* Return it held. */ 518 *sspp = ssp; 519 } 520 521 return (error); 522 } 523 524 /* 525 * Common code for connection object 526 */ 527 /*ARGSUSED*/ 528 void 529 smb_co_init(struct smb_connobj *cp, int level, char *objname) 530 { 531 532 mutex_init(&cp->co_lock, objname, MUTEX_DRIVER, NULL); 533 534 cp->co_level = level; 535 cp->co_usecount = 1; 536 SLIST_INIT(&cp->co_children); 537 } 538 539 /* 540 * Called just before free of an object 541 * of which smb_connobj is a part, i.e. 542 * _vc_free, _share_free, also sm_done. 543 */ 544 void 545 smb_co_done(struct smb_connobj *cp) 546 { 547 ASSERT(SLIST_EMPTY(&cp->co_children)); 548 mutex_destroy(&cp->co_lock); 549 } 550 551 static void 552 smb_co_addchild( 553 struct smb_connobj *parent, 554 struct smb_connobj *child) 555 { 556 557 /* 558 * Set the child's pointer to the parent. 559 * No references yet, so no need to lock. 560 */ 561 ASSERT(child->co_usecount == 1); 562 child->co_parent = parent; 563 564 /* 565 * Add the child to the parent's list of 566 * children, and in-line smb_co_hold 567 */ 568 ASSERT(MUTEX_HELD(&parent->co_lock)); 569 parent->co_usecount++; 570 SLIST_INSERT_HEAD(&parent->co_children, child, co_next); 571 } 572 573 void 574 smb_co_hold(struct smb_connobj *cp) 575 { 576 SMB_CO_LOCK(cp); 577 cp->co_usecount++; 578 SMB_CO_UNLOCK(cp); 579 } 580 581 /* 582 * Called via smb_vc_rele, smb_share_rele 583 */ 584 void 585 smb_co_rele(struct smb_connobj *co) 586 { 587 struct smb_connobj *parent; 588 int old_flags; 589 590 SMB_CO_LOCK(co); 591 if (co->co_usecount > 1) { 592 co->co_usecount--; 593 SMB_CO_UNLOCK(co); 594 return; 595 } 596 ASSERT(co->co_usecount == 1); 597 co->co_usecount = 0; 598 599 /* 600 * This list of children should be empty now. 601 * Check this while we're still linked, so 602 * we have a better chance of debugging. 603 */ 604 ASSERT(SLIST_EMPTY(&co->co_children)); 605 606 /* 607 * OK, this element is going away. 608 * 609 * We need to drop the lock on this CO so we can take the 610 * parent CO lock. The _GONE flag prevents this CO from 611 * getting new references before we can unlink it from the 612 * parent list. 613 * 614 * The _GONE flag is also used to ensure that the co_gone 615 * function is called only once. Note that smb_co_kill may 616 * do this before we get here. If we find that the _GONE 617 * flag was not already set, then call the co_gone hook 618 * (smb_share_gone, smb_vc_gone) which will disconnect 619 * the share or the VC, respectively. 620 * 621 * Note the old: smb_co_gone(co, scred); 622 * is now in-line here. 623 */ 624 old_flags = co->co_flags; 625 co->co_flags |= SMBO_GONE; 626 SMB_CO_UNLOCK(co); 627 628 if ((old_flags & SMBO_GONE) == 0 && co->co_gone) 629 co->co_gone(co); 630 631 /* 632 * If we have a parent (only smb_vclist does not) 633 * then unlink from parent's list of children. 634 * We have the only reference to the child. 635 */ 636 parent = co->co_parent; 637 if (parent) { 638 SMB_CO_LOCK(parent); 639 ASSERT(SLIST_FIRST(&parent->co_children)); 640 if (SLIST_FIRST(&parent->co_children)) { 641 SLIST_REMOVE(&parent->co_children, co, 642 smb_connobj, co_next); 643 } 644 SMB_CO_UNLOCK(parent); 645 } 646 647 /* 648 * Now it's safe to free the CO 649 */ 650 if (co->co_free) { 651 co->co_free(co); 652 } 653 654 /* 655 * Finally, if the CO had a parent, decrement 656 * the parent's hold count for the lost child. 657 */ 658 if (parent) { 659 /* 660 * Recursive call here (easier for debugging). 661 * Can only go two levels. 662 */ 663 smb_co_rele(parent); 664 } 665 } 666 667 /* 668 * Do just the first part of what co_gone does, 669 * i.e. tree disconnect, or disconnect a VC. 670 * This is used to forcibly close things. 671 */ 672 void 673 smb_co_kill(struct smb_connobj *co) 674 { 675 int old_flags; 676 677 SMB_CO_LOCK(co); 678 old_flags = co->co_flags; 679 co->co_flags |= SMBO_GONE; 680 SMB_CO_UNLOCK(co); 681 682 /* 683 * Do the same "call only once" logic here as in 684 * smb_co_rele, though it's probably not possible 685 * for this to be called after smb_co_rele. 686 */ 687 if ((old_flags & SMBO_GONE) == 0 && co->co_gone) 688 co->co_gone(co); 689 690 /* XXX: Walk list of children and kill those too? */ 691 } 692 693 694 /* 695 * Session implementation 696 */ 697 698 /* 699 * This sets the fields that are allowed to change 700 * when doing a reconnect. Many others are set in 701 * smb_vc_create and never change afterwards. 702 * Don't want domain or user to change here. 703 */ 704 int 705 smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred, 706 struct smb_vc *vcp, int is_ss) 707 { 708 int error, minauth; 709 710 /* Just save all the SMBVOPT_ options. */ 711 vcp->vc_vopt = vcspec->optflags; 712 713 /* Cleared if nego response shows antique server! */ 714 vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; 715 716 /* XXX: Odd place for this. */ 717 if (vcspec->optflags & SMBVOPT_EXT_SEC) 718 vcp->vc_hflags2 |= SMB_FLAGS2_EXT_SEC; 719 720 if (is_ss) { 721 /* Called from smb_sm_ssnsetup */ 722 723 if (vcspec->optflags & SMBVOPT_USE_KEYCHAIN) { 724 /* 725 * Get p/w hashes from the keychain. 726 * The password in vcspec->pass is 727 * fiction, so don't store it. 728 */ 729 error = smb_pkey_getpwh(vcp, scred->vc_ucred); 730 return (error); 731 } 732 733 /* 734 * Note: this can be called more than once 735 * for a given vcp, so free the old strings. 736 */ 737 SMB_STRFREE(vcp->vc_pass); 738 739 /* 740 * Don't store the cleartext password 741 * unless the minauth value was changed 742 * to allow use of cleartext passwords. 743 * (By default, this is not allowed.) 744 */ 745 minauth = vcspec->optflags & SMBVOPT_MINAUTH; 746 if (minauth == SMBVOPT_MINAUTH_NONE) 747 vcp->vc_pass = smb_strdup(vcspec->pass); 748 749 /* Compute LM and NTLM hashes. */ 750 smb_oldlm_hash(vcspec->pass, vcp->vc_lmhash); 751 smb_ntlmv1hash(vcspec->pass, vcp->vc_nthash); 752 } 753 754 /* Success! */ 755 error = 0; 756 return (error); 757 } 758 759 /*ARGSUSED*/ 760 int 761 smb_vc_create(struct smb_vcspec *vcspec, 762 struct smb_cred *scred, struct smb_vc **vcpp) 763 { 764 static char objtype[] = "smb_vc"; 765 struct smb_vc *vcp; 766 int error = 0; 767 768 ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); 769 770 /* 771 * Checks for valid uid/gid are now in 772 * smb_usr_ioc2vcspec, so at this point 773 * we know the user has right to create 774 * with the uid/gid in the vcspec. 775 */ 776 777 vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP); 778 779 smb_co_init(VCTOCP(vcp), SMBL_VC, objtype); 780 vcp->vc_co.co_free = smb_vc_free; 781 vcp->vc_co.co_gone = smb_vc_gone; 782 783 cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL); 784 sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL); 785 rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL); 786 cv_init(&vcp->iod_exit, objtype, CV_DRIVER, NULL); 787 788 vcp->vc_number = atomic_inc_uint_nv(&smb_vcnext); 789 vcp->vc_state = SMBIOD_ST_NOTCONN; 790 vcp->vc_timo = SMB_DEFRQTIMO; 791 /* 792 * I think SMB_UID_UNKNOWN is not the correct 793 * initial value for vc_smbuid. See the long 794 * comment in smb_iod_sendrq() 795 */ 796 vcp->vc_smbuid = SMB_UID_UNKNOWN; /* XXX should be zero */ 797 vcp->vc_tdesc = &smb_tran_nbtcp_desc; 798 799 /* 800 * These identify the connection. 801 */ 802 vcp->vc_zoneid = getzoneid(); 803 vcp->vc_uid = vcspec->owner; 804 vcp->vc_grp = vcspec->group; 805 vcp->vc_mode = vcspec->rights & SMBM_MASK; 806 807 vcp->vc_domain = smb_strdup(vcspec->domain); 808 vcp->vc_username = smb_strdup(vcspec->username); 809 vcp->vc_srvname = smb_strdup(vcspec->srvname); 810 vcp->vc_paddr = smb_dup_sockaddr(vcspec->sap); 811 vcp->vc_laddr = smb_dup_sockaddr(vcspec->lap); 812 813 #ifdef NOICONVSUPPORT 814 /* 815 * REVISIT 816 */ 817 error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); 818 if (error) 819 goto errout; 820 821 error = iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper); 822 if (error) 823 goto errout; 824 825 if (vcspec->servercs[0]) { 826 827 error = iconv_open(vcspec->servercs, vcspec->localcs, 828 &vcp->vc_toserver); 829 if (error) 830 goto errout; 831 832 error = iconv_open(vcspec->localcs, vcspec->servercs, 833 &vcp->vc_tolocal); 834 if (error) 835 goto errout; 836 } 837 #endif /* NOICONVSUPPORT */ 838 839 /* This fills in vcp->vc_tdata */ 840 if ((error = SMB_TRAN_CREATE(vcp, curproc)) != 0) 841 goto errout; 842 843 /* Success! */ 844 smb_co_addchild(&smb_vclist, VCTOCP(vcp)); 845 *vcpp = vcp; 846 return (0); 847 848 errout: 849 /* 850 * This will destroy the new vc. 851 * See: smb_vc_free 852 */ 853 smb_vc_rele(vcp); 854 return (error); 855 } 856 857 void 858 smb_vc_hold(struct smb_vc *vcp) 859 { 860 smb_co_hold(VCTOCP(vcp)); 861 } 862 863 void 864 smb_vc_rele(struct smb_vc *vcp) 865 { 866 smb_co_rele(VCTOCP(vcp)); 867 } 868 869 void 870 smb_vc_kill(struct smb_vc *vcp) 871 { 872 smb_co_kill(VCTOCP(vcp)); 873 } 874 875 /* 876 * Normally called via smb_vc_rele() 877 * after co_usecount drops to zero. 878 * Also called via: smb_vc_kill() 879 * 880 * Shutdown the VC to this server, 881 * invalidate shares linked with it. 882 */ 883 /*ARGSUSED*/ 884 static void 885 smb_vc_gone(struct smb_connobj *cp) 886 { 887 struct smb_vc *vcp = CPTOVC(cp); 888 889 /* 890 * Was smb_vc_disconnect(vcp); 891 */ 892 smb_iod_disconnect(vcp); 893 894 /* Note: smb_iod_destroy in vc_free */ 895 } 896 897 static void 898 smb_vc_free(struct smb_connobj *cp) 899 { 900 struct smb_vc *vcp = CPTOVC(cp); 901 902 /* 903 * The VC has no more references, so 904 * no locks should be needed here. 905 * Make sure the IOD is gone. 906 */ 907 smb_iod_destroy(vcp); 908 909 if (vcp->vc_tdata) 910 SMB_TRAN_DONE(vcp, curproc); 911 912 SMB_STRFREE(vcp->vc_username); 913 SMB_STRFREE(vcp->vc_srvname); 914 SMB_STRFREE(vcp->vc_pass); 915 SMB_STRFREE(vcp->vc_domain); 916 if (vcp->vc_paddr) { 917 smb_free_sockaddr(vcp->vc_paddr); 918 vcp->vc_paddr = NULL; 919 } 920 if (vcp->vc_laddr) { 921 smb_free_sockaddr(vcp->vc_laddr); 922 vcp->vc_laddr = NULL; 923 } 924 925 /* 926 * We are not using the iconv routines here. So commenting them for now. 927 * REVISIT. 928 */ 929 #ifdef NOTYETDEFINED 930 if (vcp->vc_tolower) 931 iconv_close(vcp->vc_tolower); 932 if (vcp->vc_toupper) 933 iconv_close(vcp->vc_toupper); 934 if (vcp->vc_tolocal) 935 iconv_close(vcp->vc_tolocal); 936 if (vcp->vc_toserver) 937 iconv_close(vcp->vc_toserver); 938 #endif 939 if (vcp->vc_intok) 940 kmem_free(vcp->vc_intok, vcp->vc_intoklen); 941 if (vcp->vc_outtok) 942 kmem_free(vcp->vc_outtok, vcp->vc_outtoklen); 943 if (vcp->vc_negtok) 944 kmem_free(vcp->vc_negtok, vcp->vc_negtoklen); 945 946 cv_destroy(&vcp->iod_exit); 947 rw_destroy(&vcp->iod_rqlock); 948 sema_destroy(&vcp->vc_sendlock); 949 cv_destroy(&vcp->vc_statechg); 950 smb_co_done(VCTOCP(vcp)); 951 kmem_free(vcp, sizeof (*vcp)); 952 } 953 954 955 /* 956 * Lookup share in the given VC. Share referenced and locked on return. 957 * VC expected to be locked on entry and will be left locked on exit. 958 */ 959 /*ARGSUSED*/ 960 int 961 smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, 962 struct smb_cred *scred, struct smb_share **sspp) 963 { 964 struct smb_connobj *co; 965 struct smb_share *ssp = NULL; 966 967 ASSERT(MUTEX_HELD(&vcp->vc_lock)); 968 969 *sspp = NULL; 970 971 /* var, head, next_field */ 972 SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { 973 ssp = CPTOSS(co); 974 975 /* No new refs if _GONE is set. */ 976 if (ssp->ss_flags & SMBS_GONE) 977 continue; 978 979 /* This has a hold, so no need to lock it. */ 980 if (strcmp(ssp->ss_name, shspec->name) == 0) 981 goto found; 982 } 983 return (ENOENT); 984 985 found: 986 /* Return it with a hold. */ 987 smb_share_hold(ssp); 988 *sspp = ssp; 989 return (0); 990 } 991 992 993 static char smb_emptypass[] = ""; 994 995 const char * 996 smb_vc_getpass(struct smb_vc *vcp) 997 { 998 if (vcp->vc_pass) 999 return (vcp->vc_pass); 1000 return (smb_emptypass); 1001 } 1002 1003 uint16_t 1004 smb_vc_nextmid(struct smb_vc *vcp) 1005 { 1006 uint16_t r; 1007 1008 r = atomic_inc_16_nv(&vcp->vc_mid); 1009 return (r); 1010 } 1011 1012 /* 1013 * Get a pointer to the IP address suitable for passing to Trusted 1014 * Extensions find_tpc() routine. Used by smbfs_mount_label_policy(). 1015 * Compare this code to nfs_mount_label_policy() if problems arise. 1016 * Without support for direct CIFS-over-TCP, we should always see 1017 * an AF_NETBIOS sockaddr here. 1018 */ 1019 void * 1020 smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers) 1021 { 1022 switch (vcp->vc_paddr->sa_family) { 1023 case AF_NETBIOS: { 1024 struct sockaddr_nb *snb; 1025 1026 *ipvers = IPV4_VERSION; 1027 /*LINTED*/ 1028 snb = (struct sockaddr_nb *)vcp->vc_paddr; 1029 return ((void *)&snb->snb_ipaddr); 1030 } 1031 case AF_INET: { 1032 struct sockaddr_in *sin; 1033 1034 *ipvers = IPV4_VERSION; 1035 /*LINTED*/ 1036 sin = (struct sockaddr_in *)vcp->vc_paddr; 1037 return ((void *)&sin->sin_addr); 1038 } 1039 case AF_INET6: { 1040 struct sockaddr_in6 *sin6; 1041 1042 *ipvers = IPV6_VERSION; 1043 /*LINTED*/ 1044 sin6 = (struct sockaddr_in6 *)vcp->vc_paddr; 1045 return ((void *)&sin6->sin6_addr); 1046 } 1047 default: 1048 SMBSDEBUG("invalid address family %d\n", 1049 vcp->vc_paddr->sa_family); 1050 *ipvers = 0; 1051 return (NULL); 1052 } 1053 } 1054 1055 /* 1056 * Share implementation 1057 */ 1058 /* 1059 * Allocate share structure and attach it to the given VC 1060 * Connection expected to be locked on entry. Share will be returned 1061 * in locked state. 1062 */ 1063 /*ARGSUSED*/ 1064 int 1065 smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, 1066 struct smb_cred *scred, struct smb_share **sspp) 1067 { 1068 static char objtype[] = "smb_ss"; 1069 struct smb_share *ssp; 1070 1071 ASSERT(MUTEX_HELD(&vcp->vc_lock)); 1072 1073 ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP); 1074 smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype); 1075 ssp->ss_co.co_free = smb_share_free; 1076 ssp->ss_co.co_gone = smb_share_gone; 1077 1078 ssp->ss_name = smb_strdup(shspec->name); 1079 ssp->ss_mount = NULL; 1080 if (shspec->pass && shspec->pass[0]) 1081 ssp->ss_pass = smb_strdup(shspec->pass); 1082 ssp->ss_type = shspec->stype; 1083 ssp->ss_tid = SMB_TID_UNKNOWN; 1084 ssp->ss_mode = shspec->rights & SMBM_MASK; 1085 ssp->ss_fsname = NULL; 1086 smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); 1087 *sspp = ssp; 1088 1089 return (0); 1090 } 1091 1092 /* 1093 * Normally called via smb_share_rele() 1094 * after co_usecount drops to zero. 1095 */ 1096 static void 1097 smb_share_free(struct smb_connobj *cp) 1098 { 1099 struct smb_share *ssp = CPTOSS(cp); 1100 1101 SMB_STRFREE(ssp->ss_name); 1102 SMB_STRFREE(ssp->ss_pass); 1103 SMB_STRFREE(ssp->ss_fsname); 1104 smb_co_done(SSTOCP(ssp)); 1105 kmem_free(ssp, sizeof (*ssp)); 1106 } 1107 1108 /* 1109 * Normally called via smb_share_rele() 1110 * after co_usecount drops to zero. 1111 * Also called via: smb_share_kill() 1112 */ 1113 static void 1114 smb_share_gone(struct smb_connobj *cp) 1115 { 1116 struct smb_cred scred; 1117 struct smb_share *ssp = CPTOSS(cp); 1118 1119 smb_credinit(&scred, curproc, NULL); 1120 smb_iod_shutdown_share(ssp); 1121 smb_smb_treedisconnect(ssp, &scred); 1122 smb_credrele(&scred); 1123 } 1124 1125 void 1126 smb_share_hold(struct smb_share *ssp) 1127 { 1128 smb_co_hold(SSTOCP(ssp)); 1129 } 1130 1131 void 1132 smb_share_rele(struct smb_share *ssp) 1133 { 1134 smb_co_rele(SSTOCP(ssp)); 1135 } 1136 1137 void 1138 smb_share_kill(struct smb_share *ssp) 1139 { 1140 smb_co_kill(SSTOCP(ssp)); 1141 } 1142 1143 1144 void 1145 smb_share_invalidate(struct smb_share *ssp) 1146 { 1147 ssp->ss_tid = SMB_TID_UNKNOWN; 1148 } 1149 1150 /* 1151 * Returns NON-zero if the share is valid. 1152 * Called with the share locked. 1153 */ 1154 int 1155 smb_share_valid(struct smb_share *ssp) 1156 { 1157 struct smb_vc *vcp = SSTOVC(ssp); 1158 1159 ASSERT(MUTEX_HELD(&ssp->ss_lock)); 1160 1161 if ((ssp->ss_flags & SMBS_CONNECTED) == 0) 1162 return (0); 1163 1164 if (ssp->ss_tid == SMB_TID_UNKNOWN) { 1165 SMBIODEBUG("found TID unknown\n"); 1166 ssp->ss_flags &= ~SMBS_CONNECTED; 1167 } 1168 1169 if (ssp->ss_vcgenid != vcp->vc_genid) { 1170 SMBIODEBUG("wrong genid\n"); 1171 ssp->ss_flags &= ~SMBS_CONNECTED; 1172 } 1173 1174 return (ssp->ss_flags & SMBS_CONNECTED); 1175 } 1176 1177 /* 1178 * Connect (or reconnect) a share object. 1179 * Called with the share locked. 1180 */ 1181 int 1182 smb_share_tcon(struct smb_share *ssp) 1183 { 1184 struct smb_vc *vcp = SSTOVC(ssp); 1185 clock_t tmo; 1186 int error; 1187 1188 ASSERT(MUTEX_HELD(&ssp->ss_lock)); 1189 1190 if (ssp->ss_flags & SMBS_CONNECTED) { 1191 SMBIODEBUG("alread connected?"); 1192 return (0); 1193 } 1194 1195 /* 1196 * Wait for completion of any state changes 1197 * that might be underway. 1198 */ 1199 while (ssp->ss_flags & SMBS_RECONNECTING) { 1200 ssp->ss_conn_waiters++; 1201 tmo = cv_wait_sig(&ssp->ss_conn_done, &ssp->ss_lock); 1202 ssp->ss_conn_waiters--; 1203 if (tmo == 0) { 1204 /* Interrupt! */ 1205 return (EINTR); 1206 } 1207 } 1208 1209 /* Did someone else do it for us? */ 1210 if (ssp->ss_flags & SMBS_CONNECTED) 1211 return (0); 1212 1213 /* 1214 * OK, we'll do the work. 1215 */ 1216 ssp->ss_flags |= SMBS_RECONNECTING; 1217 1218 /* Drop the lock while doing the call. */ 1219 SMB_SS_UNLOCK(ssp); 1220 error = smb_smb_treeconnect(ssp, &vcp->vc_scred); 1221 SMB_SS_LOCK(ssp); 1222 1223 if (!error) 1224 ssp->ss_flags |= SMBS_CONNECTED; 1225 ssp->ss_flags &= ~SMBS_RECONNECTING; 1226 1227 /* They can all go ahead! */ 1228 if (ssp->ss_conn_waiters) 1229 cv_broadcast(&ssp->ss_conn_done); 1230 1231 return (error); 1232 } 1233 1234 const char * 1235 smb_share_getpass(struct smb_share *ssp) 1236 { 1237 struct smb_vc *vcp; 1238 1239 if (ssp->ss_pass) 1240 return (ssp->ss_pass); 1241 vcp = SSTOVC(ssp); 1242 if (vcp->vc_pass) 1243 return (vcp->vc_pass); 1244 return (smb_emptypass); 1245 } 1246 1247 int 1248 smb_share_count(void) 1249 { 1250 struct smb_connobj *covc, *coss; 1251 struct smb_vc *vcp; 1252 zoneid_t zoneid = getzoneid(); 1253 int nshares = 0; 1254 1255 SMB_CO_LOCK(&smb_vclist); 1256 SLIST_FOREACH(covc, &smb_vclist.co_children, co_next) { 1257 vcp = CPTOVC(covc); 1258 1259 /* VCs in other zones are invisibile. */ 1260 if (vcp->vc_zoneid != zoneid) 1261 continue; 1262 1263 SMB_VC_LOCK(vcp); 1264 1265 /* var, head, next_field */ 1266 SLIST_FOREACH(coss, &(VCTOCP(vcp)->co_children), co_next) { 1267 nshares++; 1268 } 1269 1270 SMB_VC_UNLOCK(vcp); 1271 } 1272 SMB_CO_UNLOCK(&smb_vclist); 1273 1274 return (nshares); 1275 } 1276 1277 /* 1278 * Solaris zones support 1279 */ 1280 /*ARGSUSED*/ 1281 void 1282 lingering_vc(struct smb_vc *vc) 1283 { 1284 /* good place for a breakpoint */ 1285 DEBUG_ENTER("lingering VC"); 1286 } 1287 1288 /* 1289 * On zone shutdown, kill any IOD threads still running in this zone. 1290 */ 1291 /* ARGSUSED */ 1292 void 1293 nsmb_zone_shutdown(zoneid_t zoneid, void *data) 1294 { 1295 struct smb_connobj *co; 1296 struct smb_vc *vcp; 1297 1298 SMB_CO_LOCK(&smb_vclist); 1299 SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { 1300 vcp = CPTOVC(co); 1301 1302 if (vcp->vc_zoneid != zoneid) 1303 continue; 1304 1305 /* 1306 * This will close the connection, and 1307 * cause the IOD thread to terminate. 1308 */ 1309 smb_vc_kill(vcp); 1310 } 1311 SMB_CO_UNLOCK(&smb_vclist); 1312 } 1313 1314 /* 1315 * On zone destroy, kill any IOD threads and free all resources they used. 1316 */ 1317 /* ARGSUSED */ 1318 void 1319 nsmb_zone_destroy(zoneid_t zoneid, void *data) 1320 { 1321 struct smb_connobj *co; 1322 struct smb_vc *vcp; 1323 1324 /* 1325 * We will repeat what should have already happened 1326 * in zone_shutdown to make things go away. 1327 * 1328 * There should have been an smb_vc_rele call 1329 * by now for all VCs in the zone. If not, 1330 * there's probably more we needed to do in 1331 * the shutdown call. 1332 */ 1333 1334 SMB_CO_LOCK(&smb_vclist); 1335 1336 if (smb_vclist.co_usecount > 1) { 1337 SMBERROR("%d connections still active\n", 1338 smb_vclist.co_usecount - 1); 1339 } 1340 1341 /* var, head, next_field */ 1342 SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { 1343 vcp = CPTOVC(co); 1344 1345 if (vcp->vc_zoneid != zoneid) 1346 continue; 1347 1348 /* Debugging */ 1349 lingering_vc(vcp); 1350 } 1351 1352 SMB_CO_UNLOCK(&smb_vclist); 1353 } 1354