xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_domain.c (revision bbf6f00c25b6a2bed23c35eac6d62998ecdb338c)
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