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
71typedef 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
84static smb_domain_cache_t smb_dcache;
85
86static uint32_t smb_domain_add(smb_domain_type_t, smb_domain_t *);
87static uint32_t smb_domain_add_local(void);
88static uint32_t smb_domain_add_primary(uint32_t);
89static void smb_domain_unlink(void);
90
91static void smb_dcache_create(void);
92static void smb_dcache_destroy(void);
93static uint32_t smb_dcache_lock(int);
94static void smb_dcache_unlock(void);
95static void smb_dcache_remove(smb_domain_t *);
96static uint32_t smb_dcache_add(smb_domain_t *);
97static boolean_t smb_dcache_getdc(smb_dcinfo_t *, boolean_t);
98static void smb_dcache_setdc(const smb_dcinfo_t *);
99static boolean_t smb_dcache_wait(void);
100static uint32_t smb_dcache_updating(void);
101static 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 */
109int
110smb_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 */
130void
131smb_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 */
141static uint32_t
142smb_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 */
169boolean_t
170smb_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 */
224boolean_t
225smb_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 */
267boolean_t
268smb_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 */
300boolean_t
301smb_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 */
334void
335smb_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 */
345uint32_t
346smb_domain_start_update(void)
347{
348	return (smb_dcache_updating());
349}
350
351/*
352 * Transfer the cache from updating to ready state.
353 */
354void
355smb_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 */
364void
365smb_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 */
380void
381smb_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 */
413void
414smb_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 */
477void
478smb_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
501void
502smb_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
516void
517smb_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
530void
531smb_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 */
552static void
553smb_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 */
565static uint32_t
566smb_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 */
594static uint32_t
595smb_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 */
625static void
626smb_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 */
647static void
648smb_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 */
661static void
662smb_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 */
689static uint32_t
690smb_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 */
734static void
735smb_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
746static uint32_t
747smb_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
765static void
766smb_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
773static void
774smb_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 */
788static boolean_t
789smb_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 */
819static boolean_t
820smb_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 */
850static uint32_t
851smb_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 */
894static void
895smb_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