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