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