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