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