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