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