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