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