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