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