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