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 write 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 call 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 }