dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libadutils / common / addisc.c
blob5c5991aa32cde305ffa74ebb8be2942577cd0bcf
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
28 * Active Directory Auto-Discovery.
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.
35 * But there is an actual order in which discovery must be done. Given
36 * the discovery mechanism implemented here, that order is:
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.
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.
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.
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.
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.
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...
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.
76 * This API is not thread-safe.
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"
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.
115 #define MINIMUM_TTL (5 * 60)
116 #define MAXIMUM_TTL (20 * 60)
119 #define DNS_MAX_NAME NS_MAXDNAME
121 #define GC_PORT 3268
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"
131 /* A RR name for all GCs -- last resort this works */
132 #define GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
136 * We try res_ninit() whenever we don't have one. res_ninit() fails if
137 * idmapd is running before the network is up!
139 #define DO_RES_NINIT(ctx) \
140 if (!(ctx)->res_ninitted) \
141 (void) do_res_ninit(ctx)
143 #define DO_GETNAMEINFO(b, l, s) \
144 if (ad_disc_getnameinfo(b, l, s) != 0) \
145 (void) strlcpy(b, "?", l)
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"); \
154 _NOTE(CONSTCOND) \
155 } while (0)
157 #define is_fixed(item) \
158 ((item)->state == AD_STATE_FIXED)
160 #define is_changed(item, num, param) \
161 ((item)->param_version[num] != (param)->version)
163 void * uuid_dup(void *);
165 static ad_item_t *validate_SiteName(ad_disc_t ctx);
166 static ad_item_t *validate_PreferredDC(ad_disc_t ctx);
169 * Function definitions
173 static int
174 do_res_ninit(ad_disc_t ctx)
176 int rc;
178 rc = res_ninit(&ctx->res_state);
179 if (rc != 0)
180 return (rc);
181 ctx->res_ninitted = 1;
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.
189 * Note that some queries will try TCP anyway.
191 #if 0
192 ctx->res_state.options |= RES_USEVC;
193 #endif
194 return (0);
198 * Private getnameinfo(3socket) variant tailored to our needs.
201 ad_disc_getnameinfo(char *obuf, int olen, struct sockaddr_storage *ss)
203 struct sockaddr *sa;
204 int eai, slen;
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);
218 eai = getnameinfo(sa, slen, obuf, olen, NULL, 0, NI_NUMERICHOST);
220 return (eai);
223 static void
224 update_version(ad_item_t *item, int num, ad_item_t *param)
226 item->param_version[num] = param->version;
231 static boolean_t
232 is_valid(ad_item_t *item)
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);
241 return (B_FALSE);
245 static void
246 update_item(ad_item_t *item, void *value, enum ad_item_state state,
247 uint32_t ttl)
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++;
264 free(item->value);
266 item->value = value;
267 item->state = state;
269 if (ttl == 0)
270 item->expires = 0;
271 else
272 item->expires = time(NULL) + ttl;
275 /* Compare UUIDs */
277 ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2)
279 int rc;
281 rc = memcmp(u1, u2, UUID_LEN);
282 return (rc);
285 void *
286 uuid_dup(void *src)
288 void *dst;
289 dst = malloc(UUID_LEN);
290 if (dst != NULL)
291 (void) memcpy(dst, src, UUID_LEN);
292 return (dst);
295 /* Compare DS lists */
297 ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2)
299 int i, j;
300 int num_ds1;
301 int num_ds2;
302 boolean_t match;
304 for (i = 0; ds1[i].host[0] != '\0'; i++)
305 continue;
306 num_ds1 = i;
307 for (j = 0; ds2[j].host[0] != '\0'; j++)
308 continue;
309 num_ds2 = j;
310 if (num_ds1 != num_ds2)
311 return (1);
313 for (i = 0; i < num_ds1; i++) {
314 match = B_FALSE;
315 for (j = 0; j < num_ds2; j++) {
316 if (strcmp(ds1[i].host, ds2[j].host) == 0 &&
317 ds1[i].port == ds2[j].port) {
318 match = B_TRUE;
319 break;
322 if (!match)
323 return (1);
325 return (0);
329 /* Copy a list of DSs */
330 static ad_disc_ds_t *
331 ds_dup(const ad_disc_ds_t *srv)
333 int i;
334 int size;
335 ad_disc_ds_t *new = NULL;
337 for (i = 0; srv[i].host[0] != '\0'; i++)
338 continue;
340 size = (i + 1) * sizeof (ad_disc_ds_t);
341 new = malloc(size);
342 if (new != NULL)
343 (void) memcpy(new, srv, size);
344 return (new);
349 ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
350 ad_disc_trusteddomains_t *td2)
352 int i, j;
353 int num_td1;
354 int num_td2;
355 boolean_t match;
357 for (i = 0; td1[i].domain[0] != '\0'; i++)
358 continue;
359 num_td1 = i;
361 for (j = 0; td2[j].domain[0] != '\0'; j++)
362 continue;
363 num_td2 = j;
365 if (num_td1 != num_td2)
366 return (1);
368 for (i = 0; i < num_td1; i++) {
369 match = B_FALSE;
370 for (j = 0; j < num_td2; j++) {
371 if (domain_eq(td1[i].domain, td2[j].domain)) {
372 match = B_TRUE;
373 break;
376 if (!match)
377 return (1);
379 return (0);
384 /* Copy a list of Trusted Domains */
385 static ad_disc_trusteddomains_t *
386 td_dup(const ad_disc_trusteddomains_t *td)
388 int i;
389 int size;
390 ad_disc_trusteddomains_t *new = NULL;
392 for (i = 0; td[i].domain[0] != '\0'; i++)
393 continue;
395 size = (i + 1) * sizeof (ad_disc_trusteddomains_t);
396 new = malloc(size);
397 if (new != NULL)
398 (void) memcpy(new, td, size);
399 return (new);
405 ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1,
406 ad_disc_domainsinforest_t *df2)
408 int i, j;
409 int num_df1;
410 int num_df2;
411 boolean_t match;
413 for (i = 0; df1[i].domain[0] != '\0'; i++)
414 continue;
415 num_df1 = i;
417 for (j = 0; df2[j].domain[0] != '\0'; j++)
418 continue;
419 num_df2 = j;
421 if (num_df1 != num_df2)
422 return (1);
424 for (i = 0; i < num_df1; i++) {
425 match = B_FALSE;
426 for (j = 0; j < num_df2; j++) {
427 if (domain_eq(df1[i].domain, df2[j].domain) &&
428 strcmp(df1[i].sid, df2[j].sid) == 0) {
429 match = B_TRUE;
430 break;
433 if (!match)
434 return (1);
436 return (0);
441 /* Copy a list of Trusted Domains */
442 static ad_disc_domainsinforest_t *
443 df_dup(const ad_disc_domainsinforest_t *df)
445 int i;
446 int size;
447 ad_disc_domainsinforest_t *new = NULL;
449 for (i = 0; df[i].domain[0] != '\0'; i++)
450 continue;
452 size = (i + 1) * sizeof (ad_disc_domainsinforest_t);
453 new = malloc(size);
454 if (new != NULL)
455 (void) memcpy(new, df, size);
456 return (new);
464 * Returns an array of IPv4 address/prefix length
465 * The last subnet is NULL
467 static ad_subnet_t *
468 find_subnets()
470 int sock, n, i;
471 struct lifconf lifc;
472 struct lifreq lifr, *lifrp;
473 struct lifnum lifn;
474 uint32_t prefix_len;
475 char *s;
476 ad_subnet_t *results;
478 lifrp = &lifr;
480 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
481 logger(LOG_ERR, "Failed to open IPv4 socket for "
482 "listing network interfaces (%s)", strerror(errno));
483 return (NULL);
486 lifn.lifn_family = AF_INET;
487 lifn.lifn_flags = 0;
488 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
489 logger(LOG_ERR,
490 "Failed to find the number of network interfaces (%s)",
491 strerror(errno));
492 (void) close(sock);
493 return (NULL);
496 if (lifn.lifn_count < 1) {
497 logger(LOG_ERR, "No IPv4 network interfaces found");
498 (void) close(sock);
499 return (NULL);
502 lifc.lifc_family = AF_INET;
503 lifc.lifc_flags = 0;
504 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
505 lifc.lifc_buf = malloc(lifc.lifc_len);
507 if (lifc.lifc_buf == NULL) {
508 logger(LOG_ERR, "Out of memory");
509 (void) close(sock);
510 return (NULL);
513 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
514 logger(LOG_ERR, "Failed to list network interfaces (%s)",
515 strerror(errno));
516 free(lifc.lifc_buf);
517 (void) close(sock);
518 return (NULL);
521 n = lifc.lifc_len / (int)sizeof (struct lifreq);
523 if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) {
524 free(lifc.lifc_buf);
525 (void) close(sock);
526 return (NULL);
529 for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) {
530 if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0)
531 continue;
533 if ((lifrp->lifr_flags & IFF_UP) == 0)
534 continue;
536 if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0)
537 continue;
539 prefix_len = lifrp->lifr_addrlen;
541 s = inet_ntoa(((struct sockaddr_in *)
542 &lifrp->lifr_addr)->sin_addr);
544 (void) snprintf(results[i].subnet, sizeof (ad_subnet_t),
545 "%s/%d", s, prefix_len);
548 free(lifc.lifc_buf);
549 (void) close(sock);
551 return (results);
554 static int
555 cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2)
557 int num_subnets1;
558 int num_subnets2;
559 boolean_t matched;
560 int i, j;
562 for (i = 0; subnets1[i].subnet[0] != '\0'; i++)
563 continue;
564 num_subnets1 = i;
566 for (i = 0; subnets2[i].subnet[0] != '\0'; i++)
567 continue;
568 num_subnets2 = i;
570 if (num_subnets1 != num_subnets2)
571 return (1);
573 for (i = 0; i < num_subnets1; i++) {
574 matched = B_FALSE;
575 for (j = 0; j < num_subnets2; j++) {
576 if (strcmp(subnets1[i].subnet,
577 subnets2[j].subnet) == 0) {
578 matched = B_TRUE;
579 break;
582 if (!matched)
583 return (1);
585 return (0);
591 /* Convert a DN's DC components into a DNS domainname */
592 char *
593 DN_to_DNS(const char *dn_name)
595 char dns[DNS_MAX_NAME];
596 char *dns_name;
597 int i, j;
598 int num = 0;
600 j = 0;
601 i = 0;
603 if (dn_name == NULL)
604 return (NULL);
606 * Find all DC=<value> and form DNS name of the
607 * form <value1>.<value2>...
609 while (dn_name[i] != '\0') {
610 if (strncasecmp(&dn_name[i], "DC=", 3) == 0) {
611 i += 3;
612 if (dn_name[i] != '\0' && num > 0)
613 dns[j++] = '.';
614 while (dn_name[i] != '\0' &&
615 dn_name[i] != ',' && dn_name[i] != '+')
616 dns[j++] = dn_name[i++];
617 num++;
618 } else {
619 /* Skip attr=value as it is not DC= */
620 while (dn_name[i] != '\0' &&
621 dn_name[i] != ',' && dn_name[i] != '+')
622 i++;
624 /* Skip over separator ',' or '+' */
625 if (dn_name[i] != '\0') i++;
627 dns[j] = '\0';
628 dns_name = malloc(j + 1);
629 if (dns_name != NULL)
630 (void) strlcpy(dns_name, dns, j + 1);
631 return (dns_name);
636 * A utility function to bind to a Directory server
639 static
640 LDAP *
641 ldap_lookup_init(ad_disc_ds_t *ds)
643 int i;
644 int rc, ldversion;
645 int zero = 0;
646 int timeoutms = 5 * 1000;
647 char *saslmech = "GSSAPI";
648 uint32_t saslflags = LDAP_SASL_INTERACTIVE;
649 LDAP *ld = NULL;
651 for (i = 0; ds[i].host[0] != '\0'; i++) {
652 if (DBG(LDAP, 2)) {
653 logger(LOG_DEBUG, "adutils: ldap_lookup_init, host %s",
654 ds[i].host);
657 ld = ldap_init(ds[i].host, ds[i].port);
658 if (ld == NULL) {
659 if (DBG(LDAP, 1)) {
660 logger(LOG_DEBUG,
661 "Couldn't connect to AD DC %s:%d (%s)",
662 ds[i].host, ds[i].port,
663 strerror(errno));
665 continue;
668 ldversion = LDAP_VERSION3;
669 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
670 &ldversion);
671 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
672 LDAP_OPT_OFF);
673 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
674 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
675 /* setup TCP/IP connect timeout */
676 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
677 &timeoutms);
678 (void) ldap_set_option(ld, LDAP_OPT_RESTART,
679 LDAP_OPT_ON);
681 rc = adutils_set_thread_functions(ld);
682 if (rc != LDAP_SUCCESS) {
683 /* Error has already been logged */
684 (void) ldap_unbind(ld);
685 ld = NULL;
686 continue;
689 rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
690 saslmech, NULL, NULL, saslflags, &saslcallback,
691 NULL /* defaults */);
692 if (rc == LDAP_SUCCESS)
693 break;
695 if (DBG(LDAP, 0)) {
696 logger(LOG_INFO, "LDAP: %s:%d: %s",
697 ds[i].host, ds[i].port, ldap_err2string(rc));
698 ldap_perror(ld, ds[i].host);
700 (void) ldap_unbind(ld);
701 ld = NULL;
703 return (ld);
709 * Lookup the trusted domains in the global catalog.
711 * Returns:
712 * array of trusted domains which is terminated by
713 * an empty trusted domain.
714 * NULL an error occured
716 ad_disc_trusteddomains_t *
717 ldap_lookup_trusted_domains(LDAP **ld, ad_disc_ds_t *globalCatalog,
718 char *base_dn)
720 int scope = LDAP_SCOPE_SUBTREE;
721 char *attrs[3];
722 int rc;
723 LDAPMessage *results = NULL;
724 LDAPMessage *entry;
725 char *filter;
726 char **partner = NULL;
727 char **direction = NULL;
728 int num = 0;
729 ad_disc_trusteddomains_t *trusted_domains = NULL;
731 if (DBG(DISC, 1))
732 logger(LOG_DEBUG, "Looking for trusted domains...");
734 if (*ld == NULL)
735 *ld = ldap_lookup_init(globalCatalog);
737 if (*ld == NULL) {
738 logger(LOG_ERR, "adutils: ldap_lookup_init failed");
739 return (NULL);
742 attrs[0] = "trustPartner";
743 attrs[1] = "trustDirection";
744 attrs[2] = NULL;
747 * Trust direction values:
748 * 1 - inbound (they trust us)
749 * 2 - outbound (we trust them)
750 * 3 - bidirectional (we trust each other)
752 filter = "(&(objectclass=trustedDomain)"
753 "(|(trustDirection=3)(trustDirection=2)))";
755 rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results);
756 if (DBG(DISC, 1))
757 logger(LOG_DEBUG, "Trusted domains:");
758 if (rc == LDAP_SUCCESS) {
759 for (entry = ldap_first_entry(*ld, results);
760 entry != NULL; entry = ldap_next_entry(*ld, entry)) {
761 partner = ldap_get_values(*ld, entry, "trustPartner");
762 direction = ldap_get_values(
763 *ld, entry, "trustDirection");
765 if (partner != NULL && direction != NULL) {
766 if (DBG(DISC, 1)) {
767 logger(LOG_DEBUG, " %s (%s)",
768 partner[0], direction[0]);
770 num++;
771 void *tmp = realloc(trusted_domains,
772 (num + 1) *
773 sizeof (ad_disc_trusteddomains_t));
774 if (tmp == NULL) {
775 free(trusted_domains);
776 ldap_value_free(partner);
777 ldap_value_free(direction);
778 (void) ldap_msgfree(results);
779 return (NULL);
781 trusted_domains = tmp;
782 /* Last element should be zero */
783 (void) memset(&trusted_domains[num], 0,
784 sizeof (ad_disc_trusteddomains_t));
785 (void) strcpy(trusted_domains[num - 1].domain,
786 partner[0]);
787 trusted_domains[num - 1].direction =
788 atoi(direction[0]);
790 if (partner != NULL)
791 ldap_value_free(partner);
792 if (direction != NULL)
793 ldap_value_free(direction);
795 } else if (rc == LDAP_NO_RESULTS_RETURNED) {
796 /* This is not an error - return empty trusted domain */
797 trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
798 if (DBG(DISC, 1))
799 logger(LOG_DEBUG, " not found");
800 } else {
801 if (DBG(DISC, 1))
802 logger(LOG_DEBUG, " rc=%d", rc);
804 if (results != NULL)
805 (void) ldap_msgfree(results);
807 return (trusted_domains);
812 * This functions finds all the domains in a forest.
814 ad_disc_domainsinforest_t *
815 ldap_lookup_domains_in_forest(LDAP **ld, ad_disc_ds_t *globalCatalogs)
817 static char *attrs[] = {
818 "objectSid",
819 NULL,
821 int rc;
822 LDAPMessage *result = NULL;
823 LDAPMessage *entry;
824 int ndomains = 0;
825 int nresults;
826 ad_disc_domainsinforest_t *domains = NULL;
828 if (DBG(DISC, 1))
829 logger(LOG_DEBUG, "Looking for domains in forest...");
831 if (*ld == NULL)
832 *ld = ldap_lookup_init(globalCatalogs);
834 if (*ld == NULL) {
835 logger(LOG_ERR, "adutils: ldap_lookup_init failed");
836 return (NULL);
839 /* Find domains */
840 rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
841 "(objectClass=Domain)", attrs, 0, &result);
842 if (rc != LDAP_SUCCESS) {
843 logger(LOG_ERR, "adutils: ldap_search, rc=%d", rc);
844 goto err;
846 if (DBG(DISC, 1))
847 logger(LOG_DEBUG, "Domains in forest:");
849 nresults = ldap_count_entries(*ld, result);
850 domains = calloc(nresults + 1, sizeof (*domains));
851 if (domains == NULL) {
852 if (DBG(DISC, 1))
853 logger(LOG_DEBUG, " (nomem)");
854 goto err;
857 for (entry = ldap_first_entry(*ld, result);
858 entry != NULL;
859 entry = ldap_next_entry(*ld, entry)) {
860 struct berval **sid_ber;
861 adutils_sid_t sid;
862 char *sid_str;
863 char *name;
864 char *dn;
866 sid_ber = ldap_get_values_len(*ld, entry,
867 "objectSid");
868 if (sid_ber == NULL)
869 continue;
871 rc = adutils_getsid(sid_ber[0], &sid);
872 ldap_value_free_len(sid_ber);
873 if (rc < 0)
874 goto err;
876 if ((sid_str = adutils_sid2txt(&sid)) == NULL)
877 goto err;
879 (void) strcpy(domains[ndomains].sid, sid_str);
880 free(sid_str);
882 dn = ldap_get_dn(*ld, entry);
883 name = DN_to_DNS(dn);
884 free(dn);
885 if (name == NULL)
886 goto err;
888 (void) strcpy(domains[ndomains].domain, name);
889 free(name);
891 if (DBG(DISC, 1))
892 logger(LOG_DEBUG, " %s", domains[ndomains].domain);
894 ndomains++;
897 if (ndomains == 0) {
898 if (DBG(DISC, 1))
899 logger(LOG_DEBUG, " not found");
900 goto err;
903 if (ndomains < nresults) {
904 ad_disc_domainsinforest_t *tmp;
905 tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
906 if (tmp == NULL)
907 goto err;
908 domains = tmp;
911 if (result != NULL)
912 (void) ldap_msgfree(result);
914 return (domains);
916 err:
917 free(domains);
918 if (result != NULL)
919 (void) ldap_msgfree(result);
920 return (NULL);
924 ad_disc_t
925 ad_disc_init(void)
927 struct ad_disc *ctx;
928 ctx = calloc(1, sizeof (struct ad_disc));
929 if (ctx != NULL)
930 DO_RES_NINIT(ctx);
932 ctx->domain_name.type = AD_STRING;
933 ctx->domain_guid.type = AD_UUID;
934 ctx->domain_controller.type = AD_DIRECTORY;
935 ctx->preferred_dc.type = AD_DIRECTORY;
936 ctx->site_name.type = AD_STRING;
937 ctx->forest_name.type = AD_STRING;
938 ctx->global_catalog.type = AD_DIRECTORY;
939 ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST;
940 ctx->trusted_domains.type = AD_TRUSTED_DOMAINS;
941 /* Site specific versions */
942 ctx->site_domain_controller.type = AD_DIRECTORY;
943 ctx->site_global_catalog.type = AD_DIRECTORY;
944 return (ctx);
947 void
948 ad_disc_fini(ad_disc_t ctx)
950 if (ctx == NULL)
951 return;
953 if (ctx->res_ninitted)
954 res_ndestroy(&ctx->res_state);
956 free(ctx->subnets);
958 free(ctx->domain_name.value);
960 free(ctx->domain_guid.value);
962 free(ctx->domain_controller.value);
964 free(ctx->preferred_dc.value);
966 free(ctx->site_name.value);
968 free(ctx->forest_name.value);
970 free(ctx->global_catalog.value);
972 free(ctx->domains_in_forest.value);
974 free(ctx->trusted_domains.value);
976 /* Site specific versions */
977 free(ctx->site_domain_controller.value);
979 free(ctx->site_global_catalog.value);
981 free(ctx);
984 void
985 ad_disc_refresh(ad_disc_t ctx)
987 if (ctx->res_ninitted) {
988 res_ndestroy(&ctx->res_state);
989 ctx->res_ninitted = 0;
991 (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state));
992 DO_RES_NINIT(ctx);
994 if (ctx->domain_name.state == AD_STATE_AUTO)
995 ctx->domain_name.state = AD_STATE_INVALID;
997 if (ctx->domain_guid.state == AD_STATE_AUTO)
998 ctx->domain_guid.state = AD_STATE_INVALID;
1000 if (ctx->domain_controller.state == AD_STATE_AUTO)
1001 ctx->domain_controller.state = AD_STATE_INVALID;
1003 if (ctx->preferred_dc.state == AD_STATE_AUTO)
1004 ctx->preferred_dc.state = AD_STATE_INVALID;
1006 if (ctx->site_name.state == AD_STATE_AUTO)
1007 ctx->site_name.state = AD_STATE_INVALID;
1009 if (ctx->forest_name.state == AD_STATE_AUTO)
1010 ctx->forest_name.state = AD_STATE_INVALID;
1012 if (ctx->global_catalog.state == AD_STATE_AUTO)
1013 ctx->global_catalog.state = AD_STATE_INVALID;
1015 if (ctx->domains_in_forest.state == AD_STATE_AUTO)
1016 ctx->domains_in_forest.state = AD_STATE_INVALID;
1018 if (ctx->trusted_domains.state == AD_STATE_AUTO)
1019 ctx->trusted_domains.state = AD_STATE_INVALID;
1021 if (ctx->site_domain_controller.state == AD_STATE_AUTO)
1022 ctx->site_domain_controller.state = AD_STATE_INVALID;
1024 if (ctx->site_global_catalog.state == AD_STATE_AUTO)
1025 ctx->site_global_catalog.state = AD_STATE_INVALID;
1030 * Called when the discovery cycle is done. Sets a master TTL
1031 * that will avoid doing new time-based discoveries too soon after
1032 * the last discovery cycle. Most interesting when the discovery
1033 * cycle failed, because then the TTLs on the individual items will
1034 * not be updated and may go stale.
1036 void
1037 ad_disc_done(ad_disc_t ctx)
1039 time_t now = time(NULL);
1041 ctx->expires_not_before = now + MINIMUM_TTL;
1042 ctx->expires_not_after = now + MAXIMUM_TTL;
1045 static void
1046 log_cds(ad_disc_t ctx, ad_disc_cds_t *cds)
1048 char buf[INET6_ADDRSTRLEN];
1049 struct addrinfo *ai;
1051 if (!DBG(DISC, 1) && ctx->status_fp == NULL)
1052 return;
1054 DEBUG1STATUS(ctx, "Candidate servers:");
1055 if (cds->cds_ds.host[0] == '\0') {
1056 DEBUG1STATUS(ctx, " (empty list)");
1057 return;
1060 while (cds->cds_ds.host[0] != '\0') {
1062 DEBUG1STATUS(ctx, " %s p=%d w=%d",
1063 cds->cds_ds.host,
1064 cds->cds_ds.priority,
1065 cds->cds_ds.weight);
1067 ai = cds->cds_ai;
1068 if (ai == NULL) {
1069 DEBUG1STATUS(ctx, " (no address)");
1071 while (ai != NULL) {
1072 int eai;
1074 eai = getnameinfo(ai->ai_addr, ai->ai_addrlen,
1075 buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
1076 if (eai != 0)
1077 (void) strlcpy(buf, "?", sizeof (buf));
1079 DEBUG1STATUS(ctx, " %s", buf);
1080 ai = ai->ai_next;
1082 cds++;
1086 static void
1087 log_ds(ad_disc_t ctx, ad_disc_ds_t *ds)
1089 char buf[INET6_ADDRSTRLEN];
1091 if (!DBG(DISC, 1) && ctx->status_fp == NULL)
1092 return;
1094 DEBUG1STATUS(ctx, "Responding servers:");
1095 if (ds->host[0] == '\0') {
1096 DEBUG1STATUS(ctx, " (empty list)");
1097 return;
1100 while (ds->host[0] != '\0') {
1102 DEBUG1STATUS(ctx, " %s", ds->host);
1103 DO_GETNAMEINFO(buf, sizeof (buf), &ds->addr);
1104 DEBUG1STATUS(ctx, " %s", buf);
1106 ds++;
1110 /* Discover joined Active Directory domainName */
1111 static ad_item_t *
1112 validate_DomainName(ad_disc_t ctx)
1114 char *dname, *srvname;
1115 int len, rc;
1117 if (is_valid(&ctx->domain_name))
1118 return (&ctx->domain_name);
1121 /* Try to find our domain by searching for DCs for it */
1122 DO_RES_NINIT(ctx);
1123 if (DBG(DISC, 1))
1124 logger(LOG_DEBUG, "Looking for our AD domain name...");
1125 rc = srv_getdom(&ctx->res_state,
1126 LDAP_SRV_HEAD DC_SRV_TAIL, &srvname);
1129 * If we can't find DCs by via res_nsearch() then there's no
1130 * point in trying anything else to discover the AD domain name.
1132 if (rc < 0) {
1133 if (DBG(DISC, 1))
1134 logger(LOG_DEBUG, "Can't find our domain name.");
1135 return (NULL);
1139 * We have the FQDN of the SRV RR name, so now we extract the
1140 * domainname suffix from it.
1142 dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) +
1143 1 /* for the dot between RR name and domainname */);
1145 free(srvname);
1147 if (dname == NULL) {
1148 logger(LOG_ERR, "Out of memory");
1149 return (NULL);
1152 /* Eat any trailing dot */
1153 len = strlen(dname);
1154 if (len > 0 && dname[len - 1] == '.')
1155 dname[len - 1] = '\0';
1157 if (DBG(DISC, 1))
1158 logger(LOG_DEBUG, "Our domain name: %s", dname);
1161 * There is no "time to live" on the discovered domain,
1162 * so passing zero as TTL here, making it non-expiring.
1163 * Note that current consumers do not auto-discover the
1164 * domain name, though a future installer could.
1166 update_item(&ctx->domain_name, dname, AD_STATE_AUTO, 0);
1168 return (&ctx->domain_name);
1172 char *
1173 ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered)
1175 char *domain_name = NULL;
1176 ad_item_t *domain_name_item;
1178 domain_name_item = validate_DomainName(ctx);
1180 if (domain_name_item) {
1181 domain_name = strdup(domain_name_item->value);
1182 if (auto_discovered != NULL)
1183 *auto_discovered =
1184 (domain_name_item->state == AD_STATE_AUTO);
1185 } else if (auto_discovered != NULL)
1186 *auto_discovered = B_FALSE;
1188 return (domain_name);
1192 /* Discover domain controllers */
1193 static ad_item_t *
1194 validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
1196 ad_disc_ds_t *dc = NULL;
1197 ad_disc_cds_t *cdc = NULL;
1198 boolean_t validate_global = B_FALSE;
1199 boolean_t validate_site = B_FALSE;
1200 ad_item_t *domain_name_item;
1201 char *domain_name;
1202 ad_item_t *site_name_item = NULL;
1203 char *site_name;
1204 ad_item_t *prefer_dc_item;
1205 ad_disc_ds_t *prefer_dc = NULL;
1207 /* If the values is fixed there will not be a site specific version */
1208 if (is_fixed(&ctx->domain_controller))
1209 return (&ctx->domain_controller);
1211 domain_name_item = validate_DomainName(ctx);
1212 if (domain_name_item == NULL)
1213 return (NULL);
1214 domain_name = (char *)domain_name_item->value;
1216 /* Get (optional) preferred DC. */
1217 prefer_dc_item = validate_PreferredDC(ctx);
1218 if (prefer_dc_item != NULL)
1219 prefer_dc = prefer_dc_item->value;
1221 if (req == AD_DISC_GLOBAL)
1222 validate_global = B_TRUE;
1223 else {
1224 if (is_fixed(&ctx->site_name))
1225 validate_site = B_TRUE;
1226 else if (req == AD_DISC_PREFER_SITE)
1227 validate_global = B_TRUE;
1230 if (validate_global) {
1231 if (!is_valid(&ctx->domain_controller) ||
1232 is_changed(&ctx->domain_controller, PARAM1,
1233 domain_name_item)) {
1236 * Lookup DNS SRV RR named
1237 * _ldap._tcp.dc._msdcs.<DomainName>
1239 DEBUG1STATUS(ctx, "DNS SRV query, dom=%s",
1240 domain_name);
1241 DO_RES_NINIT(ctx);
1242 cdc = srv_query(&ctx->res_state,
1243 LDAP_SRV_HEAD DC_SRV_TAIL,
1244 domain_name, prefer_dc);
1246 if (cdc == NULL) {
1247 DEBUG1STATUS(ctx, "(no DNS response)");
1248 return (NULL);
1250 log_cds(ctx, cdc);
1253 * Filter out unresponsive servers, and
1254 * save the domain info we get back.
1256 dc = ldap_ping(
1257 ctx,
1258 cdc,
1259 domain_name,
1260 DS_DS_FLAG);
1261 srv_free(cdc);
1262 cdc = NULL;
1264 if (dc == NULL) {
1265 DEBUG1STATUS(ctx, "(no LDAP response)");
1266 return (NULL);
1268 log_ds(ctx, dc);
1270 update_item(&ctx->domain_controller, dc,
1271 AD_STATE_AUTO, dc->ttl);
1272 update_version(&ctx->domain_controller, PARAM1,
1273 domain_name_item);
1275 return (&ctx->domain_controller);
1278 if (validate_site) {
1279 site_name_item = &ctx->site_name;
1280 site_name = (char *)site_name_item->value;
1282 if (!is_valid(&ctx->site_domain_controller) ||
1283 is_changed(&ctx->site_domain_controller, PARAM1,
1284 domain_name_item) ||
1285 is_changed(&ctx->site_domain_controller, PARAM2,
1286 site_name_item)) {
1287 char rr_name[DNS_MAX_NAME];
1290 * Lookup DNS SRV RR named
1291 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName>
1293 DEBUG1STATUS(ctx, "DNS SRV query, dom=%s, site=%s",
1294 domain_name, site_name);
1295 (void) snprintf(rr_name, sizeof (rr_name),
1296 LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL,
1297 site_name);
1298 DO_RES_NINIT(ctx);
1299 cdc = srv_query(&ctx->res_state, rr_name,
1300 domain_name, prefer_dc);
1302 if (cdc == NULL) {
1303 DEBUG1STATUS(ctx, "(no DNS response)");
1304 return (NULL);
1306 log_cds(ctx, cdc);
1309 * Filter out unresponsive servers, and
1310 * save the domain info we get back.
1312 dc = ldap_ping(
1313 ctx,
1314 cdc,
1315 domain_name,
1316 DS_DS_FLAG);
1317 srv_free(cdc);
1318 cdc = NULL;
1320 if (dc == NULL) {
1321 DEBUG1STATUS(ctx, "(no LDAP response)");
1322 return (NULL);
1324 log_ds(ctx, dc);
1326 update_item(&ctx->site_domain_controller, dc,
1327 AD_STATE_AUTO, dc->ttl);
1328 update_version(&ctx->site_domain_controller, PARAM1,
1329 domain_name_item);
1330 update_version(&ctx->site_domain_controller, PARAM2,
1331 site_name_item);
1333 return (&ctx->site_domain_controller);
1335 return (NULL);
1338 ad_disc_ds_t *
1339 ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
1340 boolean_t *auto_discovered)
1342 ad_item_t *domain_controller_item;
1343 ad_disc_ds_t *domain_controller = NULL;
1345 domain_controller_item = validate_DomainController(ctx, req);
1347 if (domain_controller_item != NULL) {
1348 domain_controller = ds_dup(domain_controller_item->value);
1349 if (auto_discovered != NULL)
1350 *auto_discovered =
1351 (domain_controller_item->state == AD_STATE_AUTO);
1352 } else if (auto_discovered != NULL)
1353 *auto_discovered = B_FALSE;
1355 return (domain_controller);
1360 * Discover the Domain GUID
1361 * This info comes from validate_DomainController()
1363 static ad_item_t *
1364 validate_DomainGUID(ad_disc_t ctx)
1366 ad_item_t *domain_controller_item;
1368 if (is_fixed(&ctx->domain_guid))
1369 return (&ctx->domain_guid);
1371 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1372 if (domain_controller_item == NULL)
1373 return (NULL);
1375 if (!is_valid(&ctx->domain_guid))
1376 return (NULL);
1378 return (&ctx->domain_guid);
1382 uchar_t *
1383 ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered)
1385 ad_item_t *domain_guid_item;
1386 uchar_t *domain_guid = NULL;
1388 domain_guid_item = validate_DomainGUID(ctx);
1389 if (domain_guid_item != NULL) {
1390 domain_guid = uuid_dup(domain_guid_item->value);
1391 if (auto_discovered != NULL)
1392 *auto_discovered =
1393 (domain_guid_item->state == AD_STATE_AUTO);
1394 } else if (auto_discovered != NULL)
1395 *auto_discovered = B_FALSE;
1397 return (domain_guid);
1402 * Discover site name (for multi-homed systems the first one found wins)
1403 * This info comes from validate_DomainController()
1405 static ad_item_t *
1406 validate_SiteName(ad_disc_t ctx)
1408 ad_item_t *domain_controller_item;
1410 if (is_fixed(&ctx->site_name))
1411 return (&ctx->site_name);
1413 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1414 if (domain_controller_item == NULL)
1415 return (NULL);
1417 if (!is_valid(&ctx->site_name))
1418 return (NULL);
1420 return (&ctx->site_name);
1424 char *
1425 ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered)
1427 ad_item_t *site_name_item;
1428 char *site_name = NULL;
1430 site_name_item = validate_SiteName(ctx);
1431 if (site_name_item != NULL) {
1432 site_name = strdup(site_name_item->value);
1433 if (auto_discovered != NULL)
1434 *auto_discovered =
1435 (site_name_item->state == AD_STATE_AUTO);
1436 } else if (auto_discovered != NULL)
1437 *auto_discovered = B_FALSE;
1439 return (site_name);
1445 * Discover forest name
1446 * This info comes from validate_DomainController()
1448 static ad_item_t *
1449 validate_ForestName(ad_disc_t ctx)
1451 ad_item_t *domain_controller_item;
1453 if (is_fixed(&ctx->forest_name))
1454 return (&ctx->forest_name);
1456 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1457 if (domain_controller_item == NULL)
1458 return (NULL);
1460 if (!is_valid(&ctx->forest_name))
1461 return (NULL);
1463 return (&ctx->forest_name);
1467 char *
1468 ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered)
1470 ad_item_t *forest_name_item;
1471 char *forest_name = NULL;
1473 forest_name_item = validate_ForestName(ctx);
1475 if (forest_name_item != NULL) {
1476 forest_name = strdup(forest_name_item->value);
1477 if (auto_discovered != NULL)
1478 *auto_discovered =
1479 (forest_name_item->state == AD_STATE_AUTO);
1480 } else if (auto_discovered != NULL)
1481 *auto_discovered = B_FALSE;
1483 return (forest_name);
1487 /* Discover global catalog servers */
1488 static ad_item_t *
1489 validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
1491 ad_disc_ds_t *gc = NULL;
1492 ad_disc_cds_t *cgc = NULL;
1493 boolean_t validate_global = B_FALSE;
1494 boolean_t validate_site = B_FALSE;
1495 ad_item_t *dc_item;
1496 ad_item_t *forest_name_item;
1497 ad_item_t *site_name_item;
1498 char *forest_name;
1499 char *site_name;
1501 /* If the values is fixed there will not be a site specific version */
1502 if (is_fixed(&ctx->global_catalog))
1503 return (&ctx->global_catalog);
1505 forest_name_item = validate_ForestName(ctx);
1506 if (forest_name_item == NULL)
1507 return (NULL);
1508 forest_name = (char *)forest_name_item->value;
1510 if (req == AD_DISC_GLOBAL)
1511 validate_global = B_TRUE;
1512 else {
1513 if (is_fixed(&ctx->site_name))
1514 validate_site = B_TRUE;
1515 else if (req == AD_DISC_PREFER_SITE)
1516 validate_global = B_TRUE;
1519 if (validate_global) {
1520 if (!is_valid(&ctx->global_catalog) ||
1521 is_changed(&ctx->global_catalog, PARAM1,
1522 forest_name_item)) {
1525 * See if our DC is also a GC.
1527 dc_item = validate_DomainController(ctx, req);
1528 if (dc_item != NULL) {
1529 ad_disc_ds_t *ds = dc_item->value;
1530 if ((ds->flags & DS_GC_FLAG) != 0) {
1531 DEBUG1STATUS(ctx,
1532 "DC is also a GC for %s",
1533 forest_name);
1534 gc = ds_dup(ds);
1535 if (gc != NULL) {
1536 gc->port = GC_PORT;
1537 goto update_global;
1543 * Lookup DNS SRV RR named:
1544 * _ldap._tcp.gc._msdcs.<ForestName>
1546 DEBUG1STATUS(ctx, "DNS SRV query, forest=%s",
1547 forest_name);
1548 DO_RES_NINIT(ctx);
1549 cgc = srv_query(&ctx->res_state,
1550 LDAP_SRV_HEAD GC_SRV_TAIL,
1551 forest_name, NULL);
1553 if (cgc == NULL) {
1554 DEBUG1STATUS(ctx, "(no DNS response)");
1555 return (NULL);
1557 log_cds(ctx, cgc);
1560 * Filter out unresponsive servers, and
1561 * save the domain info we get back.
1563 gc = ldap_ping(
1564 NULL,
1565 cgc,
1566 forest_name,
1567 DS_GC_FLAG);
1568 srv_free(cgc);
1569 cgc = NULL;
1571 if (gc == NULL) {
1572 DEBUG1STATUS(ctx, "(no LDAP response)");
1573 return (NULL);
1575 log_ds(ctx, gc);
1577 update_global:
1578 update_item(&ctx->global_catalog, gc,
1579 AD_STATE_AUTO, gc->ttl);
1580 update_version(&ctx->global_catalog, PARAM1,
1581 forest_name_item);
1583 return (&ctx->global_catalog);
1586 if (validate_site) {
1587 site_name_item = &ctx->site_name;
1588 site_name = (char *)site_name_item->value;
1590 if (!is_valid(&ctx->site_global_catalog) ||
1591 is_changed(&ctx->site_global_catalog, PARAM1,
1592 forest_name_item) ||
1593 is_changed(&ctx->site_global_catalog, PARAM2,
1594 site_name_item)) {
1595 char rr_name[DNS_MAX_NAME];
1598 * See if our DC is also a GC.
1600 dc_item = validate_DomainController(ctx, req);
1601 if (dc_item != NULL) {
1602 ad_disc_ds_t *ds = dc_item->value;
1603 if ((ds->flags & DS_GC_FLAG) != 0) {
1604 DEBUG1STATUS(ctx,
1605 "DC is also a GC for %s in %s",
1606 forest_name, site_name);
1607 gc = ds_dup(ds);
1608 if (gc != NULL) {
1609 gc->port = GC_PORT;
1610 goto update_site;
1616 * Lookup DNS SRV RR named:
1617 * _ldap._tcp.<siteName>._sites.gc.
1618 * _msdcs.<ForestName>
1620 DEBUG1STATUS(ctx, "DNS SRV query, forest=%s, site=%s",
1621 forest_name, site_name);
1622 (void) snprintf(rr_name, sizeof (rr_name),
1623 LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL,
1624 site_name);
1625 DO_RES_NINIT(ctx);
1626 cgc = srv_query(&ctx->res_state, rr_name,
1627 forest_name, NULL);
1629 if (cgc == NULL) {
1630 DEBUG1STATUS(ctx, "(no DNS response)");
1631 return (NULL);
1633 log_cds(ctx, cgc);
1636 * Filter out unresponsive servers, and
1637 * save the domain info we get back.
1639 gc = ldap_ping(
1640 NULL,
1641 cgc,
1642 forest_name,
1643 DS_GC_FLAG);
1644 srv_free(cgc);
1645 cgc = NULL;
1647 if (gc == NULL) {
1648 DEBUG1STATUS(ctx, "(no LDAP response)");
1649 return (NULL);
1651 log_ds(ctx, gc);
1653 update_site:
1654 update_item(&ctx->site_global_catalog, gc,
1655 AD_STATE_AUTO, gc->ttl);
1656 update_version(&ctx->site_global_catalog, PARAM1,
1657 forest_name_item);
1658 update_version(&ctx->site_global_catalog, PARAM2,
1659 site_name_item);
1661 return (&ctx->site_global_catalog);
1663 return (NULL);
1667 ad_disc_ds_t *
1668 ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req,
1669 boolean_t *auto_discovered)
1671 ad_disc_ds_t *global_catalog = NULL;
1672 ad_item_t *global_catalog_item;
1674 global_catalog_item = validate_GlobalCatalog(ctx, req);
1676 if (global_catalog_item != NULL) {
1677 global_catalog = ds_dup(global_catalog_item->value);
1678 if (auto_discovered != NULL)
1679 *auto_discovered =
1680 (global_catalog_item->state == AD_STATE_AUTO);
1681 } else if (auto_discovered != NULL)
1682 *auto_discovered = B_FALSE;
1684 return (global_catalog);
1688 static ad_item_t *
1689 validate_TrustedDomains(ad_disc_t ctx)
1691 LDAP *ld = NULL;
1692 ad_item_t *global_catalog_item;
1693 ad_item_t *forest_name_item;
1694 ad_disc_trusteddomains_t *trusted_domains;
1695 char *dn = NULL;
1696 char *forest_name_dn;
1697 int len;
1698 int num_parts;
1700 if (is_fixed(&ctx->trusted_domains))
1701 return (&ctx->trusted_domains);
1703 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
1704 if (global_catalog_item == NULL)
1705 return (NULL);
1707 forest_name_item = validate_ForestName(ctx);
1708 if (forest_name_item == NULL)
1709 return (NULL);
1711 if (!is_valid(&ctx->trusted_domains) ||
1712 is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) ||
1713 is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) {
1715 forest_name_dn = ldap_dns_to_dn(forest_name_item->value,
1716 &num_parts);
1717 if (forest_name_dn == NULL)
1718 return (NULL);
1720 len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1;
1721 dn = malloc(len);
1722 if (dn == NULL) {
1723 free(forest_name_dn);
1724 return (NULL);
1726 (void) snprintf(dn, len, "CN=System,%s", forest_name_dn);
1727 free(forest_name_dn);
1729 trusted_domains = ldap_lookup_trusted_domains(
1730 &ld, global_catalog_item->value, dn);
1732 if (ld != NULL)
1733 (void) ldap_unbind(ld);
1734 free(dn);
1736 if (trusted_domains == NULL)
1737 return (NULL);
1739 update_item(&ctx->trusted_domains, trusted_domains,
1740 AD_STATE_AUTO, 0);
1741 update_version(&ctx->trusted_domains, PARAM1,
1742 global_catalog_item);
1743 update_version(&ctx->trusted_domains, PARAM2,
1744 forest_name_item);
1747 return (&ctx->trusted_domains);
1751 ad_disc_trusteddomains_t *
1752 ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered)
1754 ad_disc_trusteddomains_t *trusted_domains = NULL;
1755 ad_item_t *trusted_domains_item;
1757 trusted_domains_item = validate_TrustedDomains(ctx);
1759 if (trusted_domains_item != NULL) {
1760 trusted_domains = td_dup(trusted_domains_item->value);
1761 if (auto_discovered != NULL)
1762 *auto_discovered =
1763 (trusted_domains_item->state == AD_STATE_AUTO);
1764 } else if (auto_discovered != NULL)
1765 *auto_discovered = B_FALSE;
1767 return (trusted_domains);
1771 static ad_item_t *
1772 validate_DomainsInForest(ad_disc_t ctx)
1774 ad_item_t *global_catalog_item;
1775 LDAP *ld = NULL;
1776 ad_disc_domainsinforest_t *domains_in_forest;
1778 if (is_fixed(&ctx->domains_in_forest))
1779 return (&ctx->domains_in_forest);
1781 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
1782 if (global_catalog_item == NULL)
1783 return (NULL);
1785 if (!is_valid(&ctx->domains_in_forest) ||
1786 is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) {
1788 domains_in_forest = ldap_lookup_domains_in_forest(
1789 &ld, global_catalog_item->value);
1791 if (ld != NULL)
1792 (void) ldap_unbind(ld);
1794 if (domains_in_forest == NULL)
1795 return (NULL);
1797 update_item(&ctx->domains_in_forest, domains_in_forest,
1798 AD_STATE_AUTO, 0);
1799 update_version(&ctx->domains_in_forest, PARAM1,
1800 global_catalog_item);
1802 return (&ctx->domains_in_forest);
1806 ad_disc_domainsinforest_t *
1807 ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered)
1809 ad_disc_domainsinforest_t *domains_in_forest = NULL;
1810 ad_item_t *domains_in_forest_item;
1812 domains_in_forest_item = validate_DomainsInForest(ctx);
1814 if (domains_in_forest_item != NULL) {
1815 domains_in_forest = df_dup(domains_in_forest_item->value);
1816 if (auto_discovered != NULL)
1817 *auto_discovered =
1818 (domains_in_forest_item->state == AD_STATE_AUTO);
1819 } else if (auto_discovered != NULL)
1820 *auto_discovered = B_FALSE;
1822 return (domains_in_forest);
1825 static ad_item_t *
1826 validate_PreferredDC(ad_disc_t ctx)
1828 if (is_valid(&ctx->preferred_dc))
1829 return (&ctx->preferred_dc);
1831 return (NULL);
1834 ad_disc_ds_t *
1835 ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered)
1837 ad_disc_ds_t *preferred_dc = NULL;
1838 ad_item_t *preferred_dc_item;
1840 preferred_dc_item = validate_PreferredDC(ctx);
1842 if (preferred_dc_item != NULL) {
1843 preferred_dc = ds_dup(preferred_dc_item->value);
1844 if (auto_discovered != NULL)
1845 *auto_discovered =
1846 (preferred_dc_item->state == AD_STATE_AUTO);
1847 } else if (auto_discovered != NULL)
1848 *auto_discovered = B_FALSE;
1850 return (preferred_dc);
1856 ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName)
1858 char *domain_name = NULL;
1859 if (domainName != NULL) {
1860 domain_name = strdup(domainName);
1861 if (domain_name == NULL)
1862 return (-1);
1863 update_item(&ctx->domain_name, domain_name,
1864 AD_STATE_FIXED, 0);
1865 } else if (ctx->domain_name.state == AD_STATE_FIXED)
1866 ctx->domain_name.state = AD_STATE_INVALID;
1867 return (0);
1871 ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
1873 char *domain_guid = NULL;
1874 if (u != NULL) {
1875 domain_guid = uuid_dup(u);
1876 if (domain_guid == NULL)
1877 return (-1);
1878 update_item(&ctx->domain_guid, domain_guid,
1879 AD_STATE_FIXED, 0);
1880 } else if (ctx->domain_guid.state == AD_STATE_FIXED)
1881 ctx->domain_guid.state = AD_STATE_INVALID;
1882 return (0);
1885 void
1886 auto_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
1888 char *domain_guid = NULL;
1890 if (is_fixed(&ctx->domain_guid))
1891 return;
1893 domain_guid = uuid_dup(u);
1894 if (domain_guid == NULL)
1895 return;
1896 update_item(&ctx->domain_guid, domain_guid, AD_STATE_AUTO, 0);
1900 ad_disc_set_DomainController(ad_disc_t ctx,
1901 const ad_disc_ds_t *domainController)
1903 ad_disc_ds_t *domain_controller = NULL;
1904 if (domainController != NULL) {
1905 domain_controller = ds_dup(domainController);
1906 if (domain_controller == NULL)
1907 return (-1);
1908 update_item(&ctx->domain_controller, domain_controller,
1909 AD_STATE_FIXED, 0);
1910 } else if (ctx->domain_controller.state == AD_STATE_FIXED)
1911 ctx->domain_controller.state = AD_STATE_INVALID;
1912 return (0);
1916 ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
1918 char *site_name = NULL;
1919 if (siteName != NULL) {
1920 site_name = strdup(siteName);
1921 if (site_name == NULL)
1922 return (-1);
1923 update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0);
1924 } else if (ctx->site_name.state == AD_STATE_FIXED)
1925 ctx->site_name.state = AD_STATE_INVALID;
1926 return (0);
1929 void
1930 auto_set_SiteName(ad_disc_t ctx, char *siteName)
1932 char *site_name = NULL;
1934 if (is_fixed(&ctx->site_name))
1935 return;
1937 site_name = strdup(siteName);
1938 if (site_name == NULL)
1939 return;
1940 update_item(&ctx->site_name, site_name, AD_STATE_AUTO, 0);
1944 ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
1946 char *forest_name = NULL;
1947 if (forestName != NULL) {
1948 forest_name = strdup(forestName);
1949 if (forest_name == NULL)
1950 return (-1);
1951 update_item(&ctx->forest_name, forest_name,
1952 AD_STATE_FIXED, 0);
1953 } else if (ctx->forest_name.state == AD_STATE_FIXED)
1954 ctx->forest_name.state = AD_STATE_INVALID;
1955 return (0);
1958 void
1959 auto_set_ForestName(ad_disc_t ctx, char *forestName)
1961 char *forest_name = NULL;
1963 if (is_fixed(&ctx->forest_name))
1964 return;
1966 forest_name = strdup(forestName);
1967 if (forest_name == NULL)
1968 return;
1969 update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
1973 ad_disc_set_GlobalCatalog(ad_disc_t ctx,
1974 const ad_disc_ds_t *globalCatalog)
1976 ad_disc_ds_t *global_catalog = NULL;
1977 if (globalCatalog != NULL) {
1978 global_catalog = ds_dup(globalCatalog);
1979 if (global_catalog == NULL)
1980 return (-1);
1981 update_item(&ctx->global_catalog, global_catalog,
1982 AD_STATE_FIXED, 0);
1983 } else if (ctx->global_catalog.state == AD_STATE_FIXED)
1984 ctx->global_catalog.state = AD_STATE_INVALID;
1985 return (0);
1989 ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *pref_dc)
1991 ad_disc_ds_t *new_pref_dc = NULL;
1992 if (pref_dc != NULL) {
1993 new_pref_dc = ds_dup(pref_dc);
1994 if (new_pref_dc == NULL)
1995 return (-1);
1996 update_item(&ctx->preferred_dc, new_pref_dc,
1997 AD_STATE_FIXED, 0);
1998 } else if (ctx->preferred_dc.state == AD_STATE_FIXED)
1999 ctx->preferred_dc.state = AD_STATE_INVALID;
2000 return (0);
2003 void
2004 ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *fp)
2006 ctx->status_fp = fp;
2011 ad_disc_unset(ad_disc_t ctx)
2013 if (ctx->domain_name.state == AD_STATE_FIXED)
2014 ctx->domain_name.state = AD_STATE_INVALID;
2016 if (ctx->domain_controller.state == AD_STATE_FIXED)
2017 ctx->domain_controller.state = AD_STATE_INVALID;
2019 if (ctx->preferred_dc.state == AD_STATE_FIXED)
2020 ctx->preferred_dc.state = AD_STATE_INVALID;
2022 if (ctx->site_name.state == AD_STATE_FIXED)
2023 ctx->site_name.state = AD_STATE_INVALID;
2025 if (ctx->forest_name.state == AD_STATE_FIXED)
2026 ctx->forest_name.state = AD_STATE_INVALID;
2028 if (ctx->global_catalog.state == AD_STATE_FIXED)
2029 ctx->global_catalog.state = AD_STATE_INVALID;
2031 return (0);
2035 * ad_disc_get_TTL
2037 * This routines the time to live for AD
2038 * auto discovered items.
2040 * Returns:
2041 * -1 if there are no TTL items
2042 * 0 if there are expired items
2043 * else the number of seconds
2045 * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it
2046 * is positive -- min() greater than zero.
2048 #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \
2049 (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x))))
2051 ad_disc_get_TTL(ad_disc_t ctx)
2053 time_t expires;
2054 int ttl;
2056 expires = MIN_GT_ZERO(ctx->domain_controller.expires,
2057 ctx->global_catalog.expires);
2058 expires = MIN_GT_ZERO(expires, ctx->site_domain_controller.expires);
2059 expires = MIN_GT_ZERO(expires, ctx->site_global_catalog.expires);
2061 if (expires == -1) {
2062 return (-1);
2065 if (ctx->expires_not_before != 0 &&
2066 expires < ctx->expires_not_before) {
2067 expires = ctx->expires_not_before;
2070 if (ctx->expires_not_after != 0 &&
2071 expires > ctx->expires_not_after) {
2072 expires = ctx->expires_not_after;
2075 ttl = expires - time(NULL);
2077 if (ttl < 0) {
2078 return (0);
2080 return (ttl);
2083 boolean_t
2084 ad_disc_SubnetChanged(ad_disc_t ctx)
2086 ad_subnet_t *subnets;
2088 if (ctx->subnets_changed || ctx->subnets == NULL)
2089 return (B_TRUE);
2091 if ((subnets = find_subnets()) != NULL) {
2092 if (cmpsubnets(subnets, ctx->subnets) != 0)
2093 ctx->subnets_changed = B_TRUE;
2094 free(subnets);
2097 return (ctx->subnets_changed);