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