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 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 /*
28 * Active Directory Auto-Discovery.
29 *
30 * This [project private] API allows the caller to provide whatever
31 * details it knows a priori (i.e., provided via configuration so as to
32 * override auto-discovery) and in any order. Then the caller can ask
33 * for any of the auto-discoverable parameters in any order.
34 *
35 * But there is an actual order in which discovery must be done. Given
36 * the discovery mechanism implemented here, that order is:
37 *
38 * - the domain name joined must be discovered first
39 * - then the domain controllers
40 * - then the forest name and site name
41 * - then the global catalog servers, and site-specific domain
42 * controllers and global catalog servers.
43 *
44 * The API does not require it be called in the same order because there
45 * may be other discovery mechanisms in the future, and exposing
46 * ordering requirements of the current mechanism now can create trouble
47 * down the line. Also, this makes the API easier to use now, which
48 * means less work to do some day when we make this a public API.
49 *
50 * Domain discovery is done by res_nsearch() of the DNS SRV RR name for
51 * domain controllers. As long as the joined domain appears in the DNS
52 * resolver's search list then we'll find it.
53 *
54 * Domain controller discovery is a matter of formatting the DNS SRV RR
55 * FQDN for domain controllers and doing a lookup for them. Knowledge
56 * of the domain name is not fundamentally required, but we separate the
57 * two processes, which in practice can lead to one more DNS lookup than
58 * is strictly required.
59 *
60 * Forest and site name discovery require an LDAP search of the AD
61 * "configuration partition" at a domain controller for the joined
62 * domain. Forest and site name discovery depend on knowing the joined
63 * domain name and domain controllers for that domain.
64 *
65 * Global catalog server discovery requires knowledge of the forest
66 * name in order to format the DNS SRV RR FQDN to lookup. Site-specific
67 * domain controller discovery depends on knowing the site name (and,
68 * therefore, joined domain, ...). Site-specific global catalog server
69 * discovery depends on knowledge of the forest and site names, which
70 * depend on...
71 *
72 * All the work of discovering particular items is done by functions
73 * named validate_<item>(). Each such function calls validate_<item>()
74 * for any items that it depends on.
75 *
76 * This API is not thread-safe.
77 */
78
79
80 #include <stdio.h>
81 #include <string.h>
82 #include <strings.h>
83 #include <unistd.h>
84 #include <assert.h>
85 #include <stdlib.h>
86 #include <net/if.h>
87 #include <sys/types.h>
88 #include <sys/socket.h>
89 #include <sys/sockio.h>
90 #include <netinet/in.h>
91 #include <arpa/inet.h>
92 #include <arpa/nameser.h>
93 #include <resolv.h>
94 #include <netdb.h>
95 #include <ctype.h>
96 #include <errno.h>
97 #include <ldap.h>
98 #include <note.h>
99 #include <sasl/sasl.h>
100 #include <sys/u8_textprep.h>
101 #include <syslog.h>
102 #include <uuid/uuid.h>
103 #include <ads/dsgetdc.h>
104 #include "adutils_impl.h"
105 #include "addisc_impl.h"
106
107 /*
108 * These set some sanity policies for discovery. After a discovery
109 * cycle, we will consider the results (successful or unsuccessful)
110 * to be valid for at least MINIMUM_TTL seconds, and for at most
111 * MAXIMUM_TTL seconds. Note that the caller is free to request
112 * discovery cycles sooner than MINIMUM_TTL if it has reason to believe
113 * that the situation has changed.
114 */
115 #define MINIMUM_TTL (5 * 60)
116 #define MAXIMUM_TTL (20 * 60)
117
118
119 #define DNS_MAX_NAME NS_MAXDNAME
120
121 #define GC_PORT 3268
122
123 /* SRV RR names for various queries */
124 #define LDAP_SRV_HEAD "_ldap._tcp."
125 #define SITE_SRV_MIDDLE "%s._sites."
126 #define GC_SRV_TAIL "gc._msdcs"
127 #define DC_SRV_TAIL "dc._msdcs"
128 #define ALL_GC_SRV_TAIL "_gc._tcp"
129 #define PDC_SRV "_ldap._tcp.pdc._msdcs.%s"
130
131 /* A RR name for all GCs -- last resort this works */
132 #define GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
133
134
135 /*
136 * We try res_ninit() whenever we don't have one. res_ninit() fails if
137 * idmapd is running before the network is up!
138 */
139 #define DO_RES_NINIT(ctx) \
140 if (!(ctx)->res_ninitted) \
141 (void) do_res_ninit(ctx)
142
143 #define DO_GETNAMEINFO(b, l, s) \
144 if (ad_disc_getnameinfo(b, l, s) != 0) \
145 (void) strlcpy(b, "?", l)
146
147 #define DEBUG1STATUS(ctx, ...) do { \
148 if (DBG(DISC, 1)) \
149 logger(LOG_DEBUG, __VA_ARGS__); \
150 if (ctx->status_fp) { \
151 (void) fprintf(ctx->status_fp, __VA_ARGS__); \
152 (void) fprintf(ctx->status_fp, "\n"); \
153 } \
154 _NOTE(CONSTCOND) \
155 } while (0)
156
157 #define is_fixed(item) \
158 ((item)->state == AD_STATE_FIXED)
159
160 #define is_changed(item, num, param) \
161 ((item)->param_version[num] != (param)->version)
162
163 void * uuid_dup(void *);
164
165 static ad_item_t *validate_SiteName(ad_disc_t ctx);
166 static ad_item_t *validate_PreferredDC(ad_disc_t ctx);
167
168 /*
169 * Function definitions
170 */
171
172
173 static int
do_res_ninit(ad_disc_t ctx)174 do_res_ninit(ad_disc_t ctx)
175 {
176 int rc;
177
178 rc = res_ninit(&ctx->res_state);
179 if (rc != 0) {
180 if (DBG(DNS, 0))
181 logger(LOG_INFO, "res_ninit failed: %d", rc);
182 return (rc);
183 }
184 ctx->res_ninitted = 1;
185 /*
186 * The SRV records returnd by AD can be larger than 512 bytes,
187 * so we'd like to use TCP for those searches. Unfortunately,
188 * the TCP connect timeout seen by the resolver is very long
189 * (more than a couple minutes) and we can't wait that long.
190 * Don't do use TCP until we can override the timeout.
191 *
192 * Note that some queries will try TCP anyway.
193 */
194 #if 0
195 ctx->res_state.options |= RES_USEVC;
196 #endif
197 return (0);
198 }
199
200 /*
201 * Private getnameinfo(3socket) variant tailored to our needs.
202 */
203 int
ad_disc_getnameinfo(char * obuf,int olen,struct sockaddr_storage * ss)204 ad_disc_getnameinfo(char *obuf, int olen, struct sockaddr_storage *ss)
205 {
206 struct sockaddr *sa;
207 int eai, slen;
208
209 sa = (void *)ss;
210 switch (sa->sa_family) {
211 case AF_INET:
212 slen = sizeof (struct sockaddr_in);
213 break;
214 case AF_INET6:
215 slen = sizeof (struct sockaddr_in6);
216 break;
217 default:
218 return (EAI_FAMILY);
219 }
220
221 eai = getnameinfo(sa, slen, obuf, olen, NULL, 0, NI_NUMERICHOST);
222
223 return (eai);
224 }
225
226 static void
update_version(ad_item_t * item,int num,ad_item_t * param)227 update_version(ad_item_t *item, int num, ad_item_t *param)
228 {
229 item->param_version[num] = param->version;
230 }
231
232
233
234 static boolean_t
is_valid(ad_item_t * item)235 is_valid(ad_item_t *item)
236 {
237 if (item->value != NULL) {
238 if (item->state == AD_STATE_FIXED)
239 return (B_TRUE);
240 if (item->state == AD_STATE_AUTO &&
241 (item->expires == 0 || item->expires > time(NULL)))
242 return (B_TRUE);
243 }
244 return (B_FALSE);
245 }
246
247
248 static void
update_item(ad_item_t * item,void * value,enum ad_item_state state,uint32_t ttl)249 update_item(ad_item_t *item, void *value, enum ad_item_state state,
250 uint32_t ttl)
251 {
252 if (item->value != NULL && value != NULL) {
253 if ((item->type == AD_STRING &&
254 strcmp(item->value, value) != 0) ||
255 (item->type == AD_UUID &&
256 ad_disc_compare_uuid(item->value, value) != 0)||
257 (item->type == AD_DIRECTORY &&
258 ad_disc_compare_ds(item->value, value) != 0)||
259 (item->type == AD_DOMAINS_IN_FOREST &&
260 ad_disc_compare_domainsinforest(item->value, value) != 0) ||
261 (item->type == AD_TRUSTED_DOMAINS &&
262 ad_disc_compare_trusteddomains(item->value, value) != 0))
263 item->version++;
264 } else if (item->value != value)
265 item->version++;
266
267 if (item->value != NULL)
268 free(item->value);
269
270 item->value = value;
271 item->state = state;
272
273 if (ttl == 0)
274 item->expires = 0;
275 else
276 item->expires = time(NULL) + ttl;
277 }
278
279 /* Compare UUIDs */
280 int
ad_disc_compare_uuid(uuid_t * u1,uuid_t * u2)281 ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2)
282 {
283 int rc;
284
285 rc = memcmp(u1, u2, UUID_LEN);
286 return (rc);
287 }
288
289 void *
uuid_dup(void * src)290 uuid_dup(void *src)
291 {
292 void *dst;
293 dst = malloc(UUID_LEN);
294 if (dst != NULL)
295 (void) memcpy(dst, src, UUID_LEN);
296 return (dst);
297 }
298
299 /* Compare DS lists */
300 int
ad_disc_compare_ds(ad_disc_ds_t * ds1,ad_disc_ds_t * ds2)301 ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2)
302 {
303 int i, j;
304 int num_ds1;
305 int num_ds2;
306 boolean_t match;
307
308 for (i = 0; ds1[i].host[0] != '\0'; i++)
309 continue;
310 num_ds1 = i;
311 for (j = 0; ds2[j].host[0] != '\0'; j++)
312 continue;
313 num_ds2 = j;
314 if (num_ds1 != num_ds2)
315 return (1);
316
317 for (i = 0; i < num_ds1; i++) {
318 match = B_FALSE;
319 for (j = 0; j < num_ds2; j++) {
320 if (strcmp(ds1[i].host, ds2[j].host) == 0 &&
321 ds1[i].port == ds2[j].port) {
322 match = B_TRUE;
323 break;
324 }
325 }
326 if (!match)
327 return (1);
328 }
329 return (0);
330 }
331
332
333 /* Copy a list of DSs */
334 static ad_disc_ds_t *
ds_dup(const ad_disc_ds_t * srv)335 ds_dup(const ad_disc_ds_t *srv)
336 {
337 int i;
338 int size;
339 ad_disc_ds_t *new = NULL;
340
341 for (i = 0; srv[i].host[0] != '\0'; i++)
342 continue;
343
344 size = (i + 1) * sizeof (ad_disc_ds_t);
345 new = malloc(size);
346 if (new != NULL)
347 (void) memcpy(new, srv, size);
348 return (new);
349 }
350
351
352 int
ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t * td1,ad_disc_trusteddomains_t * td2)353 ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
354 ad_disc_trusteddomains_t *td2)
355 {
356 int i, j;
357 int num_td1;
358 int num_td2;
359 boolean_t match;
360
361 for (i = 0; td1[i].domain[0] != '\0'; i++)
362 continue;
363 num_td1 = i;
364
365 for (j = 0; td2[j].domain[0] != '\0'; j++)
366 continue;
367 num_td2 = j;
368
369 if (num_td1 != num_td2)
370 return (1);
371
372 for (i = 0; i < num_td1; i++) {
373 match = B_FALSE;
374 for (j = 0; j < num_td2; j++) {
375 if (domain_eq(td1[i].domain, td2[j].domain)) {
376 match = B_TRUE;
377 break;
378 }
379 }
380 if (!match)
381 return (1);
382 }
383 return (0);
384 }
385
386
387
388 /* Copy a list of Trusted Domains */
389 static ad_disc_trusteddomains_t *
td_dup(const ad_disc_trusteddomains_t * td)390 td_dup(const ad_disc_trusteddomains_t *td)
391 {
392 int i;
393 int size;
394 ad_disc_trusteddomains_t *new = NULL;
395
396 for (i = 0; td[i].domain[0] != '\0'; i++)
397 continue;
398
399 size = (i + 1) * sizeof (ad_disc_trusteddomains_t);
400 new = malloc(size);
401 if (new != NULL)
402 (void) memcpy(new, td, size);
403 return (new);
404 }
405
406
407
408 int
ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t * df1,ad_disc_domainsinforest_t * df2)409 ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1,
410 ad_disc_domainsinforest_t *df2)
411 {
412 int i, j;
413 int num_df1;
414 int num_df2;
415 boolean_t match;
416
417 for (i = 0; df1[i].domain[0] != '\0'; i++)
418 continue;
419 num_df1 = i;
420
421 for (j = 0; df2[j].domain[0] != '\0'; j++)
422 continue;
423 num_df2 = j;
424
425 if (num_df1 != num_df2)
426 return (1);
427
428 for (i = 0; i < num_df1; i++) {
429 match = B_FALSE;
430 for (j = 0; j < num_df2; j++) {
431 if (domain_eq(df1[i].domain, df2[j].domain) &&
432 strcmp(df1[i].sid, df2[j].sid) == 0) {
433 match = B_TRUE;
434 break;
435 }
436 }
437 if (!match)
438 return (1);
439 }
440 return (0);
441 }
442
443
444
445 /* Copy a list of Trusted Domains */
446 static ad_disc_domainsinforest_t *
df_dup(const ad_disc_domainsinforest_t * df)447 df_dup(const ad_disc_domainsinforest_t *df)
448 {
449 int i;
450 int size;
451 ad_disc_domainsinforest_t *new = NULL;
452
453 for (i = 0; df[i].domain[0] != '\0'; i++)
454 continue;
455
456 size = (i + 1) * sizeof (ad_disc_domainsinforest_t);
457 new = malloc(size);
458 if (new != NULL)
459 (void) memcpy(new, df, size);
460 return (new);
461 }
462
463
464
465
466
467 /*
468 * Returns an array of IPv4 address/prefix length
469 * The last subnet is NULL
470 */
471 static ad_subnet_t *
find_subnets()472 find_subnets()
473 {
474 int sock, n, i;
475 struct lifconf lifc;
476 struct lifreq lifr, *lifrp;
477 struct lifnum lifn;
478 uint32_t prefix_len;
479 char *s;
480 ad_subnet_t *results;
481
482 lifrp = &lifr;
483
484 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
485 logger(LOG_ERR, "Failed to open IPv4 socket for "
486 "listing network interfaces (%s)", strerror(errno));
487 return (NULL);
488 }
489
490 lifn.lifn_family = AF_INET;
491 lifn.lifn_flags = 0;
492 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
493 logger(LOG_ERR,
494 "Failed to find the number of network interfaces (%s)",
495 strerror(errno));
496 (void) close(sock);
497 return (NULL);
498 }
499
500 if (lifn.lifn_count < 1) {
501 logger(LOG_ERR, "No IPv4 network interfaces found");
502 (void) close(sock);
503 return (NULL);
504 }
505
506 lifc.lifc_family = AF_INET;
507 lifc.lifc_flags = 0;
508 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
509 lifc.lifc_buf = malloc(lifc.lifc_len);
510
511 if (lifc.lifc_buf == NULL) {
512 logger(LOG_ERR, "Out of memory");
513 (void) close(sock);
514 return (NULL);
515 }
516
517 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
518 logger(LOG_ERR, "Failed to list network interfaces (%s)",
519 strerror(errno));
520 free(lifc.lifc_buf);
521 (void) close(sock);
522 return (NULL);
523 }
524
525 n = lifc.lifc_len / (int)sizeof (struct lifreq);
526
527 if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) {
528 free(lifc.lifc_buf);
529 (void) close(sock);
530 return (NULL);
531 }
532
533 for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) {
534 if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0)
535 continue;
536
537 if ((lifrp->lifr_flags & IFF_UP) == 0)
538 continue;
539
540 if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0)
541 continue;
542
543 prefix_len = lifrp->lifr_addrlen;
544
545 s = inet_ntoa(((struct sockaddr_in *)
546 &lifrp->lifr_addr)->sin_addr);
547
548 (void) snprintf(results[i].subnet, sizeof (ad_subnet_t),
549 "%s/%d", s, prefix_len);
550 }
551
552 free(lifc.lifc_buf);
553 (void) close(sock);
554
555 return (results);
556 }
557
558 static int
cmpsubnets(ad_subnet_t * subnets1,ad_subnet_t * subnets2)559 cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2)
560 {
561 int num_subnets1;
562 int num_subnets2;
563 boolean_t matched;
564 int i, j;
565
566 for (i = 0; subnets1[i].subnet[0] != '\0'; i++)
567 continue;
568 num_subnets1 = i;
569
570 for (i = 0; subnets2[i].subnet[0] != '\0'; i++)
571 continue;
572 num_subnets2 = i;
573
574 if (num_subnets1 != num_subnets2)
575 return (1);
576
577 for (i = 0; i < num_subnets1; i++) {
578 matched = B_FALSE;
579 for (j = 0; j < num_subnets2; j++) {
580 if (strcmp(subnets1[i].subnet,
581 subnets2[j].subnet) == 0) {
582 matched = B_TRUE;
583 break;
584 }
585 }
586 if (!matched)
587 return (1);
588 }
589 return (0);
590 }
591
592
593
594
595 /* Convert a DN's DC components into a DNS domainname */
596 char *
DN_to_DNS(const char * dn_name)597 DN_to_DNS(const char *dn_name)
598 {
599 char dns[DNS_MAX_NAME];
600 char *dns_name;
601 int i, j;
602 int num = 0;
603
604 j = 0;
605 i = 0;
606
607 if (dn_name == NULL)
608 return (NULL);
609 /*
610 * Find all DC=<value> and form DNS name of the
611 * form <value1>.<value2>...
612 */
613 while (dn_name[i] != '\0') {
614 if (strncasecmp(&dn_name[i], "DC=", 3) == 0) {
615 i += 3;
616 if (dn_name[i] != '\0' && num > 0)
617 dns[j++] = '.';
618 while (dn_name[i] != '\0' &&
619 dn_name[i] != ',' && dn_name[i] != '+')
620 dns[j++] = dn_name[i++];
621 num++;
622 } else {
623 /* Skip attr=value as it is not DC= */
624 while (dn_name[i] != '\0' &&
625 dn_name[i] != ',' && dn_name[i] != '+')
626 i++;
627 }
628 /* Skip over separator ',' or '+' */
629 if (dn_name[i] != '\0') i++;
630 }
631 dns[j] = '\0';
632 dns_name = malloc(j + 1);
633 if (dns_name != NULL)
634 (void) strlcpy(dns_name, dns, j + 1);
635 return (dns_name);
636 }
637
638
639 /*
640 * A utility function to bind to a Directory server
641 */
642
643 static
644 LDAP *
ldap_lookup_init(ad_disc_ds_t * ds)645 ldap_lookup_init(ad_disc_ds_t *ds)
646 {
647 int i;
648 int rc, ldversion;
649 int zero = 0;
650 int timeoutms = 5 * 1000;
651 char *saslmech = "GSSAPI";
652 uint32_t saslflags = LDAP_SASL_INTERACTIVE;
653 LDAP *ld = NULL;
654
655 for (i = 0; ds[i].host[0] != '\0'; i++) {
656 if (DBG(LDAP, 2)) {
657 logger(LOG_DEBUG, "adutils: ldap_lookup_init, host %s",
658 ds[i].host);
659 }
660
661 ld = ldap_init(ds[i].host, ds[i].port);
662 if (ld == NULL) {
663 if (DBG(LDAP, 1)) {
664 logger(LOG_DEBUG,
665 "Couldn't connect to AD DC %s:%d (%s)",
666 ds[i].host, ds[i].port,
667 strerror(errno));
668 }
669 continue;
670 }
671
672 ldversion = LDAP_VERSION3;
673 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
674 &ldversion);
675 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
676 LDAP_OPT_OFF);
677 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
678 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
679 /* setup TCP/IP connect timeout */
680 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
681 &timeoutms);
682 (void) ldap_set_option(ld, LDAP_OPT_RESTART,
683 LDAP_OPT_ON);
684
685 rc = adutils_set_thread_functions(ld);
686 if (rc != LDAP_SUCCESS) {
687 /* Error has already been logged */
688 (void) ldap_unbind(ld);
689 ld = NULL;
690 continue;
691 }
692
693 rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
694 saslmech, NULL, NULL, saslflags, &saslcallback,
695 NULL /* defaults */);
696 if (rc == LDAP_SUCCESS)
697 break;
698
699 if (DBG(LDAP, 0)) {
700 logger(LOG_INFO, "LDAP: %s:%d: %s",
701 ds[i].host, ds[i].port, ldap_err2string(rc));
702 ldap_perror(ld, ds[i].host);
703 }
704 (void) ldap_unbind(ld);
705 ld = NULL;
706 }
707 return (ld);
708 }
709
710
711
712 /*
713 * Lookup the trusted domains in the global catalog.
714 *
715 * Returns:
716 * array of trusted domains which is terminated by
717 * an empty trusted domain.
718 * NULL an error occured
719 */
720 ad_disc_trusteddomains_t *
ldap_lookup_trusted_domains(LDAP ** ld,ad_disc_ds_t * globalCatalog,char * base_dn)721 ldap_lookup_trusted_domains(LDAP **ld, ad_disc_ds_t *globalCatalog,
722 char *base_dn)
723 {
724 int scope = LDAP_SCOPE_SUBTREE;
725 char *attrs[3];
726 int rc;
727 LDAPMessage *results = NULL;
728 LDAPMessage *entry;
729 char *filter;
730 char **partner = NULL;
731 char **direction = NULL;
732 int num = 0;
733 ad_disc_trusteddomains_t *trusted_domains = NULL;
734
735 if (DBG(DISC, 1))
736 logger(LOG_DEBUG, "Looking for trusted domains...");
737
738 if (*ld == NULL)
739 *ld = ldap_lookup_init(globalCatalog);
740
741 if (*ld == NULL) {
742 logger(LOG_ERR, "adutils: ldap_lookup_init failed");
743 return (NULL);
744 }
745
746 attrs[0] = "trustPartner";
747 attrs[1] = "trustDirection";
748 attrs[2] = NULL;
749
750 /*
751 * Trust direction values:
752 * 1 - inbound (they trust us)
753 * 2 - outbound (we trust them)
754 * 3 - bidirectional (we trust each other)
755 */
756 filter = "(&(objectclass=trustedDomain)"
757 "(|(trustDirection=3)(trustDirection=2)))";
758
759 rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results);
760 if (DBG(DISC, 1))
761 logger(LOG_DEBUG, "Trusted domains:");
762 if (rc == LDAP_SUCCESS) {
763 for (entry = ldap_first_entry(*ld, results);
764 entry != NULL; entry = ldap_next_entry(*ld, entry)) {
765 partner = ldap_get_values(*ld, entry, "trustPartner");
766 direction = ldap_get_values(
767 *ld, entry, "trustDirection");
768
769 if (partner != NULL && direction != NULL) {
770 if (DBG(DISC, 1)) {
771 logger(LOG_DEBUG, " %s (%s)",
772 partner[0], direction[0]);
773 }
774 num++;
775 void *tmp = realloc(trusted_domains,
776 (num + 1) *
777 sizeof (ad_disc_trusteddomains_t));
778 if (tmp == NULL) {
779 free(trusted_domains);
780 ldap_value_free(partner);
781 ldap_value_free(direction);
782 (void) ldap_msgfree(results);
783 return (NULL);
784 }
785 trusted_domains = tmp;
786 /* Last element should be zero */
787 (void) memset(&trusted_domains[num], 0,
788 sizeof (ad_disc_trusteddomains_t));
789 (void) strcpy(trusted_domains[num - 1].domain,
790 partner[0]);
791 trusted_domains[num - 1].direction =
792 atoi(direction[0]);
793 }
794 if (partner != NULL)
795 ldap_value_free(partner);
796 if (direction != NULL)
797 ldap_value_free(direction);
798 }
799 } else if (rc == LDAP_NO_RESULTS_RETURNED) {
800 /* This is not an error - return empty trusted domain */
801 trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
802 if (DBG(DISC, 1))
803 logger(LOG_DEBUG, " not found");
804 } else {
805 if (DBG(DISC, 1))
806 logger(LOG_DEBUG, " rc=%d", rc);
807 }
808 if (results != NULL)
809 (void) ldap_msgfree(results);
810
811 return (trusted_domains);
812 }
813
814
815 /*
816 * This functions finds all the domains in a forest.
817 */
818 ad_disc_domainsinforest_t *
ldap_lookup_domains_in_forest(LDAP ** ld,ad_disc_ds_t * globalCatalogs)819 ldap_lookup_domains_in_forest(LDAP **ld, ad_disc_ds_t *globalCatalogs)
820 {
821 static char *attrs[] = {
822 "objectSid",
823 NULL,
824 };
825 int rc;
826 LDAPMessage *result = NULL;
827 LDAPMessage *entry;
828 int ndomains = 0;
829 int nresults;
830 ad_disc_domainsinforest_t *domains = NULL;
831
832 if (DBG(DISC, 1))
833 logger(LOG_DEBUG, "Looking for domains in forest...");
834
835 if (*ld == NULL)
836 *ld = ldap_lookup_init(globalCatalogs);
837
838 if (*ld == NULL) {
839 logger(LOG_ERR, "adutils: ldap_lookup_init failed");
840 return (NULL);
841 }
842
843 /* Find domains */
844 rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
845 "(objectClass=Domain)", attrs, 0, &result);
846 if (rc != LDAP_SUCCESS) {
847 logger(LOG_ERR, "adutils: ldap_search, rc=%d", rc);
848 goto err;
849 }
850 if (DBG(DISC, 1))
851 logger(LOG_DEBUG, "Domains in forest:");
852
853 nresults = ldap_count_entries(*ld, result);
854 domains = calloc(nresults + 1, sizeof (*domains));
855 if (domains == NULL) {
856 if (DBG(DISC, 1))
857 logger(LOG_DEBUG, " (nomem)");
858 goto err;
859 }
860
861 for (entry = ldap_first_entry(*ld, result);
862 entry != NULL;
863 entry = ldap_next_entry(*ld, entry)) {
864 struct berval **sid_ber;
865 adutils_sid_t sid;
866 char *sid_str;
867 char *name;
868 char *dn;
869
870 sid_ber = ldap_get_values_len(*ld, entry,
871 "objectSid");
872 if (sid_ber == NULL)
873 continue;
874
875 rc = adutils_getsid(sid_ber[0], &sid);
876 ldap_value_free_len(sid_ber);
877 if (rc < 0)
878 goto err;
879
880 if ((sid_str = adutils_sid2txt(&sid)) == NULL)
881 goto err;
882
883 (void) strcpy(domains[ndomains].sid, sid_str);
884 free(sid_str);
885
886 dn = ldap_get_dn(*ld, entry);
887 name = DN_to_DNS(dn);
888 free(dn);
889 if (name == NULL)
890 goto err;
891
892 (void) strcpy(domains[ndomains].domain, name);
893 free(name);
894
895 if (DBG(DISC, 1))
896 logger(LOG_DEBUG, " %s", domains[ndomains].domain);
897
898 ndomains++;
899 }
900
901 if (ndomains == 0) {
902 if (DBG(DISC, 1))
903 logger(LOG_DEBUG, " not found");
904 goto err;
905 }
906
907 if (ndomains < nresults) {
908 ad_disc_domainsinforest_t *tmp;
909 tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
910 if (tmp == NULL)
911 goto err;
912 domains = tmp;
913 }
914
915 if (result != NULL)
916 (void) ldap_msgfree(result);
917
918 return (domains);
919
920 err:
921 free(domains);
922 if (result != NULL)
923 (void) ldap_msgfree(result);
924 return (NULL);
925 }
926
927
928 ad_disc_t
ad_disc_init(void)929 ad_disc_init(void)
930 {
931 struct ad_disc *ctx;
932 ctx = calloc(1, sizeof (struct ad_disc));
933 if (ctx != NULL)
934 DO_RES_NINIT(ctx);
935
936 ctx->domain_name.type = AD_STRING;
937 ctx->domain_guid.type = AD_UUID;
938 ctx->domain_controller.type = AD_DIRECTORY;
939 ctx->preferred_dc.type = AD_DIRECTORY;
940 ctx->site_name.type = AD_STRING;
941 ctx->forest_name.type = AD_STRING;
942 ctx->global_catalog.type = AD_DIRECTORY;
943 ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST;
944 ctx->trusted_domains.type = AD_TRUSTED_DOMAINS;
945 /* Site specific versions */
946 ctx->site_domain_controller.type = AD_DIRECTORY;
947 ctx->site_global_catalog.type = AD_DIRECTORY;
948 return (ctx);
949 }
950
951 void
ad_disc_fini(ad_disc_t ctx)952 ad_disc_fini(ad_disc_t ctx)
953 {
954 if (ctx == NULL)
955 return;
956
957 if (ctx->res_ninitted)
958 res_ndestroy(&ctx->res_state);
959
960 if (ctx->subnets != NULL)
961 free(ctx->subnets);
962
963 if (ctx->domain_name.value != NULL)
964 free(ctx->domain_name.value);
965
966 if (ctx->domain_guid.value != NULL)
967 free(ctx->domain_guid.value);
968
969 if (ctx->domain_controller.value != NULL)
970 free(ctx->domain_controller.value);
971
972 if (ctx->preferred_dc.value != NULL)
973 free(ctx->preferred_dc.value);
974
975 if (ctx->site_name.value != NULL)
976 free(ctx->site_name.value);
977
978 if (ctx->forest_name.value != NULL)
979 free(ctx->forest_name.value);
980
981 if (ctx->global_catalog.value != NULL)
982 free(ctx->global_catalog.value);
983
984 if (ctx->domains_in_forest.value != NULL)
985 free(ctx->domains_in_forest.value);
986
987 if (ctx->trusted_domains.value != NULL)
988 free(ctx->trusted_domains.value);
989
990 /* Site specific versions */
991 if (ctx->site_domain_controller.value != NULL)
992 free(ctx->site_domain_controller.value);
993
994 if (ctx->site_global_catalog.value != NULL)
995 free(ctx->site_global_catalog.value);
996
997 free(ctx);
998 }
999
1000 void
ad_disc_refresh(ad_disc_t ctx)1001 ad_disc_refresh(ad_disc_t ctx)
1002 {
1003 if (ctx->res_ninitted) {
1004 res_ndestroy(&ctx->res_state);
1005 ctx->res_ninitted = 0;
1006 }
1007 (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state));
1008 DO_RES_NINIT(ctx);
1009
1010 if (ctx->domain_name.state == AD_STATE_AUTO)
1011 ctx->domain_name.state = AD_STATE_INVALID;
1012
1013 if (ctx->domain_guid.state == AD_STATE_AUTO)
1014 ctx->domain_guid.state = AD_STATE_INVALID;
1015
1016 if (ctx->domain_controller.state == AD_STATE_AUTO)
1017 ctx->domain_controller.state = AD_STATE_INVALID;
1018
1019 if (ctx->preferred_dc.state == AD_STATE_AUTO)
1020 ctx->preferred_dc.state = AD_STATE_INVALID;
1021
1022 if (ctx->site_name.state == AD_STATE_AUTO)
1023 ctx->site_name.state = AD_STATE_INVALID;
1024
1025 if (ctx->forest_name.state == AD_STATE_AUTO)
1026 ctx->forest_name.state = AD_STATE_INVALID;
1027
1028 if (ctx->global_catalog.state == AD_STATE_AUTO)
1029 ctx->global_catalog.state = AD_STATE_INVALID;
1030
1031 if (ctx->domains_in_forest.state == AD_STATE_AUTO)
1032 ctx->domains_in_forest.state = AD_STATE_INVALID;
1033
1034 if (ctx->trusted_domains.state == AD_STATE_AUTO)
1035 ctx->trusted_domains.state = AD_STATE_INVALID;
1036
1037 if (ctx->site_domain_controller.state == AD_STATE_AUTO)
1038 ctx->site_domain_controller.state = AD_STATE_INVALID;
1039
1040 if (ctx->site_global_catalog.state == AD_STATE_AUTO)
1041 ctx->site_global_catalog.state = AD_STATE_INVALID;
1042 }
1043
1044
1045 /*
1046 * Called when the discovery cycle is done. Sets a master TTL
1047 * that will avoid doing new time-based discoveries too soon after
1048 * the last discovery cycle. Most interesting when the discovery
1049 * cycle failed, because then the TTLs on the individual items will
1050 * not be updated and may go stale.
1051 */
1052 void
ad_disc_done(ad_disc_t ctx)1053 ad_disc_done(ad_disc_t ctx)
1054 {
1055 time_t now = time(NULL);
1056
1057 ctx->expires_not_before = now + MINIMUM_TTL;
1058 ctx->expires_not_after = now + MAXIMUM_TTL;
1059 }
1060
1061 static void
log_cds(ad_disc_t ctx,ad_disc_cds_t * cds)1062 log_cds(ad_disc_t ctx, ad_disc_cds_t *cds)
1063 {
1064 char buf[INET6_ADDRSTRLEN];
1065 struct addrinfo *ai;
1066
1067 if (!DBG(DISC, 1) && ctx->status_fp == NULL)
1068 return;
1069
1070 DEBUG1STATUS(ctx, "Candidate servers:");
1071 if (cds->cds_ds.host[0] == '\0') {
1072 DEBUG1STATUS(ctx, " (empty list)");
1073 return;
1074 }
1075
1076 while (cds->cds_ds.host[0] != '\0') {
1077
1078 DEBUG1STATUS(ctx, " %s p=%d w=%d",
1079 cds->cds_ds.host,
1080 cds->cds_ds.priority,
1081 cds->cds_ds.weight);
1082
1083 ai = cds->cds_ai;
1084 if (ai == NULL) {
1085 DEBUG1STATUS(ctx, " (no address)");
1086 }
1087 while (ai != NULL) {
1088 int eai;
1089
1090 eai = getnameinfo(ai->ai_addr, ai->ai_addrlen,
1091 buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
1092 if (eai != 0)
1093 (void) strlcpy(buf, "?", sizeof (buf));
1094
1095 DEBUG1STATUS(ctx, " %s", buf);
1096 ai = ai->ai_next;
1097 }
1098 cds++;
1099 }
1100 }
1101
1102 static void
log_ds(ad_disc_t ctx,ad_disc_ds_t * ds)1103 log_ds(ad_disc_t ctx, ad_disc_ds_t *ds)
1104 {
1105 char buf[INET6_ADDRSTRLEN];
1106
1107 if (!DBG(DISC, 1) && ctx->status_fp == NULL)
1108 return;
1109
1110 DEBUG1STATUS(ctx, "Responding servers:");
1111 if (ds->host[0] == '\0') {
1112 DEBUG1STATUS(ctx, " (empty list)");
1113 return;
1114 }
1115
1116 while (ds->host[0] != '\0') {
1117
1118 DEBUG1STATUS(ctx, " %s", ds->host);
1119 DO_GETNAMEINFO(buf, sizeof (buf), &ds->addr);
1120 DEBUG1STATUS(ctx, " %s", buf);
1121
1122 ds++;
1123 }
1124 }
1125
1126 /* Discover joined Active Directory domainName */
1127 static ad_item_t *
validate_DomainName(ad_disc_t ctx)1128 validate_DomainName(ad_disc_t ctx)
1129 {
1130 char *dname, *srvname;
1131 int len, rc;
1132
1133 if (is_valid(&ctx->domain_name))
1134 return (&ctx->domain_name);
1135
1136
1137 /* Try to find our domain by searching for DCs for it */
1138 DO_RES_NINIT(ctx);
1139 if (DBG(DISC, 1))
1140 logger(LOG_DEBUG, "Looking for our AD domain name...");
1141 rc = srv_getdom(&ctx->res_state,
1142 LDAP_SRV_HEAD DC_SRV_TAIL, &srvname);
1143
1144 /*
1145 * If we can't find DCs by via res_nsearch() then there's no
1146 * point in trying anything else to discover the AD domain name.
1147 */
1148 if (rc < 0) {
1149 if (DBG(DISC, 1))
1150 logger(LOG_DEBUG, "Can't find our domain name.");
1151 return (NULL);
1152 }
1153
1154 /*
1155 * We have the FQDN of the SRV RR name, so now we extract the
1156 * domainname suffix from it.
1157 */
1158 dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) +
1159 1 /* for the dot between RR name and domainname */);
1160
1161 free(srvname);
1162
1163 if (dname == NULL) {
1164 logger(LOG_ERR, "Out of memory");
1165 return (NULL);
1166 }
1167
1168 /* Eat any trailing dot */
1169 len = strlen(dname);
1170 if (len > 0 && dname[len - 1] == '.')
1171 dname[len - 1] = '\0';
1172
1173 if (DBG(DISC, 1))
1174 logger(LOG_DEBUG, "Our domain name: %s", dname);
1175
1176 /*
1177 * There is no "time to live" on the discovered domain,
1178 * so passing zero as TTL here, making it non-expiring.
1179 * Note that current consumers do not auto-discover the
1180 * domain name, though a future installer could.
1181 */
1182 update_item(&ctx->domain_name, dname, AD_STATE_AUTO, 0);
1183
1184 return (&ctx->domain_name);
1185 }
1186
1187
1188 char *
ad_disc_get_DomainName(ad_disc_t ctx,boolean_t * auto_discovered)1189 ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered)
1190 {
1191 char *domain_name = NULL;
1192 ad_item_t *domain_name_item;
1193
1194 domain_name_item = validate_DomainName(ctx);
1195
1196 if (domain_name_item) {
1197 domain_name = strdup(domain_name_item->value);
1198 if (auto_discovered != NULL)
1199 *auto_discovered =
1200 (domain_name_item->state == AD_STATE_AUTO);
1201 } else if (auto_discovered != NULL)
1202 *auto_discovered = B_FALSE;
1203
1204 return (domain_name);
1205 }
1206
1207
1208 /* Discover domain controllers */
1209 static ad_item_t *
validate_DomainController(ad_disc_t ctx,enum ad_disc_req req)1210 validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
1211 {
1212 ad_disc_ds_t *dc = NULL;
1213 ad_disc_cds_t *cdc = NULL;
1214 boolean_t validate_global = B_FALSE;
1215 boolean_t validate_site = B_FALSE;
1216 ad_item_t *domain_name_item;
1217 char *domain_name;
1218 ad_item_t *site_name_item = NULL;
1219 char *site_name;
1220 ad_item_t *prefer_dc_item;
1221 ad_disc_ds_t *prefer_dc = NULL;
1222
1223 /* If the values is fixed there will not be a site specific version */
1224 if (is_fixed(&ctx->domain_controller))
1225 return (&ctx->domain_controller);
1226
1227 domain_name_item = validate_DomainName(ctx);
1228 if (domain_name_item == NULL) {
1229 DEBUG1STATUS(ctx, "(no domain name)");
1230 return (NULL);
1231 }
1232 domain_name = (char *)domain_name_item->value;
1233
1234 /* Get (optional) preferred DC. */
1235 prefer_dc_item = validate_PreferredDC(ctx);
1236 if (prefer_dc_item != NULL)
1237 prefer_dc = prefer_dc_item->value;
1238
1239 if (req == AD_DISC_GLOBAL)
1240 validate_global = B_TRUE;
1241 else {
1242 if (is_fixed(&ctx->site_name))
1243 validate_site = B_TRUE;
1244 if (req == AD_DISC_PREFER_SITE)
1245 validate_global = B_TRUE;
1246 }
1247
1248 /*
1249 * If we're trying both site-specific and global,
1250 * try the site-specific first, then fall-back.
1251 */
1252 if (validate_site) {
1253 site_name_item = &ctx->site_name;
1254 site_name = (char *)site_name_item->value;
1255
1256 if (!is_valid(&ctx->site_domain_controller) ||
1257 is_changed(&ctx->site_domain_controller, PARAM1,
1258 domain_name_item) ||
1259 is_changed(&ctx->site_domain_controller, PARAM2,
1260 site_name_item)) {
1261 char rr_name[DNS_MAX_NAME];
1262
1263 /*
1264 * Lookup DNS SRV RR named
1265 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName>
1266 */
1267 DEBUG1STATUS(ctx, "DNS SRV query, dom=%s, site=%s",
1268 domain_name, site_name);
1269 (void) snprintf(rr_name, sizeof (rr_name),
1270 LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL,
1271 site_name);
1272 DO_RES_NINIT(ctx);
1273 cdc = srv_query(&ctx->res_state, rr_name,
1274 domain_name, prefer_dc);
1275
1276 if (cdc == NULL) {
1277 DEBUG1STATUS(ctx, "(no DNS response)");
1278 goto try_global;
1279 }
1280 log_cds(ctx, cdc);
1281
1282 /*
1283 * Filter out unresponsive servers, and
1284 * save the domain info we get back.
1285 */
1286 dc = ldap_ping(
1287 ctx,
1288 cdc,
1289 domain_name,
1290 DS_DS_FLAG);
1291 srv_free(cdc);
1292 cdc = NULL;
1293
1294 if (dc == NULL) {
1295 DEBUG1STATUS(ctx, "(no LDAP response)");
1296 goto try_global;
1297 }
1298 log_ds(ctx, dc);
1299
1300 update_item(&ctx->site_domain_controller, dc,
1301 AD_STATE_AUTO, dc->ttl);
1302 update_version(&ctx->site_domain_controller, PARAM1,
1303 domain_name_item);
1304 update_version(&ctx->site_domain_controller, PARAM2,
1305 site_name_item);
1306 }
1307 return (&ctx->site_domain_controller);
1308 }
1309
1310 try_global:
1311
1312 if (validate_global) {
1313 if (!is_valid(&ctx->domain_controller) ||
1314 is_changed(&ctx->domain_controller, PARAM1,
1315 domain_name_item)) {
1316
1317 /*
1318 * Lookup DNS SRV RR named
1319 * _ldap._tcp.dc._msdcs.<DomainName>
1320 */
1321 DEBUG1STATUS(ctx, "DNS SRV query, dom=%s",
1322 domain_name);
1323 DO_RES_NINIT(ctx);
1324 cdc = srv_query(&ctx->res_state,
1325 LDAP_SRV_HEAD DC_SRV_TAIL,
1326 domain_name, prefer_dc);
1327
1328 if (cdc == NULL) {
1329 DEBUG1STATUS(ctx, "(no DNS response)");
1330 return (NULL);
1331 }
1332 log_cds(ctx, cdc);
1333
1334 /*
1335 * Filter out unresponsive servers, and
1336 * save the domain info we get back.
1337 */
1338 dc = ldap_ping(
1339 ctx,
1340 cdc,
1341 domain_name,
1342 DS_DS_FLAG);
1343 srv_free(cdc);
1344 cdc = NULL;
1345
1346 if (dc == NULL) {
1347 DEBUG1STATUS(ctx, "(no LDAP response)");
1348 return (NULL);
1349 }
1350 log_ds(ctx, dc);
1351
1352 update_item(&ctx->domain_controller, dc,
1353 AD_STATE_AUTO, dc->ttl);
1354 update_version(&ctx->domain_controller, PARAM1,
1355 domain_name_item);
1356 }
1357 return (&ctx->domain_controller);
1358 }
1359
1360 return (NULL);
1361 }
1362
1363 ad_disc_ds_t *
ad_disc_get_DomainController(ad_disc_t ctx,enum ad_disc_req req,boolean_t * auto_discovered)1364 ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
1365 boolean_t *auto_discovered)
1366 {
1367 ad_item_t *domain_controller_item;
1368 ad_disc_ds_t *domain_controller = NULL;
1369
1370 domain_controller_item = validate_DomainController(ctx, req);
1371
1372 if (domain_controller_item != NULL) {
1373 domain_controller = ds_dup(domain_controller_item->value);
1374 if (auto_discovered != NULL)
1375 *auto_discovered =
1376 (domain_controller_item->state == AD_STATE_AUTO);
1377 } else if (auto_discovered != NULL)
1378 *auto_discovered = B_FALSE;
1379
1380 return (domain_controller);
1381 }
1382
1383
1384 /*
1385 * Discover the Domain GUID
1386 * This info comes from validate_DomainController()
1387 */
1388 static ad_item_t *
validate_DomainGUID(ad_disc_t ctx)1389 validate_DomainGUID(ad_disc_t ctx)
1390 {
1391 ad_item_t *domain_controller_item;
1392
1393 if (is_fixed(&ctx->domain_guid))
1394 return (&ctx->domain_guid);
1395
1396 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1397 if (domain_controller_item == NULL)
1398 return (NULL);
1399
1400 if (!is_valid(&ctx->domain_guid))
1401 return (NULL);
1402
1403 return (&ctx->domain_guid);
1404 }
1405
1406
1407 uchar_t *
ad_disc_get_DomainGUID(ad_disc_t ctx,boolean_t * auto_discovered)1408 ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered)
1409 {
1410 ad_item_t *domain_guid_item;
1411 uchar_t *domain_guid = NULL;
1412
1413 domain_guid_item = validate_DomainGUID(ctx);
1414 if (domain_guid_item != NULL) {
1415 domain_guid = uuid_dup(domain_guid_item->value);
1416 if (auto_discovered != NULL)
1417 *auto_discovered =
1418 (domain_guid_item->state == AD_STATE_AUTO);
1419 } else if (auto_discovered != NULL)
1420 *auto_discovered = B_FALSE;
1421
1422 return (domain_guid);
1423 }
1424
1425
1426 /*
1427 * Discover site name (for multi-homed systems the first one found wins)
1428 * This info comes from validate_DomainController()
1429 */
1430 static ad_item_t *
validate_SiteName(ad_disc_t ctx)1431 validate_SiteName(ad_disc_t ctx)
1432 {
1433 ad_item_t *domain_controller_item;
1434
1435 if (is_fixed(&ctx->site_name))
1436 return (&ctx->site_name);
1437
1438 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1439 if (domain_controller_item == NULL)
1440 return (NULL);
1441
1442 if (!is_valid(&ctx->site_name))
1443 return (NULL);
1444
1445 return (&ctx->site_name);
1446 }
1447
1448
1449 char *
ad_disc_get_SiteName(ad_disc_t ctx,boolean_t * auto_discovered)1450 ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered)
1451 {
1452 ad_item_t *site_name_item;
1453 char *site_name = NULL;
1454
1455 site_name_item = validate_SiteName(ctx);
1456 if (site_name_item != NULL) {
1457 site_name = strdup(site_name_item->value);
1458 if (auto_discovered != NULL)
1459 *auto_discovered =
1460 (site_name_item->state == AD_STATE_AUTO);
1461 } else if (auto_discovered != NULL)
1462 *auto_discovered = B_FALSE;
1463
1464 return (site_name);
1465 }
1466
1467
1468
1469 /*
1470 * Discover forest name
1471 * This info comes from validate_DomainController()
1472 */
1473 static ad_item_t *
validate_ForestName(ad_disc_t ctx)1474 validate_ForestName(ad_disc_t ctx)
1475 {
1476 ad_item_t *domain_controller_item;
1477
1478 if (is_fixed(&ctx->forest_name))
1479 return (&ctx->forest_name);
1480
1481 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1482 if (domain_controller_item == NULL)
1483 return (NULL);
1484
1485 if (!is_valid(&ctx->forest_name))
1486 return (NULL);
1487
1488 return (&ctx->forest_name);
1489 }
1490
1491
1492 char *
ad_disc_get_ForestName(ad_disc_t ctx,boolean_t * auto_discovered)1493 ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered)
1494 {
1495 ad_item_t *forest_name_item;
1496 char *forest_name = NULL;
1497
1498 forest_name_item = validate_ForestName(ctx);
1499
1500 if (forest_name_item != NULL) {
1501 forest_name = strdup(forest_name_item->value);
1502 if (auto_discovered != NULL)
1503 *auto_discovered =
1504 (forest_name_item->state == AD_STATE_AUTO);
1505 } else if (auto_discovered != NULL)
1506 *auto_discovered = B_FALSE;
1507
1508 return (forest_name);
1509 }
1510
1511
1512 /* Discover global catalog servers */
1513 static ad_item_t *
validate_GlobalCatalog(ad_disc_t ctx,enum ad_disc_req req)1514 validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
1515 {
1516 ad_disc_ds_t *gc = NULL;
1517 ad_disc_cds_t *cgc = NULL;
1518 boolean_t validate_global = B_FALSE;
1519 boolean_t validate_site = B_FALSE;
1520 ad_item_t *dc_item;
1521 ad_item_t *forest_name_item;
1522 ad_item_t *site_name_item;
1523 char *forest_name;
1524 char *site_name;
1525
1526 /* If the values is fixed there will not be a site specific version */
1527 if (is_fixed(&ctx->global_catalog))
1528 return (&ctx->global_catalog);
1529
1530 forest_name_item = validate_ForestName(ctx);
1531 if (forest_name_item == NULL) {
1532 DEBUG1STATUS(ctx, "(no forrest name)");
1533 return (NULL);
1534 }
1535 forest_name = (char *)forest_name_item->value;
1536
1537 if (req == AD_DISC_GLOBAL)
1538 validate_global = B_TRUE;
1539 else {
1540 if (is_fixed(&ctx->site_name))
1541 validate_site = B_TRUE;
1542 if (req == AD_DISC_PREFER_SITE)
1543 validate_global = B_TRUE;
1544 }
1545
1546 /*
1547 * If we're trying both site-specific and global,
1548 * try the site-specific first, then fall-back.
1549 */
1550 if (validate_site) {
1551 site_name_item = &ctx->site_name;
1552 site_name = (char *)site_name_item->value;
1553
1554 if (!is_valid(&ctx->site_global_catalog) ||
1555 is_changed(&ctx->site_global_catalog, PARAM1,
1556 forest_name_item) ||
1557 is_changed(&ctx->site_global_catalog, PARAM2,
1558 site_name_item)) {
1559 char rr_name[DNS_MAX_NAME];
1560
1561 /*
1562 * See if our DC is also a GC.
1563 */
1564 dc_item = validate_DomainController(ctx, req);
1565 if (dc_item != NULL) {
1566 ad_disc_ds_t *ds = dc_item->value;
1567 if ((ds->flags & DS_GC_FLAG) != 0) {
1568 DEBUG1STATUS(ctx,
1569 "DC is also a GC for %s in %s",
1570 forest_name, site_name);
1571 gc = ds_dup(ds);
1572 if (gc != NULL) {
1573 gc->port = GC_PORT;
1574 goto update_site;
1575 }
1576 }
1577 }
1578
1579 /*
1580 * Lookup DNS SRV RR named:
1581 * _ldap._tcp.<siteName>._sites.gc.
1582 * _msdcs.<ForestName>
1583 */
1584 DEBUG1STATUS(ctx, "DNS SRV query, forest=%s, site=%s",
1585 forest_name, site_name);
1586 (void) snprintf(rr_name, sizeof (rr_name),
1587 LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL,
1588 site_name);
1589 DO_RES_NINIT(ctx);
1590 cgc = srv_query(&ctx->res_state, rr_name,
1591 forest_name, NULL);
1592
1593 if (cgc == NULL) {
1594 DEBUG1STATUS(ctx, "(no DNS response)");
1595 goto try_global;
1596 }
1597 log_cds(ctx, cgc);
1598
1599 /*
1600 * Filter out unresponsive servers, and
1601 * save the domain info we get back.
1602 */
1603 gc = ldap_ping(
1604 NULL,
1605 cgc,
1606 forest_name,
1607 DS_GC_FLAG);
1608 srv_free(cgc);
1609 cgc = NULL;
1610
1611 if (gc == NULL) {
1612 DEBUG1STATUS(ctx, "(no LDAP response)");
1613 goto try_global;
1614 }
1615 log_ds(ctx, gc);
1616
1617 update_site:
1618 update_item(&ctx->site_global_catalog, gc,
1619 AD_STATE_AUTO, gc->ttl);
1620 update_version(&ctx->site_global_catalog, PARAM1,
1621 forest_name_item);
1622 update_version(&ctx->site_global_catalog, PARAM2,
1623 site_name_item);
1624 }
1625 return (&ctx->site_global_catalog);
1626 }
1627
1628 try_global:
1629
1630 if (validate_global) {
1631 if (!is_valid(&ctx->global_catalog) ||
1632 is_changed(&ctx->global_catalog, PARAM1,
1633 forest_name_item)) {
1634
1635 /*
1636 * See if our DC is also a GC.
1637 */
1638 dc_item = validate_DomainController(ctx, req);
1639 if (dc_item != NULL) {
1640 ad_disc_ds_t *ds = dc_item->value;
1641 if ((ds->flags & DS_GC_FLAG) != 0) {
1642 DEBUG1STATUS(ctx,
1643 "DC is also a GC for %s",
1644 forest_name);
1645 gc = ds_dup(ds);
1646 if (gc != NULL) {
1647 gc->port = GC_PORT;
1648 goto update_global;
1649 }
1650 }
1651 }
1652
1653 /*
1654 * Lookup DNS SRV RR named:
1655 * _ldap._tcp.gc._msdcs.<ForestName>
1656 */
1657 DEBUG1STATUS(ctx, "DNS SRV query, forest=%s",
1658 forest_name);
1659 DO_RES_NINIT(ctx);
1660 cgc = srv_query(&ctx->res_state,
1661 LDAP_SRV_HEAD GC_SRV_TAIL,
1662 forest_name, NULL);
1663
1664 if (cgc == NULL) {
1665 DEBUG1STATUS(ctx, "(no DNS response)");
1666 return (NULL);
1667 }
1668 log_cds(ctx, cgc);
1669
1670 /*
1671 * Filter out unresponsive servers, and
1672 * save the domain info we get back.
1673 */
1674 gc = ldap_ping(
1675 NULL,
1676 cgc,
1677 forest_name,
1678 DS_GC_FLAG);
1679 srv_free(cgc);
1680 cgc = NULL;
1681
1682 if (gc == NULL) {
1683 DEBUG1STATUS(ctx, "(no LDAP response)");
1684 return (NULL);
1685 }
1686 log_ds(ctx, gc);
1687
1688 update_global:
1689 update_item(&ctx->global_catalog, gc,
1690 AD_STATE_AUTO, gc->ttl);
1691 update_version(&ctx->global_catalog, PARAM1,
1692 forest_name_item);
1693 }
1694 return (&ctx->global_catalog);
1695 }
1696 return (NULL);
1697 }
1698
1699
1700 ad_disc_ds_t *
ad_disc_get_GlobalCatalog(ad_disc_t ctx,enum ad_disc_req req,boolean_t * auto_discovered)1701 ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req,
1702 boolean_t *auto_discovered)
1703 {
1704 ad_disc_ds_t *global_catalog = NULL;
1705 ad_item_t *global_catalog_item;
1706
1707 global_catalog_item = validate_GlobalCatalog(ctx, req);
1708
1709 if (global_catalog_item != NULL) {
1710 global_catalog = ds_dup(global_catalog_item->value);
1711 if (auto_discovered != NULL)
1712 *auto_discovered =
1713 (global_catalog_item->state == AD_STATE_AUTO);
1714 } else if (auto_discovered != NULL)
1715 *auto_discovered = B_FALSE;
1716
1717 return (global_catalog);
1718 }
1719
1720
1721 static ad_item_t *
validate_TrustedDomains(ad_disc_t ctx)1722 validate_TrustedDomains(ad_disc_t ctx)
1723 {
1724 LDAP *ld = NULL;
1725 ad_item_t *global_catalog_item;
1726 ad_item_t *forest_name_item;
1727 ad_disc_trusteddomains_t *trusted_domains;
1728 char *dn = NULL;
1729 char *forest_name_dn;
1730 int len;
1731 int num_parts;
1732
1733 if (is_fixed(&ctx->trusted_domains))
1734 return (&ctx->trusted_domains);
1735
1736 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
1737 if (global_catalog_item == NULL)
1738 return (NULL);
1739
1740 forest_name_item = validate_ForestName(ctx);
1741 if (forest_name_item == NULL)
1742 return (NULL);
1743
1744 if (!is_valid(&ctx->trusted_domains) ||
1745 is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) ||
1746 is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) {
1747
1748 forest_name_dn = ldap_dns_to_dn(forest_name_item->value,
1749 &num_parts);
1750 if (forest_name_dn == NULL)
1751 return (NULL);
1752
1753 len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1;
1754 dn = malloc(len);
1755 if (dn == NULL) {
1756 free(forest_name_dn);
1757 return (NULL);
1758 }
1759 (void) snprintf(dn, len, "CN=System,%s", forest_name_dn);
1760 free(forest_name_dn);
1761
1762 trusted_domains = ldap_lookup_trusted_domains(
1763 &ld, global_catalog_item->value, dn);
1764
1765 if (ld != NULL)
1766 (void) ldap_unbind(ld);
1767 free(dn);
1768
1769 if (trusted_domains == NULL)
1770 return (NULL);
1771
1772 update_item(&ctx->trusted_domains, trusted_domains,
1773 AD_STATE_AUTO, 0);
1774 update_version(&ctx->trusted_domains, PARAM1,
1775 global_catalog_item);
1776 update_version(&ctx->trusted_domains, PARAM2,
1777 forest_name_item);
1778 }
1779
1780 return (&ctx->trusted_domains);
1781 }
1782
1783
1784 ad_disc_trusteddomains_t *
ad_disc_get_TrustedDomains(ad_disc_t ctx,boolean_t * auto_discovered)1785 ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered)
1786 {
1787 ad_disc_trusteddomains_t *trusted_domains = NULL;
1788 ad_item_t *trusted_domains_item;
1789
1790 trusted_domains_item = validate_TrustedDomains(ctx);
1791
1792 if (trusted_domains_item != NULL) {
1793 trusted_domains = td_dup(trusted_domains_item->value);
1794 if (auto_discovered != NULL)
1795 *auto_discovered =
1796 (trusted_domains_item->state == AD_STATE_AUTO);
1797 } else if (auto_discovered != NULL)
1798 *auto_discovered = B_FALSE;
1799
1800 return (trusted_domains);
1801 }
1802
1803
1804 static ad_item_t *
validate_DomainsInForest(ad_disc_t ctx)1805 validate_DomainsInForest(ad_disc_t ctx)
1806 {
1807 ad_item_t *global_catalog_item;
1808 LDAP *ld = NULL;
1809 ad_disc_domainsinforest_t *domains_in_forest;
1810
1811 if (is_fixed(&ctx->domains_in_forest))
1812 return (&ctx->domains_in_forest);
1813
1814 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
1815 if (global_catalog_item == NULL)
1816 return (NULL);
1817
1818 if (!is_valid(&ctx->domains_in_forest) ||
1819 is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) {
1820
1821 domains_in_forest = ldap_lookup_domains_in_forest(
1822 &ld, global_catalog_item->value);
1823
1824 if (ld != NULL)
1825 (void) ldap_unbind(ld);
1826
1827 if (domains_in_forest == NULL)
1828 return (NULL);
1829
1830 update_item(&ctx->domains_in_forest, domains_in_forest,
1831 AD_STATE_AUTO, 0);
1832 update_version(&ctx->domains_in_forest, PARAM1,
1833 global_catalog_item);
1834 }
1835 return (&ctx->domains_in_forest);
1836 }
1837
1838
1839 ad_disc_domainsinforest_t *
ad_disc_get_DomainsInForest(ad_disc_t ctx,boolean_t * auto_discovered)1840 ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered)
1841 {
1842 ad_disc_domainsinforest_t *domains_in_forest = NULL;
1843 ad_item_t *domains_in_forest_item;
1844
1845 domains_in_forest_item = validate_DomainsInForest(ctx);
1846
1847 if (domains_in_forest_item != NULL) {
1848 domains_in_forest = df_dup(domains_in_forest_item->value);
1849 if (auto_discovered != NULL)
1850 *auto_discovered =
1851 (domains_in_forest_item->state == AD_STATE_AUTO);
1852 } else if (auto_discovered != NULL)
1853 *auto_discovered = B_FALSE;
1854
1855 return (domains_in_forest);
1856 }
1857
1858 static ad_item_t *
validate_PreferredDC(ad_disc_t ctx)1859 validate_PreferredDC(ad_disc_t ctx)
1860 {
1861 if (is_valid(&ctx->preferred_dc))
1862 return (&ctx->preferred_dc);
1863
1864 return (NULL);
1865 }
1866
1867 ad_disc_ds_t *
ad_disc_get_PreferredDC(ad_disc_t ctx,boolean_t * auto_discovered)1868 ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered)
1869 {
1870 ad_disc_ds_t *preferred_dc = NULL;
1871 ad_item_t *preferred_dc_item;
1872
1873 preferred_dc_item = validate_PreferredDC(ctx);
1874
1875 if (preferred_dc_item != NULL) {
1876 preferred_dc = ds_dup(preferred_dc_item->value);
1877 if (auto_discovered != NULL)
1878 *auto_discovered =
1879 (preferred_dc_item->state == AD_STATE_AUTO);
1880 } else if (auto_discovered != NULL)
1881 *auto_discovered = B_FALSE;
1882
1883 return (preferred_dc);
1884 }
1885
1886
1887
1888 int
ad_disc_set_DomainName(ad_disc_t ctx,const char * domainName)1889 ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName)
1890 {
1891 char *domain_name = NULL;
1892 if (domainName != NULL) {
1893 domain_name = strdup(domainName);
1894 if (domain_name == NULL)
1895 return (-1);
1896 update_item(&ctx->domain_name, domain_name,
1897 AD_STATE_FIXED, 0);
1898 } else if (ctx->domain_name.state == AD_STATE_FIXED)
1899 ctx->domain_name.state = AD_STATE_INVALID;
1900 return (0);
1901 }
1902
1903 int
ad_disc_set_DomainGUID(ad_disc_t ctx,uchar_t * u)1904 ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
1905 {
1906 char *domain_guid = NULL;
1907 if (u != NULL) {
1908 domain_guid = uuid_dup(u);
1909 if (domain_guid == NULL)
1910 return (-1);
1911 update_item(&ctx->domain_guid, domain_guid,
1912 AD_STATE_FIXED, 0);
1913 } else if (ctx->domain_guid.state == AD_STATE_FIXED)
1914 ctx->domain_guid.state = AD_STATE_INVALID;
1915 return (0);
1916 }
1917
1918 void
auto_set_DomainGUID(ad_disc_t ctx,uchar_t * u)1919 auto_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
1920 {
1921 char *domain_guid = NULL;
1922
1923 if (is_fixed(&ctx->domain_guid))
1924 return;
1925
1926 domain_guid = uuid_dup(u);
1927 if (domain_guid == NULL)
1928 return;
1929 update_item(&ctx->domain_guid, domain_guid, AD_STATE_AUTO, 0);
1930 }
1931
1932 int
ad_disc_set_DomainController(ad_disc_t ctx,const ad_disc_ds_t * domainController)1933 ad_disc_set_DomainController(ad_disc_t ctx,
1934 const ad_disc_ds_t *domainController)
1935 {
1936 ad_disc_ds_t *domain_controller = NULL;
1937 if (domainController != NULL) {
1938 domain_controller = ds_dup(domainController);
1939 if (domain_controller == NULL)
1940 return (-1);
1941 update_item(&ctx->domain_controller, domain_controller,
1942 AD_STATE_FIXED, 0);
1943 } else if (ctx->domain_controller.state == AD_STATE_FIXED)
1944 ctx->domain_controller.state = AD_STATE_INVALID;
1945 return (0);
1946 }
1947
1948 int
ad_disc_set_SiteName(ad_disc_t ctx,const char * siteName)1949 ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
1950 {
1951 char *site_name = NULL;
1952 if (siteName != NULL) {
1953 site_name = strdup(siteName);
1954 if (site_name == NULL)
1955 return (-1);
1956 update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0);
1957 } else if (ctx->site_name.state == AD_STATE_FIXED)
1958 ctx->site_name.state = AD_STATE_INVALID;
1959 return (0);
1960 }
1961
1962 void
auto_set_SiteName(ad_disc_t ctx,char * siteName)1963 auto_set_SiteName(ad_disc_t ctx, char *siteName)
1964 {
1965 char *site_name = NULL;
1966
1967 if (is_fixed(&ctx->site_name))
1968 return;
1969
1970 site_name = strdup(siteName);
1971 if (site_name == NULL)
1972 return;
1973 update_item(&ctx->site_name, site_name, AD_STATE_AUTO, 0);
1974 }
1975
1976 int
ad_disc_set_ForestName(ad_disc_t ctx,const char * forestName)1977 ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
1978 {
1979 char *forest_name = NULL;
1980 if (forestName != NULL) {
1981 forest_name = strdup(forestName);
1982 if (forest_name == NULL)
1983 return (-1);
1984 update_item(&ctx->forest_name, forest_name,
1985 AD_STATE_FIXED, 0);
1986 } else if (ctx->forest_name.state == AD_STATE_FIXED)
1987 ctx->forest_name.state = AD_STATE_INVALID;
1988 return (0);
1989 }
1990
1991 void
auto_set_ForestName(ad_disc_t ctx,char * forestName)1992 auto_set_ForestName(ad_disc_t ctx, char *forestName)
1993 {
1994 char *forest_name = NULL;
1995
1996 if (is_fixed(&ctx->forest_name))
1997 return;
1998
1999 forest_name = strdup(forestName);
2000 if (forest_name == NULL)
2001 return;
2002 update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
2003 }
2004
2005 int
ad_disc_set_GlobalCatalog(ad_disc_t ctx,const ad_disc_ds_t * globalCatalog)2006 ad_disc_set_GlobalCatalog(ad_disc_t ctx,
2007 const ad_disc_ds_t *globalCatalog)
2008 {
2009 ad_disc_ds_t *global_catalog = NULL;
2010 if (globalCatalog != NULL) {
2011 global_catalog = ds_dup(globalCatalog);
2012 if (global_catalog == NULL)
2013 return (-1);
2014 update_item(&ctx->global_catalog, global_catalog,
2015 AD_STATE_FIXED, 0);
2016 } else if (ctx->global_catalog.state == AD_STATE_FIXED)
2017 ctx->global_catalog.state = AD_STATE_INVALID;
2018 return (0);
2019 }
2020
2021 int
ad_disc_set_PreferredDC(ad_disc_t ctx,const ad_disc_ds_t * pref_dc)2022 ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *pref_dc)
2023 {
2024 ad_disc_ds_t *new_pref_dc = NULL;
2025 if (pref_dc != NULL) {
2026 new_pref_dc = ds_dup(pref_dc);
2027 if (new_pref_dc == NULL)
2028 return (-1);
2029 update_item(&ctx->preferred_dc, new_pref_dc,
2030 AD_STATE_FIXED, 0);
2031 } else if (ctx->preferred_dc.state == AD_STATE_FIXED)
2032 ctx->preferred_dc.state = AD_STATE_INVALID;
2033 return (0);
2034 }
2035
2036 void
ad_disc_set_StatusFP(ad_disc_t ctx,struct __FILE_TAG * fp)2037 ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *fp)
2038 {
2039 ctx->status_fp = fp;
2040 }
2041
2042
2043 int
ad_disc_unset(ad_disc_t ctx)2044 ad_disc_unset(ad_disc_t ctx)
2045 {
2046 if (ctx->domain_name.state == AD_STATE_FIXED)
2047 ctx->domain_name.state = AD_STATE_INVALID;
2048
2049 if (ctx->domain_controller.state == AD_STATE_FIXED)
2050 ctx->domain_controller.state = AD_STATE_INVALID;
2051
2052 if (ctx->preferred_dc.state == AD_STATE_FIXED)
2053 ctx->preferred_dc.state = AD_STATE_INVALID;
2054
2055 if (ctx->site_name.state == AD_STATE_FIXED)
2056 ctx->site_name.state = AD_STATE_INVALID;
2057
2058 if (ctx->forest_name.state == AD_STATE_FIXED)
2059 ctx->forest_name.state = AD_STATE_INVALID;
2060
2061 if (ctx->global_catalog.state == AD_STATE_FIXED)
2062 ctx->global_catalog.state = AD_STATE_INVALID;
2063
2064 return (0);
2065 }
2066
2067 /*
2068 * ad_disc_get_TTL
2069 *
2070 * This routines the time to live for AD
2071 * auto discovered items.
2072 *
2073 * Returns:
2074 * -1 if there are no TTL items
2075 * 0 if there are expired items
2076 * else the number of seconds
2077 *
2078 * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it
2079 * is positive -- min() greater than zero.
2080 */
2081 #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \
2082 (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x))))
2083 int
ad_disc_get_TTL(ad_disc_t ctx)2084 ad_disc_get_TTL(ad_disc_t ctx)
2085 {
2086 time_t expires;
2087 int ttl;
2088
2089 expires = MIN_GT_ZERO(ctx->domain_controller.expires,
2090 ctx->global_catalog.expires);
2091 expires = MIN_GT_ZERO(expires, ctx->site_domain_controller.expires);
2092 expires = MIN_GT_ZERO(expires, ctx->site_global_catalog.expires);
2093
2094 if (expires == -1) {
2095 return (-1);
2096 }
2097
2098 if (ctx->expires_not_before != 0 &&
2099 expires < ctx->expires_not_before) {
2100 expires = ctx->expires_not_before;
2101 }
2102
2103 if (ctx->expires_not_after != 0 &&
2104 expires > ctx->expires_not_after) {
2105 expires = ctx->expires_not_after;
2106 }
2107
2108 ttl = expires - time(NULL);
2109
2110 if (ttl < 0) {
2111 return (0);
2112 }
2113 return (ttl);
2114 }
2115
2116 boolean_t
ad_disc_SubnetChanged(ad_disc_t ctx)2117 ad_disc_SubnetChanged(ad_disc_t ctx)
2118 {
2119 ad_subnet_t *subnets;
2120
2121 if (ctx->subnets_changed || ctx->subnets == NULL)
2122 return (B_TRUE);
2123
2124 if ((subnets = find_subnets()) != NULL) {
2125 if (cmpsubnets(subnets, ctx->subnets) != 0)
2126 ctx->subnets_changed = B_TRUE;
2127 free(subnets);
2128 }
2129
2130 return (ctx->subnets_changed);
2131 }
2132