1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 26 */ 27 28 /* 29 * This file defines the domain environment values and the domain 30 * database interface. The database is a single linked list of 31 * structures containing domain type, name and SID information. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/list.h> 37 #include <stdio.h> 38 #include <strings.h> 39 #include <string.h> 40 #include <syslog.h> 41 #include <unistd.h> 42 #include <stdlib.h> 43 #include <synch.h> 44 #include <pwd.h> 45 #include <grp.h> 46 #include <assert.h> 47 48 #include <smbsrv/smbinfo.h> 49 #include <smbsrv/string.h> 50 #include <smbsrv/smb_sid.h> 51 #include <smbsrv/libsmb.h> 52 53 #define SMB_DOMAINS_FILE "domains" 54 55 #define SMB_DCACHE_UPDATE_WAIT 45 /* seconds */ 56 57 /* 58 * Domain cache states 59 */ 60 #define SMB_DCACHE_STATE_NONE 0 61 #define SMB_DCACHE_STATE_READY 1 62 #define SMB_DCACHE_STATE_UPDATING 2 63 #define SMB_DCACHE_STATE_DESTROYING 3 64 65 /* 66 * Cache lock modes 67 */ 68 #define SMB_DCACHE_RDLOCK 0 69 #define SMB_DCACHE_WRLOCK 1 70 71 typedef struct smb_domain_cache { 72 list_t dc_cache; 73 rwlock_t dc_cache_lck; 74 uint32_t dc_state; 75 uint32_t dc_nops; 76 mutex_t dc_mtx; 77 cond_t dc_cv; 78 /* domain controller information */ 79 cond_t dc_dci_cv; 80 boolean_t dc_dci_valid; 81 smb_dcinfo_t dc_dci; 82 } smb_domain_cache_t; 83 84 static smb_domain_cache_t smb_dcache; 85 86 static uint32_t smb_domain_add(smb_domain_type_t, smb_domain_t *); 87 static uint32_t smb_domain_add_local(void); 88 static uint32_t smb_domain_add_primary(uint32_t); 89 static void smb_domain_unlink(void); 90 91 static void smb_dcache_create(void); 92 static void smb_dcache_destroy(void); 93 static uint32_t smb_dcache_lock(int); 94 static void smb_dcache_unlock(void); 95 static void smb_dcache_remove(smb_domain_t *); 96 static uint32_t smb_dcache_add(smb_domain_t *); 97 static boolean_t smb_dcache_getdc(smb_dcinfo_t *, boolean_t); 98 static void smb_dcache_setdc(const smb_dcinfo_t *); 99 static boolean_t smb_dcache_wait(void); 100 static uint32_t smb_dcache_updating(void); 101 static void smb_dcache_ready(void); 102 103 /* 104 * domain cache one time initialization. This function should 105 * only be called during service startup. 106 * 107 * Returns 0 on success and an error code on failure. 108 */ 109 int 110 smb_domain_init(uint32_t secmode) 111 { 112 smb_domain_t di; 113 int rc; 114 115 smb_dcache_create(); 116 117 if ((rc = smb_domain_add_local()) != 0) 118 return (rc); 119 120 bzero(&di, sizeof (di)); 121 smb_domain_set_basic_info(NT_BUILTIN_DOMAIN_SIDSTR, "BUILTIN", "", &di); 122 (void) smb_domain_add(SMB_DOMAIN_BUILTIN, &di); 123 124 return (smb_domain_add_primary(secmode)); 125 } 126 127 /* 128 * Destroys the cache upon service termination 129 */ 130 void 131 smb_domain_fini(void) 132 { 133 smb_dcache_destroy(); 134 smb_domain_unlink(); 135 } 136 137 /* 138 * Add a domain structure to domain cache. There is no checking 139 * for duplicates. 140 */ 141 static uint32_t 142 smb_domain_add(smb_domain_type_t type, smb_domain_t *di) 143 { 144 uint32_t res; 145 146 if ((di == NULL) || (di->di_sid == NULL)) 147 return (SMB_DOMAIN_INVALID_ARG); 148 149 if ((res = smb_dcache_lock(SMB_DCACHE_WRLOCK)) == SMB_DOMAIN_SUCCESS) { 150 di->di_type = type; 151 res = smb_dcache_add(di); 152 smb_dcache_unlock(); 153 } 154 155 return (res); 156 } 157 158 /* 159 * Lookup a domain by its name. The passed name is the NETBIOS or fully 160 * qualified DNS name or non-qualified DNS name. 161 * 162 * If the requested domain is found and given 'di' pointer is not NULL 163 * it'll be filled with the domain information and B_TRUE is returned. 164 * If the caller only needs to check a domain existence it can pass 165 * NULL for 'di' and just check the return value. 166 * 167 * If the domain is not in the cache B_FALSE is returned. 168 */ 169 boolean_t 170 smb_domain_lookup_name(char *name, smb_domain_t *di) 171 { 172 boolean_t found = B_FALSE; 173 smb_domain_t *dcnode; 174 char *p; 175 176 bzero(di, sizeof (smb_domain_t)); 177 178 if (name == NULL || *name == '\0') 179 return (B_FALSE); 180 181 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 182 return (B_FALSE); 183 184 dcnode = list_head(&smb_dcache.dc_cache); 185 while (dcnode) { 186 found = (smb_strcasecmp(dcnode->di_nbname, name, 0) == 0) || 187 (smb_strcasecmp(dcnode->di_fqname, name, 0) == 0); 188 189 if (found) { 190 if (di) 191 *di = *dcnode; 192 break; 193 } 194 195 if ((p = strchr(dcnode->di_fqname, '.')) != NULL) { 196 *p = '\0'; 197 found = (smb_strcasecmp(dcnode->di_fqname, name, 198 0) == 0); 199 *p = '.'; 200 if (found) { 201 if (di) 202 *di = *dcnode; 203 break; 204 } 205 } 206 207 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 208 } 209 210 smb_dcache_unlock(); 211 return (found); 212 } 213 214 /* 215 * Lookup a domain by its SID. 216 * 217 * If the requested domain is found and given 'di' pointer is not NULL 218 * it'll be filled with the domain information and B_TRUE is returned. 219 * If the caller only needs to check a domain existence it can pass 220 * NULL for 'di' and just check the return value. 221 * 222 * If the domain is not in the cache B_FALSE is returned. 223 */ 224 boolean_t 225 smb_domain_lookup_sid(smb_sid_t *sid, smb_domain_t *di) 226 { 227 boolean_t found = B_FALSE; 228 smb_domain_t *dcnode; 229 char sidstr[SMB_SID_STRSZ]; 230 231 bzero(di, sizeof (smb_domain_t)); 232 233 if (sid == NULL) 234 return (B_FALSE); 235 236 smb_sid_tostr(sid, sidstr); 237 238 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 239 return (B_FALSE); 240 241 dcnode = list_head(&smb_dcache.dc_cache); 242 while (dcnode) { 243 found = (strcmp(dcnode->di_sid, sidstr) == 0); 244 if (found) { 245 if (di) 246 *di = *dcnode; 247 break; 248 } 249 250 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 251 } 252 253 smb_dcache_unlock(); 254 return (found); 255 } 256 257 /* 258 * Lookup a domain by its type. 259 * 260 * If the requested domain is found and given 'di' pointer is not NULL 261 * it'll be filled with the domain information and B_TRUE is returned. 262 * If the caller only needs to check a domain existence it can pass 263 * NULL for 'di' and just check the return value. 264 * 265 * If the domain is not in the cache B_FALSE is returned. 266 */ 267 boolean_t 268 smb_domain_lookup_type(smb_domain_type_t type, smb_domain_t *di) 269 { 270 boolean_t found = B_FALSE; 271 smb_domain_t *dcnode; 272 273 bzero(di, sizeof (smb_domain_t)); 274 275 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 276 return (B_FALSE); 277 278 dcnode = list_head(&smb_dcache.dc_cache); 279 while (dcnode) { 280 if (dcnode->di_type == type) { 281 found = B_TRUE; 282 if (di) 283 *di = *dcnode; 284 break; 285 } 286 287 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 288 } 289 290 smb_dcache_unlock(); 291 return (found); 292 } 293 294 /* 295 * Returns primary domain information plus the name of 296 * the selected domain controller. 297 * 298 * Returns TRUE on success. 299 */ 300 boolean_t 301 smb_domain_getinfo(smb_domainex_t *dxi) 302 { 303 boolean_t rv; 304 305 /* Note: this waits for the dcache lock. */ 306 rv = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary); 307 if (!rv) { 308 syslog(LOG_ERR, "smb_domain_getinfo: no primary domain"); 309 return (B_FALSE); 310 } 311 312 /* 313 * The 2nd arg TRUE means this will wait for DC info. 314 * 315 * Note that we do NOT hold the dcache rwlock here 316 * (not even as reader) because we already have what we 317 * need from the dcache (our primary domain) and we don't 318 * want to interfere with the DC locator which will take 319 * the dcache lock as writer to update the domain info. 320 */ 321 rv = smb_dcache_getdc(&dxi->d_dci, B_TRUE); 322 if (!rv) { 323 syslog(LOG_ERR, "smb_domain_getinfo: no DC info"); 324 return (B_FALSE); 325 } 326 327 return (B_TRUE); 328 } 329 330 /* 331 * Get the name of the current DC (if any) 332 * Does NOT block. 333 */ 334 void 335 smb_domain_current_dc(smb_dcinfo_t *dci) 336 { 337 (void) smb_dcache_getdc(dci, B_FALSE); 338 } 339 340 /* 341 * Transfer the cache to updating state. 342 * In this state any request for reading the cache would 343 * be blocked until the update is finished. 344 */ 345 uint32_t 346 smb_domain_start_update(void) 347 { 348 return (smb_dcache_updating()); 349 } 350 351 /* 352 * Transfer the cache from updating to ready state. 353 */ 354 void 355 smb_domain_end_update(void) 356 { 357 smb_dcache_ready(); 358 } 359 360 /* 361 * Mark the current domain controller (DC) info invalid 362 * until the DC locator calls smb_domain_update(). 363 */ 364 void 365 smb_domain_bad_dc(void) 366 { 367 (void) mutex_lock(&smb_dcache.dc_mtx); 368 smb_dcache.dc_dci_valid = B_FALSE; 369 (void) mutex_unlock(&smb_dcache.dc_mtx); 370 } 371 372 /* 373 * Updates the cache with given information for the primary 374 * domain, possible trusted domains and the selected domain 375 * controller. 376 * 377 * Before adding the new entries existing entries of type 378 * primary and trusted will be removed from cache. 379 */ 380 void 381 smb_domain_update(smb_domainex_t *dxi) 382 { 383 smb_domain_t *dcnode; 384 int i; 385 386 if (smb_dcache_lock(SMB_DCACHE_WRLOCK) != SMB_DOMAIN_SUCCESS) 387 return; 388 389 dcnode = list_head(&smb_dcache.dc_cache); 390 while (dcnode) { 391 if ((dcnode->di_type == SMB_DOMAIN_PRIMARY) || 392 (dcnode->di_type == SMB_DOMAIN_TRUSTED)) { 393 smb_dcache_remove(dcnode); 394 dcnode = list_head(&smb_dcache.dc_cache); 395 } else { 396 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 397 } 398 } 399 400 if (smb_dcache_add(&dxi->d_primary) == SMB_DOMAIN_SUCCESS) { 401 for (i = 0; i < dxi->d_trusted.td_num; i++) 402 (void) smb_dcache_add(&dxi->d_trusted.td_domains[i]); 403 404 smb_dcache_setdc(&dxi->d_dci); 405 } 406 407 smb_dcache_unlock(); 408 } 409 410 /* 411 * Write the list of domains to /var/run/smb/domains. 412 */ 413 void 414 smb_domain_save(void) 415 { 416 char fname[MAXPATHLEN]; 417 char tag; 418 smb_domain_t *domain; 419 FILE *fp; 420 struct passwd *pwd; 421 struct group *grp; 422 uid_t uid; 423 gid_t gid; 424 425 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 426 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 427 428 if ((fp = fopen(fname, "w")) == NULL) 429 return; 430 431 pwd = getpwnam("root"); 432 grp = getgrnam("sys"); 433 uid = (pwd == NULL) ? 0 : pwd->pw_uid; 434 gid = (grp == NULL) ? 3 : grp->gr_gid; 435 436 (void) lockf(fileno(fp), F_LOCK, 0); 437 (void) fchmod(fileno(fp), 0600); 438 (void) fchown(fileno(fp), uid, gid); 439 440 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 441 return; 442 443 domain = list_head(&smb_dcache.dc_cache); 444 while (domain) { 445 switch (domain->di_type) { 446 case SMB_DOMAIN_PRIMARY: 447 tag = '*'; 448 break; 449 450 case SMB_DOMAIN_TRUSTED: 451 case SMB_DOMAIN_UNTRUSTED: 452 tag = '-'; 453 break; 454 455 case SMB_DOMAIN_LOCAL: 456 tag = '.'; 457 break; 458 default: 459 domain = list_next(&smb_dcache.dc_cache, domain); 460 continue; 461 } 462 463 (void) fprintf(fp, "[%c] [%s] [%s]\n", 464 tag, domain->di_nbname, domain->di_sid); 465 466 domain = list_next(&smb_dcache.dc_cache, domain); 467 } 468 469 smb_dcache_unlock(); 470 (void) lockf(fileno(fp), F_ULOCK, 0); 471 (void) fclose(fp); 472 } 473 474 /* 475 * List the domains in /var/run/smb/domains. 476 */ 477 void 478 smb_domain_show(void) 479 { 480 char buf[MAXPATHLEN]; 481 char *p; 482 FILE *fp; 483 484 (void) snprintf(buf, MAXPATHLEN, "%s/%s", 485 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 486 487 if ((fp = fopen(buf, "r")) != NULL) { 488 (void) lockf(fileno(fp), F_LOCK, 0); 489 490 while (fgets(buf, MAXPATHLEN, fp) != NULL) { 491 if ((p = strchr(buf, '\n')) != NULL) 492 *p = '\0'; 493 (void) printf("%s\n", buf); 494 } 495 496 (void) lockf(fileno(fp), F_ULOCK, 0); 497 (void) fclose(fp); 498 } 499 } 500 501 void 502 smb_domain_set_basic_info(char *sid, char *nb_domain, char *fq_domain, 503 smb_domain_t *di) 504 { 505 if (sid == NULL || nb_domain == NULL || fq_domain == NULL || 506 di == NULL) 507 return; 508 509 (void) strlcpy(di->di_sid, sid, SMB_SID_STRSZ); 510 (void) strlcpy(di->di_nbname, nb_domain, NETBIOS_NAME_SZ); 511 (void) smb_strupr(di->di_nbname); 512 (void) strlcpy(di->di_fqname, fq_domain, MAXHOSTNAMELEN); 513 di->di_binsid = NULL; 514 } 515 516 void 517 smb_domain_set_dns_info(char *sid, char *nb_domain, char *fq_domain, 518 char *forest, char *guid, smb_domain_t *di) 519 { 520 if (di == NULL || forest == NULL || guid == NULL) 521 return; 522 523 /* Caller zeros out *di before this. */ 524 smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); 525 (void) strlcpy(di->di_u.di_dns.ddi_forest, forest, MAXHOSTNAMELEN); 526 (void) strlcpy(di->di_u.di_dns.ddi_guid, guid, 527 UUID_PRINTABLE_STRING_LENGTH); 528 } 529 530 void 531 smb_domain_set_trust_info(char *sid, char *nb_domain, char *fq_domain, 532 uint32_t trust_dir, uint32_t trust_type, uint32_t trust_attrs, 533 smb_domain_t *di) 534 { 535 smb_domain_trust_t *ti; 536 537 if (di == NULL) 538 return; 539 540 /* Caller zeros out *di before this. */ 541 di->di_type = SMB_DOMAIN_TRUSTED; 542 ti = &di->di_u.di_trust; 543 smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); 544 ti->dti_trust_direction = trust_dir; 545 ti->dti_trust_type = trust_type; 546 ti->dti_trust_attrs = trust_attrs; 547 } 548 549 /* 550 * Remove the /var/run/smb/domains file. 551 */ 552 static void 553 smb_domain_unlink(void) 554 { 555 char fname[MAXPATHLEN]; 556 557 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 558 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 559 (void) unlink(fname); 560 } 561 562 /* 563 * Add an entry for the local domain to the domain cache 564 */ 565 static uint32_t 566 smb_domain_add_local(void) 567 { 568 char *lsidstr; 569 char hostname[NETBIOS_NAME_SZ]; 570 char fq_name[MAXHOSTNAMELEN]; 571 smb_domain_t di; 572 573 if ((lsidstr = smb_config_get_localsid()) == NULL) 574 return (SMB_DOMAIN_NOMACHINE_SID); 575 576 if (smb_getnetbiosname(hostname, NETBIOS_NAME_SZ) != 0) { 577 free(lsidstr); 578 return (SMB_DOMAIN_NOMACHINE_SID); 579 } 580 581 bzero(&di, sizeof (di)); 582 *fq_name = '\0'; 583 (void) smb_getfqhostname(fq_name, MAXHOSTNAMELEN); 584 smb_domain_set_basic_info(lsidstr, hostname, fq_name, &di); 585 (void) smb_domain_add(SMB_DOMAIN_LOCAL, &di); 586 587 free(lsidstr); 588 return (SMB_DOMAIN_SUCCESS); 589 } 590 591 /* 592 * Add an entry for the primary domain to the domain cache 593 */ 594 static uint32_t 595 smb_domain_add_primary(uint32_t secmode) 596 { 597 char sidstr[SMB_SID_STRSZ]; 598 char fq_name[MAXHOSTNAMELEN]; 599 char nb_name[NETBIOS_NAME_SZ]; 600 smb_domain_t di; 601 int rc; 602 603 if (secmode != SMB_SECMODE_DOMAIN) 604 return (SMB_DOMAIN_SUCCESS); 605 606 rc = smb_config_getstr(SMB_CI_DOMAIN_SID, sidstr, sizeof (sidstr)); 607 if (rc != SMBD_SMF_OK) 608 return (SMB_DOMAIN_NODOMAIN_SID); 609 610 rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_name, NETBIOS_NAME_SZ); 611 if ((rc != SMBD_SMF_OK) || (*nb_name == '\0')) 612 return (SMB_DOMAIN_NODOMAIN_NAME); 613 614 bzero(&di, sizeof (di)); 615 (void) smb_getfqdomainname(fq_name, MAXHOSTNAMELEN); 616 smb_domain_set_basic_info(sidstr, nb_name, fq_name, &di); 617 (void) smb_domain_add(SMB_DOMAIN_PRIMARY, &di); 618 return (SMB_DOMAIN_SUCCESS); 619 } 620 621 /* 622 * Initialize the domain cache. 623 * This function does not populate the cache. 624 */ 625 static void 626 smb_dcache_create(void) 627 { 628 (void) mutex_lock(&smb_dcache.dc_mtx); 629 if (smb_dcache.dc_state != SMB_DCACHE_STATE_NONE) { 630 (void) mutex_unlock(&smb_dcache.dc_mtx); 631 return; 632 } 633 634 list_create(&smb_dcache.dc_cache, sizeof (smb_domain_t), 635 offsetof(smb_domain_t, di_lnd)); 636 637 smb_dcache.dc_nops = 0; 638 bzero(&smb_dcache.dc_dci, sizeof (smb_dcache.dc_dci)); 639 smb_dcache.dc_dci_valid = B_FALSE; 640 smb_dcache.dc_state = SMB_DCACHE_STATE_READY; 641 (void) mutex_unlock(&smb_dcache.dc_mtx); 642 } 643 644 /* 645 * Removes and frees all the cache entries 646 */ 647 static void 648 smb_dcache_flush(void) 649 { 650 smb_domain_t *di; 651 652 (void) rw_wrlock(&smb_dcache.dc_cache_lck); 653 while ((di = list_head(&smb_dcache.dc_cache)) != NULL) 654 smb_dcache_remove(di); 655 (void) rw_unlock(&smb_dcache.dc_cache_lck); 656 } 657 658 /* 659 * Destroys the cache. 660 */ 661 static void 662 smb_dcache_destroy(void) 663 { 664 (void) mutex_lock(&smb_dcache.dc_mtx); 665 if ((smb_dcache.dc_state == SMB_DCACHE_STATE_READY) || 666 (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING)) { 667 smb_dcache.dc_state = SMB_DCACHE_STATE_DESTROYING; 668 while (smb_dcache.dc_nops > 0) 669 (void) cond_wait(&smb_dcache.dc_cv, 670 &smb_dcache.dc_mtx); 671 672 smb_dcache_flush(); 673 list_destroy(&smb_dcache.dc_cache); 674 675 smb_dcache.dc_state = SMB_DCACHE_STATE_NONE; 676 } 677 (void) mutex_unlock(&smb_dcache.dc_mtx); 678 } 679 680 /* 681 * Lock the cache with the specified mode. 682 * If the cache is in updating state and a read lock is 683 * requested, the lock won't be granted until either the 684 * update is finished or SMB_DCACHE_UPDATE_WAIT has passed. 685 * 686 * Whenever a lock is granted, the number of inflight cache 687 * operations is incremented. 688 */ 689 static uint32_t 690 smb_dcache_lock(int mode) 691 { 692 (void) mutex_lock(&smb_dcache.dc_mtx); 693 switch (smb_dcache.dc_state) { 694 case SMB_DCACHE_STATE_NONE: 695 case SMB_DCACHE_STATE_DESTROYING: 696 default: 697 (void) mutex_unlock(&smb_dcache.dc_mtx); 698 return (SMB_DOMAIN_INTERNAL_ERR); 699 700 case SMB_DCACHE_STATE_UPDATING: 701 if (mode == SMB_DCACHE_RDLOCK) { 702 /* 703 * Read operations should wait until the update 704 * is completed. 705 */ 706 if (!smb_dcache_wait()) { 707 (void) mutex_unlock(&smb_dcache.dc_mtx); 708 return (SMB_DOMAIN_INTERNAL_ERR); 709 } 710 } 711 /* FALLTHROUGH */ 712 713 case SMB_DCACHE_STATE_READY: 714 smb_dcache.dc_nops++; 715 break; 716 } 717 (void) mutex_unlock(&smb_dcache.dc_mtx); 718 719 /* 720 * Lock has to be taken outside the mutex otherwise 721 * there could be a deadlock 722 */ 723 if (mode == SMB_DCACHE_RDLOCK) 724 (void) rw_rdlock(&smb_dcache.dc_cache_lck); 725 else 726 (void) rw_wrlock(&smb_dcache.dc_cache_lck); 727 728 return (SMB_DOMAIN_SUCCESS); 729 } 730 731 /* 732 * Decrement the number of inflight operations and then unlock. 733 */ 734 static void 735 smb_dcache_unlock(void) 736 { 737 (void) mutex_lock(&smb_dcache.dc_mtx); 738 assert(smb_dcache.dc_nops > 0); 739 smb_dcache.dc_nops--; 740 (void) cond_broadcast(&smb_dcache.dc_cv); 741 (void) mutex_unlock(&smb_dcache.dc_mtx); 742 743 (void) rw_unlock(&smb_dcache.dc_cache_lck); 744 } 745 746 static uint32_t 747 smb_dcache_add(smb_domain_t *di) 748 { 749 smb_domain_t *dcnode; 750 751 if ((dcnode = malloc(sizeof (smb_domain_t))) == NULL) 752 return (SMB_DOMAIN_NO_MEMORY); 753 754 *dcnode = *di; 755 dcnode->di_binsid = smb_sid_fromstr(dcnode->di_sid); 756 if (dcnode->di_binsid == NULL) { 757 free(dcnode); 758 return (SMB_DOMAIN_NO_MEMORY); 759 } 760 761 list_insert_tail(&smb_dcache.dc_cache, dcnode); 762 return (SMB_DOMAIN_SUCCESS); 763 } 764 765 static void 766 smb_dcache_remove(smb_domain_t *di) 767 { 768 list_remove(&smb_dcache.dc_cache, di); 769 smb_sid_free(di->di_binsid); 770 free(di); 771 } 772 773 static void 774 smb_dcache_setdc(const smb_dcinfo_t *dci) 775 { 776 (void) mutex_lock(&smb_dcache.dc_mtx); 777 smb_dcache.dc_dci = *dci; /* struct assignment! */ 778 smb_dcache.dc_dci_valid = B_TRUE; 779 (void) cond_broadcast(&smb_dcache.dc_dci_cv); 780 (void) mutex_unlock(&smb_dcache.dc_mtx); 781 } 782 783 /* 784 * Get information about our domain controller. If the wait arg 785 * is true, wait for the DC locator to finish before copying. 786 * Returns TRUE on success (have DC info). 787 */ 788 static boolean_t 789 smb_dcache_getdc(smb_dcinfo_t *dci, boolean_t wait) 790 { 791 timestruc_t to; 792 boolean_t rv; 793 int err; 794 795 to.tv_sec = time(NULL) + SMB_DCACHE_UPDATE_WAIT; 796 to.tv_nsec = 0; 797 798 (void) mutex_lock(&smb_dcache.dc_mtx); 799 800 while (wait && !smb_dcache.dc_dci_valid) { 801 err = cond_timedwait(&smb_dcache.dc_dci_cv, 802 &smb_dcache.dc_mtx, &to); 803 if (err == ETIME) 804 break; 805 } 806 *dci = smb_dcache.dc_dci; /* struct assignment! */ 807 rv = smb_dcache.dc_dci_valid; 808 809 (void) mutex_unlock(&smb_dcache.dc_mtx); 810 811 return (rv); 812 } 813 814 /* 815 * Waits for SMB_DCACHE_UPDATE_WAIT seconds if cache is in 816 * UPDATING state. Upon wake up returns true if cache is 817 * ready to be used, otherwise it returns false. 818 */ 819 static boolean_t 820 smb_dcache_wait(void) 821 { 822 timestruc_t to; 823 int err; 824 825 assert(MUTEX_HELD(&smb_dcache.dc_mtx)); 826 827 to.tv_sec = time(NULL) + SMB_DCACHE_UPDATE_WAIT; 828 to.tv_nsec = 0; 829 while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) { 830 err = cond_timedwait(&smb_dcache.dc_cv, 831 &smb_dcache.dc_mtx, &to); 832 if (err == ETIME) 833 break; 834 } 835 836 return (smb_dcache.dc_state == SMB_DCACHE_STATE_READY); 837 } 838 839 /* 840 * Transfers the cache into UPDATING state, this will ensure 841 * any read access to the cache will be stalled until the 842 * update is finished. This is to avoid providing incomplete, 843 * inconsistent or stale information. 844 * 845 * If another thread is already updating the cache, other 846 * callers will wait until cache is no longer in UPDATING 847 * state. The return code is decided based on the new 848 * state of the cache. 849 */ 850 static uint32_t 851 smb_dcache_updating(void) 852 { 853 uint32_t rc; 854 855 (void) mutex_lock(&smb_dcache.dc_mtx); 856 switch (smb_dcache.dc_state) { 857 case SMB_DCACHE_STATE_READY: 858 smb_dcache.dc_state = SMB_DCACHE_STATE_UPDATING; 859 rc = SMB_DOMAIN_SUCCESS; 860 break; 861 862 case SMB_DCACHE_STATE_UPDATING: 863 while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) 864 (void) cond_wait(&smb_dcache.dc_cv, 865 &smb_dcache.dc_mtx); 866 867 if (smb_dcache.dc_state == SMB_DCACHE_STATE_READY) { 868 smb_dcache.dc_state = SMB_DCACHE_STATE_UPDATING; 869 rc = SMB_DOMAIN_SUCCESS; 870 } else { 871 rc = SMB_DOMAIN_NO_CACHE; 872 } 873 break; 874 875 case SMB_DCACHE_STATE_NONE: 876 case SMB_DCACHE_STATE_DESTROYING: 877 rc = SMB_DOMAIN_NO_CACHE; 878 break; 879 880 default: 881 break; 882 } 883 884 (void) mutex_unlock(&smb_dcache.dc_mtx); 885 return (rc); 886 } 887 888 /* 889 * Transfers the cache from UPDATING to READY state. 890 * 891 * Nothing will happen if the cache is no longer available 892 * or it is being destroyed. 893 */ 894 static void 895 smb_dcache_ready(void) 896 { 897 (void) mutex_lock(&smb_dcache.dc_mtx); 898 switch (smb_dcache.dc_state) { 899 case SMB_DCACHE_STATE_UPDATING: 900 smb_dcache.dc_state = SMB_DCACHE_STATE_READY; 901 (void) cond_broadcast(&smb_dcache.dc_cv); 902 break; 903 904 case SMB_DCACHE_STATE_NONE: 905 case SMB_DCACHE_STATE_DESTROYING: 906 break; 907 908 default: 909 assert(0); 910 } 911 (void) mutex_unlock(&smb_dcache.dc_mtx); 912 } 913