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