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