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