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