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