1c8e26105Sjp /* 2c8e26105Sjp * CDDL HEADER START 3c8e26105Sjp * 4c8e26105Sjp * The contents of this file are subject to the terms of the 5c8e26105Sjp * Common Development and Distribution License (the "License"). 6c8e26105Sjp * You may not use this file except in compliance with the License. 7c8e26105Sjp * 8c8e26105Sjp * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9c8e26105Sjp * or http://www.opensolaris.org/os/licensing. 10c8e26105Sjp * See the License for the specific language governing permissions 11c8e26105Sjp * and limitations under the License. 12c8e26105Sjp * 13c8e26105Sjp * When distributing Covered Code, include this CDDL HEADER in each 14c8e26105Sjp * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15c8e26105Sjp * If applicable, add the following below this CDDL HEADER, with the 16c8e26105Sjp * fields enclosed by brackets "[]" replaced with your own identifying 17c8e26105Sjp * information: Portions Copyright [yyyy] [name of copyright owner] 18c8e26105Sjp * 19c8e26105Sjp * CDDL HEADER END 20c8e26105Sjp */ 21c8e26105Sjp 22c8e26105Sjp /* 237a8a68f5SJulian Pullen * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24c8e26105Sjp * Use is subject to license terms. 25c8e26105Sjp */ 26c8e26105Sjp 270dcc7149Snw /* 280dcc7149Snw * Active Directory Auto-Discovery. 290dcc7149Snw * 300dcc7149Snw * This [project private] API allows the caller to provide whatever 310dcc7149Snw * details it knows a priori (i.e., provided via configuration so as to 320dcc7149Snw * override auto-discovery) and in any order. Then the caller can ask 330dcc7149Snw * for any of the auto-discoverable parameters in any order. 340dcc7149Snw * 350dcc7149Snw * But there is an actual order in which discovery must be done. Given 360dcc7149Snw * the discovery mechanism implemented here, that order is: 370dcc7149Snw * 380dcc7149Snw * - the domain name joined must be discovered first 390dcc7149Snw * - then the domain controllers 400dcc7149Snw * - then the forest name and site name 410dcc7149Snw * - then the global catalog servers, and site-specific domain 420dcc7149Snw * controllers and global catalog servers. 430dcc7149Snw * 440dcc7149Snw * The API does not require it be called in the same order because there 450dcc7149Snw * may be other discovery mechanisms in the future, and exposing 460dcc7149Snw * ordering requirements of the current mechanism now can create trouble 470dcc7149Snw * down the line. Also, this makes the API easier to use now, which 480dcc7149Snw * means less work to do some day when we make this a public API. 490dcc7149Snw * 500dcc7149Snw * Domain discovery is done by res_nsearch() of the DNS SRV RR name for 510dcc7149Snw * domain controllers. As long as the joined domain appears in the DNS 520dcc7149Snw * resolver's search list then we'll find it. 530dcc7149Snw * 540dcc7149Snw * Domain controller discovery is a matter of formatting the DNS SRV RR 550dcc7149Snw * FQDN for domain controllers and doing a lookup for them. Knowledge 560dcc7149Snw * of the domain name is not fundamentally required, but we separate the 570dcc7149Snw * two processes, which in practice can lead to one more DNS lookup than 580dcc7149Snw * is strictly required. 590dcc7149Snw * 600dcc7149Snw * Forest and site name discovery require an LDAP search of the AD 610dcc7149Snw * "configuration partition" at a domain controller for the joined 620dcc7149Snw * domain. Forest and site name discovery depend on knowing the joined 630dcc7149Snw * domain name and domain controllers for that domain. 640dcc7149Snw * 650dcc7149Snw * Global catalog server discovery requires knowledge of the forest 660dcc7149Snw * name in order to format the DNS SRV RR FQDN to lookup. Site-specific 670dcc7149Snw * domain controller discovery depends on knowing the site name (and, 680dcc7149Snw * therefore, joined domain, ...). Site-specific global catalog server 690dcc7149Snw * discovery depends on knowledge of the forest and site names, which 700dcc7149Snw * depend on... 710dcc7149Snw * 720dcc7149Snw * All the work of discovering particular items is done by functions 730dcc7149Snw * named validate_<item>(). Each such function calls validate_<item>() 740dcc7149Snw * for any items that it depends on. 750dcc7149Snw * 760dcc7149Snw * This API is not thread-safe. 770dcc7149Snw */ 780dcc7149Snw 79c8e26105Sjp 80c8e26105Sjp #include <stdio.h> 81c8e26105Sjp #include <string.h> 82c8e26105Sjp #include <strings.h> 83c8e26105Sjp #include <unistd.h> 844d61c878SJulian Pullen #include <assert.h> 85c8e26105Sjp #include <stdlib.h> 86c8e26105Sjp #include <net/if.h> 87c8e26105Sjp #include <net/if.h> 88c8e26105Sjp #include <sys/types.h> 89c8e26105Sjp #include <sys/socket.h> 90c8e26105Sjp #include <sys/sockio.h> 91c8e26105Sjp #include <netinet/in.h> 92c8e26105Sjp #include <netinet/in.h> 93c8e26105Sjp #include <arpa/inet.h> 94c8e26105Sjp #include <arpa/nameser.h> 95c8e26105Sjp #include <resolv.h> 96c8e26105Sjp #include <netdb.h> 97c8e26105Sjp #include <ctype.h> 98c8e26105Sjp #include <errno.h> 99c8e26105Sjp #include <ldap.h> 100c8e26105Sjp #include <sasl/sasl.h> 1014d61c878SJulian Pullen #include <sys/u8_textprep.h> 1027a8a68f5SJulian Pullen #include <syslog.h> 1037a8a68f5SJulian Pullen #include "adutils_impl.h" 104c8e26105Sjp #include "addisc.h" 105c8e26105Sjp 106c8e26105Sjp 1074d61c878SJulian Pullen enum ad_item_state { 1084d61c878SJulian Pullen AD_STATE_INVALID = 0, /* The value is not valid */ 1094d61c878SJulian Pullen AD_STATE_FIXED, /* The value was fixed by caller */ 1104d61c878SJulian Pullen AD_STATE_AUTO /* The value is auto discovered */ 1114d61c878SJulian Pullen }; 1124d61c878SJulian Pullen 1134d61c878SJulian Pullen enum ad_data_type { 1144d61c878SJulian Pullen AD_STRING = 123, 1154d61c878SJulian Pullen AD_DIRECTORY, 1164d61c878SJulian Pullen AD_DOMAINS_IN_FOREST, 1174d61c878SJulian Pullen AD_TRUSTED_DOMAINS 1184d61c878SJulian Pullen }; 1194d61c878SJulian Pullen 1204d61c878SJulian Pullen 1214d61c878SJulian Pullen typedef struct ad_subnet { 1224d61c878SJulian Pullen char subnet[24]; 1234d61c878SJulian Pullen } ad_subnet_t; 1244d61c878SJulian Pullen 1254d61c878SJulian Pullen 1264d61c878SJulian Pullen typedef struct ad_item { 1274d61c878SJulian Pullen enum ad_item_state state; 1284d61c878SJulian Pullen enum ad_data_type type; 1294d61c878SJulian Pullen void *value; 1304d61c878SJulian Pullen time_t ttl; 1314d61c878SJulian Pullen unsigned int version; /* Version is only changed */ 1324d61c878SJulian Pullen /* if the value changes */ 1334d61c878SJulian Pullen #define PARAM1 0 1344d61c878SJulian Pullen #define PARAM2 1 1354d61c878SJulian Pullen int param_version[2]; 1364d61c878SJulian Pullen /* These holds the version of */ 1374d61c878SJulian Pullen /* dependents so that a dependent */ 1384d61c878SJulian Pullen /* change can be detected */ 1394d61c878SJulian Pullen } ad_item_t; 1404d61c878SJulian Pullen 1414d61c878SJulian Pullen typedef struct ad_disc { 1424d61c878SJulian Pullen struct __res_state res_state; 1434d61c878SJulian Pullen int res_ninitted; 1444d61c878SJulian Pullen ad_subnet_t *subnets; 1457a8a68f5SJulian Pullen boolean_t subnets_changed; 1464d61c878SJulian Pullen time_t subnets_last_check; 1474d61c878SJulian Pullen ad_item_t domain_name; /* DNS hostname string */ 1484d61c878SJulian Pullen ad_item_t domain_controller; /* Directory hostname and */ 1494d61c878SJulian Pullen /* port array */ 1504d61c878SJulian Pullen ad_item_t site_name; /* String */ 1514d61c878SJulian Pullen ad_item_t forest_name; /* DNS forestname string */ 1524d61c878SJulian Pullen ad_item_t global_catalog; /* Directory hostname and */ 1534d61c878SJulian Pullen /* port array */ 1544d61c878SJulian Pullen ad_item_t domains_in_forest; /* DNS domainname and SID */ 1554d61c878SJulian Pullen /* array */ 1564d61c878SJulian Pullen ad_item_t trusted_domains; /* DNS domainname and trust */ 1574d61c878SJulian Pullen /* direction array */ 1584d61c878SJulian Pullen /* Site specfic versions */ 1594d61c878SJulian Pullen ad_item_t site_domain_controller; /* Directory hostname and */ 1604d61c878SJulian Pullen /* port array */ 1614d61c878SJulian Pullen ad_item_t site_global_catalog; /* Directory hostname and */ 1624d61c878SJulian Pullen /* port array */ 1634d61c878SJulian Pullen } ad_disc; 1644d61c878SJulian Pullen 165c8e26105Sjp 166c8e26105Sjp #define DNS_MAX_NAME NS_MAXDNAME 167c8e26105Sjp 168c8e26105Sjp 169c8e26105Sjp /* SRV RR names for various queries */ 170c8e26105Sjp #define LDAP_SRV_HEAD "_ldap._tcp." 171c8e26105Sjp #define SITE_SRV_MIDDLE "%s._sites." 172c8e26105Sjp #define GC_SRV_TAIL "gc._msdcs" 173c8e26105Sjp #define DC_SRV_TAIL "dc._msdcs" 174c8e26105Sjp #define ALL_GC_SRV_TAIL "_gc._tcp" 175c8e26105Sjp #define PDC_SRV "_ldap._tcp.pdc._msdcs.%s" 176c8e26105Sjp 177c8e26105Sjp /* A RR name for all GCs -- last resort this works */ 178c8e26105Sjp #define GC_ALL_A_NAME_FSTR "gc._msdcs.%s." 179c8e26105Sjp 180c8e26105Sjp 1810dcc7149Snw /* 1820dcc7149Snw * We try res_ninit() whenever we don't have one. res_ninit() fails if 1830dcc7149Snw * idmapd is running before the network is up! 1840dcc7149Snw */ 1850dcc7149Snw #define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \ 1864d61c878SJulian Pullen (ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1) 187c8e26105Sjp 188c8e26105Sjp #define is_fixed(item) \ 1894d61c878SJulian Pullen ((item)->state == AD_STATE_FIXED) 190c8e26105Sjp 191c8e26105Sjp #define is_changed(item, num, param) \ 192c8e26105Sjp ((item)->param_version[num] != (param)->version) 193c8e26105Sjp 194c8e26105Sjp /*LINTLIBRARY*/ 195c8e26105Sjp 196c8e26105Sjp /* 197c8e26105Sjp * Function definitions 198c8e26105Sjp */ 1994d61c878SJulian Pullen static ad_item_t * 2004d61c878SJulian Pullen validate_SiteName(ad_disc_t ctx); 201c8e26105Sjp 202c8e26105Sjp 203c8e26105Sjp 204c8e26105Sjp static void 205c8e26105Sjp update_version(ad_item_t *item, int num, ad_item_t *param) 206c8e26105Sjp { 207c8e26105Sjp item->param_version[num] = param->version; 208c8e26105Sjp } 209c8e26105Sjp 210c8e26105Sjp 2114d61c878SJulian Pullen 2127a8a68f5SJulian Pullen static boolean_t 2134d61c878SJulian Pullen is_valid(ad_item_t *item) 214c8e26105Sjp { 2154d61c878SJulian Pullen if (item->value != NULL) { 2164d61c878SJulian Pullen if (item->state == AD_STATE_FIXED) 2177a8a68f5SJulian Pullen return (B_TRUE); 2184d61c878SJulian Pullen if (item->state == AD_STATE_AUTO && 2194d61c878SJulian Pullen (item->ttl == 0 || item->ttl > time(NULL))) 2207a8a68f5SJulian Pullen return (B_TRUE); 2214d61c878SJulian Pullen } 2227a8a68f5SJulian Pullen return (B_FALSE); 223c8e26105Sjp } 224c8e26105Sjp 225c8e26105Sjp 226c8e26105Sjp static void 2274d61c878SJulian Pullen update_item(ad_item_t *item, void *value, enum ad_item_state state, 228c8e26105Sjp uint32_t ttl) 229c8e26105Sjp { 2304d61c878SJulian Pullen if (item->value != NULL && value != NULL) { 2314d61c878SJulian Pullen if ((item->type == AD_STRING && 2324d61c878SJulian Pullen strcmp(item->value, value) != 0) || 2334d61c878SJulian Pullen (item->type == AD_DIRECTORY && 2344d61c878SJulian Pullen ad_disc_compare_ds(item->value, value) != 0)|| 2354d61c878SJulian Pullen (item->type == AD_DOMAINS_IN_FOREST && 2364d61c878SJulian Pullen ad_disc_compare_domainsinforest(item->value, value) != 0) || 2374d61c878SJulian Pullen (item->type == AD_TRUSTED_DOMAINS && 2384d61c878SJulian Pullen ad_disc_compare_trusteddomains(item->value, value) != 0)) 239c8e26105Sjp item->version++; 2404d61c878SJulian Pullen } else if (item->value != value) 241c8e26105Sjp item->version++; 242c8e26105Sjp 2434d61c878SJulian Pullen if (item->value != NULL) 2444d61c878SJulian Pullen free(item->value); 245c8e26105Sjp 2464d61c878SJulian Pullen item->value = value; 2474d61c878SJulian Pullen item->state = state; 248c8e26105Sjp 249c8e26105Sjp if (ttl == 0) 250c8e26105Sjp item->ttl = 0; 251c8e26105Sjp else 252c8e26105Sjp item->ttl = time(NULL) + ttl; 253c8e26105Sjp } 254c8e26105Sjp 255c8e26105Sjp 2564d61c878SJulian Pullen /* Compare DS lists */ 2574d61c878SJulian Pullen int 2584d61c878SJulian Pullen ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) 259c8e26105Sjp { 2607a8a68f5SJulian Pullen int i, j; 2617a8a68f5SJulian Pullen int num_ds1; 2627a8a68f5SJulian Pullen int num_ds2; 2637a8a68f5SJulian Pullen boolean_t match; 264c8e26105Sjp 2654d61c878SJulian Pullen for (i = 0; ds1[i].host[0] != '\0'; i++) 2664d61c878SJulian Pullen continue; 2674d61c878SJulian Pullen num_ds1 = i; 2684d61c878SJulian Pullen for (j = 0; ds2[j].host[0] != '\0'; j++) 2694d61c878SJulian Pullen continue; 2704d61c878SJulian Pullen num_ds2 = j; 2714d61c878SJulian Pullen if (num_ds1 != num_ds2) 2724d61c878SJulian Pullen return (1); 273c8e26105Sjp 2744d61c878SJulian Pullen for (i = 0; i < num_ds1; i++) { 2757a8a68f5SJulian Pullen match = B_FALSE; 2764d61c878SJulian Pullen for (j = 0; j < num_ds2; j++) { 277928e1f97SJordan Brown if (strcmp(ds1[i].host, ds2[j].host) == 0 && 278928e1f97SJordan Brown ds1[i].port == ds2[j].port) { 2797a8a68f5SJulian Pullen match = B_TRUE; 2804d61c878SJulian Pullen break; 2814d61c878SJulian Pullen } 2824d61c878SJulian Pullen } 2834d61c878SJulian Pullen if (!match) 2844d61c878SJulian Pullen return (1); 2854d61c878SJulian Pullen } 2864d61c878SJulian Pullen return (0); 2874d61c878SJulian Pullen } 288c8e26105Sjp 2894d61c878SJulian Pullen 2904d61c878SJulian Pullen /* Copy a list of DSs */ 2914d61c878SJulian Pullen static idmap_ad_disc_ds_t * 2924d61c878SJulian Pullen ds_dup(const idmap_ad_disc_ds_t *srv) 2934d61c878SJulian Pullen { 2944d61c878SJulian Pullen int i; 2954d61c878SJulian Pullen int size; 2964d61c878SJulian Pullen idmap_ad_disc_ds_t *new = NULL; 2974d61c878SJulian Pullen 2984d61c878SJulian Pullen for (i = 0; srv[i].host[0] != '\0'; i++) 2994d61c878SJulian Pullen continue; 3004d61c878SJulian Pullen 3014d61c878SJulian Pullen size = (i + 1) * sizeof (idmap_ad_disc_ds_t); 3024d61c878SJulian Pullen new = malloc(size); 3034d61c878SJulian Pullen if (new != NULL) 3044d61c878SJulian Pullen memcpy(new, srv, size); 3054d61c878SJulian Pullen return (new); 306c8e26105Sjp } 307c8e26105Sjp 308c8e26105Sjp 3094d61c878SJulian Pullen int 3104d61c878SJulian Pullen ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1, 3114d61c878SJulian Pullen ad_disc_trusteddomains_t *td2) 312c8e26105Sjp { 3137a8a68f5SJulian Pullen int i, j; 3147a8a68f5SJulian Pullen int num_td1; 3157a8a68f5SJulian Pullen int num_td2; 3167a8a68f5SJulian Pullen boolean_t match; 317c8e26105Sjp 3184d61c878SJulian Pullen for (i = 0; td1[i].domain[0] != '\0'; i++) 3194d61c878SJulian Pullen continue; 3204d61c878SJulian Pullen num_td1 = i; 321c8e26105Sjp 3224d61c878SJulian Pullen for (j = 0; td2[j].domain[0] != '\0'; j++) 3234d61c878SJulian Pullen continue; 3244d61c878SJulian Pullen num_td2 = j; 3254d61c878SJulian Pullen 3264d61c878SJulian Pullen if (num_td1 != num_td2) 3274d61c878SJulian Pullen return (1); 3284d61c878SJulian Pullen 3294d61c878SJulian Pullen for (i = 0; i < num_td1; i++) { 3307a8a68f5SJulian Pullen match = B_FALSE; 3314d61c878SJulian Pullen for (j = 0; j < num_td2; j++) { 3321fcced4cSJordan Brown if (domain_eq(td1[i].domain, td2[j].domain)) { 3337a8a68f5SJulian Pullen match = B_TRUE; 3344d61c878SJulian Pullen break; 3354d61c878SJulian Pullen } 3364d61c878SJulian Pullen } 3374d61c878SJulian Pullen if (!match) 3384d61c878SJulian Pullen return (1); 3394d61c878SJulian Pullen } 3404d61c878SJulian Pullen return (0); 3414d61c878SJulian Pullen } 3424d61c878SJulian Pullen 3434d61c878SJulian Pullen 3444d61c878SJulian Pullen 3454d61c878SJulian Pullen /* Copy a list of Trusted Domains */ 3464d61c878SJulian Pullen static ad_disc_trusteddomains_t * 3474d61c878SJulian Pullen td_dup(const ad_disc_trusteddomains_t *td) 3484d61c878SJulian Pullen { 3494d61c878SJulian Pullen int i; 3504d61c878SJulian Pullen int size; 3514d61c878SJulian Pullen ad_disc_trusteddomains_t *new = NULL; 3524d61c878SJulian Pullen 3534d61c878SJulian Pullen for (i = 0; td[i].domain[0] != '\0'; i++) 3544d61c878SJulian Pullen continue; 3554d61c878SJulian Pullen 3564d61c878SJulian Pullen size = (i + 1) * sizeof (ad_disc_trusteddomains_t); 3574d61c878SJulian Pullen new = malloc(size); 3584d61c878SJulian Pullen if (new != NULL) 3594d61c878SJulian Pullen memcpy(new, td, size); 3604d61c878SJulian Pullen return (new); 3614d61c878SJulian Pullen } 3624d61c878SJulian Pullen 3634d61c878SJulian Pullen 3644d61c878SJulian Pullen 3654d61c878SJulian Pullen int 3664d61c878SJulian Pullen ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1, 3674d61c878SJulian Pullen ad_disc_domainsinforest_t *df2) 3684d61c878SJulian Pullen { 3697a8a68f5SJulian Pullen int i, j; 3707a8a68f5SJulian Pullen int num_df1; 3717a8a68f5SJulian Pullen int num_df2; 3727a8a68f5SJulian Pullen boolean_t match; 3734d61c878SJulian Pullen 3744d61c878SJulian Pullen for (i = 0; df1[i].domain[0] != '\0'; i++) 3754d61c878SJulian Pullen continue; 3764d61c878SJulian Pullen num_df1 = i; 3774d61c878SJulian Pullen 3784d61c878SJulian Pullen for (j = 0; df2[j].domain[0] != '\0'; j++) 3794d61c878SJulian Pullen continue; 3804d61c878SJulian Pullen num_df2 = j; 3814d61c878SJulian Pullen 3824d61c878SJulian Pullen if (num_df1 != num_df2) 3834d61c878SJulian Pullen return (1); 3844d61c878SJulian Pullen 3854d61c878SJulian Pullen for (i = 0; i < num_df1; i++) { 3867a8a68f5SJulian Pullen match = B_FALSE; 3874d61c878SJulian Pullen for (j = 0; j < num_df2; j++) { 3881fcced4cSJordan Brown if (domain_eq(df1[i].domain, df2[j].domain) && 389928e1f97SJordan Brown strcmp(df1[i].sid, df2[j].sid) == 0) { 3907a8a68f5SJulian Pullen match = B_TRUE; 3914d61c878SJulian Pullen break; 3924d61c878SJulian Pullen } 3934d61c878SJulian Pullen } 3944d61c878SJulian Pullen if (!match) 3954d61c878SJulian Pullen return (1); 3964d61c878SJulian Pullen } 3974d61c878SJulian Pullen return (0); 3984d61c878SJulian Pullen } 399c8e26105Sjp 4004d61c878SJulian Pullen 4014d61c878SJulian Pullen 4024d61c878SJulian Pullen /* Copy a list of Trusted Domains */ 4034d61c878SJulian Pullen static ad_disc_domainsinforest_t * 4044d61c878SJulian Pullen df_dup(const ad_disc_domainsinforest_t *df) 4054d61c878SJulian Pullen { 4064d61c878SJulian Pullen int i; 4074d61c878SJulian Pullen int size; 4084d61c878SJulian Pullen ad_disc_domainsinforest_t *new = NULL; 4094d61c878SJulian Pullen 4104d61c878SJulian Pullen for (i = 0; df[i].domain[0] != '\0'; i++) 4114d61c878SJulian Pullen continue; 4124d61c878SJulian Pullen 4134d61c878SJulian Pullen size = (i + 1) * sizeof (ad_disc_domainsinforest_t); 4144d61c878SJulian Pullen new = malloc(size); 4154d61c878SJulian Pullen if (new != NULL) 4164d61c878SJulian Pullen memcpy(new, df, size); 4174d61c878SJulian Pullen return (new); 418c8e26105Sjp } 419c8e26105Sjp 420c8e26105Sjp 421c8e26105Sjp 4224d61c878SJulian Pullen 4234d61c878SJulian Pullen 424c8e26105Sjp /* 425c8e26105Sjp * Returns an array of IPv4 address/prefix length 426c8e26105Sjp * The last subnet is NULL 427c8e26105Sjp */ 428c8e26105Sjp static ad_subnet_t * 429c8e26105Sjp find_subnets() 430c8e26105Sjp { 431c8e26105Sjp int sock, n, i; 432c8e26105Sjp struct lifconf lifc; 433c8e26105Sjp struct lifreq lifr, *lifrp; 434c8e26105Sjp struct lifnum lifn; 435c8e26105Sjp uint32_t prefix_len; 436c8e26105Sjp char *s; 437c8e26105Sjp ad_subnet_t *results; 438c8e26105Sjp 439c8e26105Sjp lifrp = &lifr; 440c8e26105Sjp 441c8e26105Sjp if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 4427a8a68f5SJulian Pullen logger(LOG_ERR, "Failed to open IPv4 socket for " 44371590c90Snw "listing network interfaces (%s)", strerror(errno)); 444c8e26105Sjp return (NULL); 445c8e26105Sjp } 446c8e26105Sjp 447c8e26105Sjp lifn.lifn_family = AF_INET; 448c8e26105Sjp lifn.lifn_flags = 0; 449c8e26105Sjp if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { 4507a8a68f5SJulian Pullen logger(LOG_ERR, 45171590c90Snw "Failed to find the number of network interfaces (%s)", 45271590c90Snw strerror(errno)); 453c8e26105Sjp close(sock); 454c8e26105Sjp return (NULL); 455c8e26105Sjp } 456c8e26105Sjp 457c8e26105Sjp if (lifn.lifn_count < 1) { 4587a8a68f5SJulian Pullen logger(LOG_ERR, "No IPv4 network interfaces found"); 459c8e26105Sjp close(sock); 460c8e26105Sjp return (NULL); 461c8e26105Sjp } 462c8e26105Sjp 463c8e26105Sjp lifc.lifc_family = AF_INET; 464c8e26105Sjp lifc.lifc_flags = 0; 465c8e26105Sjp lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 466c8e26105Sjp lifc.lifc_buf = malloc(lifc.lifc_len); 467c8e26105Sjp 468c8e26105Sjp if (lifc.lifc_buf == NULL) { 4697a8a68f5SJulian Pullen logger(LOG_ERR, "Out of memory"); 470c8e26105Sjp close(sock); 471c8e26105Sjp return (NULL); 472c8e26105Sjp } 473c8e26105Sjp 474c8e26105Sjp if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { 4757a8a68f5SJulian Pullen logger(LOG_ERR, "Failed to list network interfaces (%s)", 47671590c90Snw strerror(errno)); 477c8e26105Sjp free(lifc.lifc_buf); 478c8e26105Sjp close(sock); 479c8e26105Sjp return (NULL); 480c8e26105Sjp } 481c8e26105Sjp 482c8e26105Sjp n = lifc.lifc_len / (int)sizeof (struct lifreq); 483c8e26105Sjp 484c8e26105Sjp if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) { 485c8e26105Sjp free(lifc.lifc_buf); 486c8e26105Sjp close(sock); 487c8e26105Sjp return (NULL); 488c8e26105Sjp } 489c8e26105Sjp 490c8e26105Sjp for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) { 491c8e26105Sjp if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0) 492c8e26105Sjp continue; 493c8e26105Sjp 494c8e26105Sjp if ((lifrp->lifr_flags & IFF_UP) == 0) 495c8e26105Sjp continue; 496c8e26105Sjp 497c8e26105Sjp if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0) 498c8e26105Sjp continue; 499c8e26105Sjp 500c8e26105Sjp prefix_len = lifrp->lifr_addrlen; 501c8e26105Sjp 502c8e26105Sjp s = inet_ntoa(((struct sockaddr_in *) 503c8e26105Sjp &lifrp->lifr_addr)->sin_addr); 504c8e26105Sjp 505c8e26105Sjp (void) snprintf(results[i].subnet, sizeof (ad_subnet_t), 506c8e26105Sjp "%s/%d", s, prefix_len); 507c8e26105Sjp } 508c8e26105Sjp 509c8e26105Sjp free(lifc.lifc_buf); 510c8e26105Sjp close(sock); 511c8e26105Sjp 512c8e26105Sjp return (results); 513c8e26105Sjp } 514c8e26105Sjp 515c8e26105Sjp static int 516c8e26105Sjp cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2) 517c8e26105Sjp { 518c8e26105Sjp int num_subnets1; 519c8e26105Sjp int num_subnets2; 5207a8a68f5SJulian Pullen boolean_t matched; 521c8e26105Sjp int i, j; 522c8e26105Sjp 523c8e26105Sjp for (i = 0; subnets1[i].subnet[0] != '\0'; i++) 5244d61c878SJulian Pullen continue; 525c8e26105Sjp num_subnets1 = i; 526c8e26105Sjp 527c8e26105Sjp for (i = 0; subnets2[i].subnet[0] != '\0'; i++) 5284d61c878SJulian Pullen continue; 529c8e26105Sjp num_subnets2 = i; 530c8e26105Sjp 531c8e26105Sjp if (num_subnets1 != num_subnets2) 532c8e26105Sjp return (1); 533c8e26105Sjp 534c8e26105Sjp for (i = 0; i < num_subnets1; i++) { 5357a8a68f5SJulian Pullen matched = B_FALSE; 536c8e26105Sjp for (j = 0; j < num_subnets2; j++) { 537c8e26105Sjp if (strcmp(subnets1[i].subnet, 538c8e26105Sjp subnets2[j].subnet) == 0) { 5397a8a68f5SJulian Pullen matched = B_TRUE; 540c8e26105Sjp break; 541c8e26105Sjp } 542c8e26105Sjp } 543c8e26105Sjp if (!matched) 544c8e26105Sjp return (1); 545c8e26105Sjp } 546c8e26105Sjp return (0); 547c8e26105Sjp } 548c8e26105Sjp 549c8e26105Sjp 550c8e26105Sjp 551c8e26105Sjp 552c8e26105Sjp /* Convert a DN's DC components into a DNS domainname */ 5537a8a68f5SJulian Pullen char * 554c8e26105Sjp DN_to_DNS(const char *dn_name) 555c8e26105Sjp { 556c8e26105Sjp char dns[DNS_MAX_NAME]; 557c8e26105Sjp char *dns_name; 558c8e26105Sjp int i, j; 559c8e26105Sjp int num = 0; 560c8e26105Sjp 561c8e26105Sjp j = 0; 562c8e26105Sjp i = 0; 563c8e26105Sjp 5644d61c878SJulian Pullen if (dn_name == NULL) 5654d61c878SJulian Pullen return (NULL); 566c8e26105Sjp /* 567c8e26105Sjp * Find all DC=<value> and form DNS name of the 568c8e26105Sjp * form <value1>.<value2>... 569c8e26105Sjp */ 5704d61c878SJulian Pullen while (dn_name[i] != '\0') { 571c8e26105Sjp if (strncasecmp(&dn_name[i], "DC=", 3) == 0) { 572c8e26105Sjp i += 3; 5734d61c878SJulian Pullen if (dn_name[i] != '\0' && num > 0) 574c8e26105Sjp dns[j++] = '.'; 5754d61c878SJulian Pullen while (dn_name[i] != '\0' && 576c8e26105Sjp dn_name[i] != ',' && dn_name[i] != '+') 577c8e26105Sjp dns[j++] = dn_name[i++]; 578c8e26105Sjp num++; 579c8e26105Sjp } else { 580c8e26105Sjp /* Skip attr=value as it is not DC= */ 5814d61c878SJulian Pullen while (dn_name[i] != '\0' && 582c8e26105Sjp dn_name[i] != ',' && dn_name[i] != '+') 583c8e26105Sjp i++; 584c8e26105Sjp } 585c8e26105Sjp /* Skip over separator ',' or '+' */ 5864d61c878SJulian Pullen if (dn_name[i] != '\0') i++; 587c8e26105Sjp } 588c8e26105Sjp dns[j] = '\0'; 589c8e26105Sjp dns_name = malloc(j + 1); 590c8e26105Sjp if (dns_name != NULL) 591c8e26105Sjp (void) strlcpy(dns_name, dns, j + 1); 592c8e26105Sjp return (dns_name); 593c8e26105Sjp } 594c8e26105Sjp 595c8e26105Sjp 596c8e26105Sjp /* Format the DN of an AD LDAP subnet object for some subnet */ 597c8e26105Sjp static char * 598c8e26105Sjp subnet_to_DN(const char *subnet, const char *baseDN) 599c8e26105Sjp { 600c8e26105Sjp char *result; 601c8e26105Sjp int len; 602c8e26105Sjp 603c8e26105Sjp len = snprintf(NULL, 0, 604c8e26105Sjp "CN=%s,CN=Subnets,CN=Sites,%s", 605c8e26105Sjp subnet, baseDN) + 1; 606c8e26105Sjp 607c8e26105Sjp result = malloc(len); 608c8e26105Sjp if (result != NULL) 609c8e26105Sjp (void) snprintf(result, len, 610c8e26105Sjp "CN=%s,CN=Subnets,CN=Sites,%s", 611c8e26105Sjp subnet, baseDN); 612c8e26105Sjp return (result); 613c8e26105Sjp } 614c8e26105Sjp 615c8e26105Sjp 616c8e26105Sjp /* Make a list of subnet object DNs from a list of subnets */ 617c8e26105Sjp static char ** 618c8e26105Sjp subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) 619c8e26105Sjp { 620c8e26105Sjp char **results; 621c8e26105Sjp int i, j; 622c8e26105Sjp 623c8e26105Sjp for (i = 0; subnets[i].subnet[0] != '\0'; i++) 6244d61c878SJulian Pullen continue; 625c8e26105Sjp 626c8e26105Sjp results = calloc(i + 1, sizeof (char *)); 627c8e26105Sjp if (results == NULL) 628c8e26105Sjp return (NULL); 629c8e26105Sjp 630c8e26105Sjp for (i = 0; subnets[i].subnet[0] != '\0'; i++) { 631c8e26105Sjp if ((results[i] = subnet_to_DN(subnets[i].subnet, base_dn)) 632c8e26105Sjp == NULL) { 633c8e26105Sjp for (j = 0; j < i; j++) 634c8e26105Sjp free(results[j]); 635c8e26105Sjp free(results); 636c8e26105Sjp return (NULL); 637c8e26105Sjp } 638c8e26105Sjp } 639c8e26105Sjp 640c8e26105Sjp return (results); 641c8e26105Sjp } 642c8e26105Sjp 643c8e26105Sjp 644c8e26105Sjp /* Compare SRC RRs; used with qsort() */ 645c8e26105Sjp static int 646479ac375Sdm srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2) 647c8e26105Sjp { 648c8e26105Sjp if (s1->priority < s2->priority) 649c8e26105Sjp return (1); 650c8e26105Sjp else if (s1->priority > s2->priority) 651c8e26105Sjp return (-1); 652c8e26105Sjp 653c8e26105Sjp if (s1->weight < s2->weight) 654c8e26105Sjp return (1); 655c8e26105Sjp else if (s1->weight > s2->weight) 656c8e26105Sjp return (-1); 657c8e26105Sjp 658c8e26105Sjp return (0); 659c8e26105Sjp } 660c8e26105Sjp 661c8e26105Sjp 662c8e26105Sjp /* 663c8e26105Sjp * Query or search the SRV RRs for a given name. 664c8e26105Sjp * 665c8e26105Sjp * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any 666c8e26105Sjp * search list/option), else query (as in res_nquery(3RESOLV)). 667c8e26105Sjp * 668c8e26105Sjp * The output TTL will be the one of the SRV RR with the lowest TTL. 669c8e26105Sjp */ 670479ac375Sdm idmap_ad_disc_ds_t * 671c8e26105Sjp srv_query(res_state state, const char *svc_name, const char *dname, 672c8e26105Sjp char **rrname, uint32_t *ttl) 673c8e26105Sjp { 674479ac375Sdm idmap_ad_disc_ds_t *srv; 675479ac375Sdm idmap_ad_disc_ds_t *srv_res; 676c8e26105Sjp union { 677c8e26105Sjp HEADER hdr; 678c8e26105Sjp uchar_t buf[NS_MAXMSG]; 679c8e26105Sjp } msg; 680c8e26105Sjp int len, cnt, qdcount, ancount; 681c8e26105Sjp uchar_t *ptr, *eom; 682c8e26105Sjp uchar_t *end; 683c8e26105Sjp uint16_t type; 684c8e26105Sjp /* LINTED E_FUNC_SET_NOT_USED */ 685c8e26105Sjp uint16_t class; 686c8e26105Sjp uint32_t rttl; 687c8e26105Sjp uint16_t size; 688c8e26105Sjp char namebuf[NS_MAXDNAME]; 689c8e26105Sjp 6900dcc7149Snw if (state == NULL) 6910dcc7149Snw return (NULL); 6920dcc7149Snw 693c8e26105Sjp /* Set negative result TTL */ 694c8e26105Sjp *ttl = 5 * 60; 695c8e26105Sjp 696c8e26105Sjp /* 1. query necessary resource records */ 697c8e26105Sjp 698c8e26105Sjp /* Search, querydomain or query */ 699c8e26105Sjp if (rrname != NULL) { 700c8e26105Sjp *rrname = NULL; 701c8e26105Sjp len = res_nsearch(state, svc_name, C_IN, T_SRV, 702c8e26105Sjp msg.buf, sizeof (msg.buf)); 7037a8a68f5SJulian Pullen logger(LOG_DEBUG, "Searching DNS for SRV RRs named '%s'", 7047a8a68f5SJulian Pullen svc_name); 7057a8a68f5SJulian Pullen if (len < 0) { 7067a8a68f5SJulian Pullen logger(LOG_DEBUG, "DNS search for '%s' failed (%s)", 7077a8a68f5SJulian Pullen svc_name, hstrerror(state->res_h_errno)); 7087a8a68f5SJulian Pullen return (NULL); 7097a8a68f5SJulian Pullen } 710c8e26105Sjp } else if (dname != NULL) { 7117a8a68f5SJulian Pullen logger(LOG_DEBUG, 7127a8a68f5SJulian Pullen "Querying DNS for SRV RRs named '%s' for '%s' ", 7137a8a68f5SJulian Pullen svc_name, dname); 714c8e26105Sjp 715e3f2c991SKeyur Desai len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 716e3f2c991SKeyur Desai msg.buf, sizeof (msg.buf)); 717e3f2c991SKeyur Desai 7187a8a68f5SJulian Pullen if (len < 0) { 7197a8a68f5SJulian Pullen logger(LOG_DEBUG, 7207a8a68f5SJulian Pullen "DNS query for '%s' for '%s' failed (%s)", 7217a8a68f5SJulian Pullen svc_name, dname, hstrerror(state->res_h_errno)); 7227a8a68f5SJulian Pullen return (NULL); 7237a8a68f5SJulian Pullen } 724c8e26105Sjp } 7257a8a68f5SJulian Pullen 726c8e26105Sjp if (len > sizeof (msg.buf)) { 7277a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query %ib message doesn't fit" 728c8e26105Sjp " into %ib buffer", 72971590c90Snw len, sizeof (msg.buf)); 730c8e26105Sjp return (NULL); 731c8e26105Sjp } 732c8e26105Sjp 733c8e26105Sjp /* 2. parse the reply, skip header and question sections */ 734c8e26105Sjp 735c8e26105Sjp ptr = msg.buf + sizeof (msg.hdr); 736c8e26105Sjp eom = msg.buf + len; 737c8e26105Sjp qdcount = ntohs(msg.hdr.qdcount); 738c8e26105Sjp ancount = ntohs(msg.hdr.ancount); 739c8e26105Sjp 740c8e26105Sjp for (cnt = qdcount; cnt > 0; --cnt) { 741c8e26105Sjp if ((len = dn_skipname(ptr, eom)) < 0) { 7427a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid message format"); 743c8e26105Sjp return (NULL); 744c8e26105Sjp } 745c8e26105Sjp ptr += len + QFIXEDSZ; 746c8e26105Sjp } 747c8e26105Sjp 748c8e26105Sjp /* 3. walk through the answer section */ 749c8e26105Sjp 750479ac375Sdm srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); 751c8e26105Sjp *ttl = (uint32_t)-1; 752c8e26105Sjp 753c8e26105Sjp for (srv = srv_res, cnt = ancount; 754c8e26105Sjp cnt > 0; --cnt, srv++) { 755c8e26105Sjp 756c8e26105Sjp len = dn_expand(msg.buf, eom, ptr, namebuf, 757c8e26105Sjp sizeof (namebuf)); 758c8e26105Sjp if (len < 0) { 7597a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid message format"); 760c8e26105Sjp return (NULL); 761c8e26105Sjp } 762c8e26105Sjp if (rrname != NULL && *rrname == NULL) 763c8e26105Sjp *rrname = strdup(namebuf); 764c8e26105Sjp ptr += len; 765c8e26105Sjp NS_GET16(type, ptr); 766c8e26105Sjp NS_GET16(class, ptr); 767c8e26105Sjp NS_GET32(rttl, ptr); 768c8e26105Sjp NS_GET16(size, ptr); 769c8e26105Sjp if ((end = ptr + size) > eom) { 7707a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid message format"); 771c8e26105Sjp return (NULL); 772c8e26105Sjp } 773c8e26105Sjp 774c8e26105Sjp if (type != T_SRV) { 775c8e26105Sjp ptr = end; 776c8e26105Sjp continue; 777c8e26105Sjp } 778c8e26105Sjp 779c8e26105Sjp NS_GET16(srv->priority, ptr); 780c8e26105Sjp NS_GET16(srv->weight, ptr); 781c8e26105Sjp NS_GET16(srv->port, ptr); 782c8e26105Sjp len = dn_expand(msg.buf, eom, ptr, srv->host, 783c8e26105Sjp sizeof (srv->host)); 784c8e26105Sjp if (len < 0) { 7857a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid SRV record"); 786c8e26105Sjp return (NULL); 787c8e26105Sjp } 788c8e26105Sjp 789c8e26105Sjp if (rttl < *ttl) 790c8e26105Sjp *ttl = rttl; 791c8e26105Sjp 7927a8a68f5SJulian Pullen logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d", 79371590c90Snw namebuf, rttl, srv->priority, srv->weight, srv->host, 79471590c90Snw srv->port); 795c8e26105Sjp 796c8e26105Sjp /* 3. move ptr to the end of current record */ 797c8e26105Sjp 798c8e26105Sjp ptr = end; 799c8e26105Sjp } 800c8e26105Sjp 801c8e26105Sjp if (ancount > 1) 802c8e26105Sjp qsort(srv_res, ancount, sizeof (*srv_res), 803c8e26105Sjp (int (*)(const void *, const void *))srvcmp); 804c8e26105Sjp 805c8e26105Sjp return (srv_res); 806c8e26105Sjp } 807c8e26105Sjp 808c8e26105Sjp 8094d61c878SJulian Pullen /* 8104d61c878SJulian Pullen * A utility function to bind to a Directory server 8114d61c878SJulian Pullen */ 8124d61c878SJulian Pullen 8134d61c878SJulian Pullen static LDAP* 8144d61c878SJulian Pullen ldap_lookup_init(idmap_ad_disc_ds_t *ds) 8154d61c878SJulian Pullen { 8164d61c878SJulian Pullen int i; 8174d61c878SJulian Pullen int rc, ldversion; 8184d61c878SJulian Pullen int zero = 0; 8194d61c878SJulian Pullen int timeoutms = 5 * 1000; 8204d61c878SJulian Pullen char *saslmech = "GSSAPI"; 8214d61c878SJulian Pullen uint32_t saslflags = LDAP_SASL_INTERACTIVE; 8224d61c878SJulian Pullen LDAP *ld = NULL; 8234d61c878SJulian Pullen 8244d61c878SJulian Pullen for (i = 0; ds[i].host[0] != '\0'; i++) { 8254d61c878SJulian Pullen ld = ldap_init(ds[i].host, ds[i].port); 8264d61c878SJulian Pullen if (ld == NULL) { 8277a8a68f5SJulian Pullen logger(LOG_DEBUG, "Couldn't connect to " 8284d61c878SJulian Pullen "AD DC %s:%d (%s)", 8294d61c878SJulian Pullen ds[i].host, ds[i].port, 8304d61c878SJulian Pullen strerror(errno)); 8314d61c878SJulian Pullen continue; 8324d61c878SJulian Pullen } 8334d61c878SJulian Pullen 8344d61c878SJulian Pullen ldversion = LDAP_VERSION3; 8354d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 8364d61c878SJulian Pullen &ldversion); 8374d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, 8384d61c878SJulian Pullen LDAP_OPT_OFF); 8394d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 8404d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 8414d61c878SJulian Pullen /* setup TCP/IP connect timeout */ 8424d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 8434d61c878SJulian Pullen &timeoutms); 8444d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_OPT_RESTART, 8454d61c878SJulian Pullen LDAP_OPT_ON); 8464d61c878SJulian Pullen 847*bd428526SJulian Pullen rc = adutils_set_thread_functions(ld); 848*bd428526SJulian Pullen if (rc != LDAP_SUCCESS) { 849*bd428526SJulian Pullen /* Error has already been logged */ 850*bd428526SJulian Pullen (void) ldap_unbind(ld); 851*bd428526SJulian Pullen ld = NULL; 852*bd428526SJulian Pullen continue; 853*bd428526SJulian Pullen } 854*bd428526SJulian Pullen 8554d61c878SJulian Pullen rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, 8564d61c878SJulian Pullen saslmech, NULL, NULL, saslflags, &saslcallback, 8574d61c878SJulian Pullen NULL /* defaults */); 8584d61c878SJulian Pullen if (rc == LDAP_SUCCESS) 8594d61c878SJulian Pullen break; 8604d61c878SJulian Pullen 8617a8a68f5SJulian Pullen logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", 8624d61c878SJulian Pullen ds[i].host, ds[i].port, ldap_err2string(rc)); 8634d61c878SJulian Pullen (void) ldap_unbind(ld); 8644d61c878SJulian Pullen ld = NULL; 8654d61c878SJulian Pullen } 8664d61c878SJulian Pullen return (ld); 8674d61c878SJulian Pullen } 8684d61c878SJulian Pullen 869c8e26105Sjp 8707a8a68f5SJulian Pullen 871c8e26105Sjp /* 872c8e26105Sjp * A utility function to get the value of some attribute of one of one 873c8e26105Sjp * or more AD LDAP objects named by the dn_list; first found one wins. 874c8e26105Sjp */ 875c8e26105Sjp static char * 876479ac375Sdm ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, 877c8e26105Sjp char **dn_list, char *attr) 878c8e26105Sjp { 879c8e26105Sjp int i; 8804d61c878SJulian Pullen int rc; 881c8e26105Sjp int scope = LDAP_SCOPE_BASE; 882c8e26105Sjp char *attrs[2]; 883c8e26105Sjp LDAPMessage *results = NULL; 884c8e26105Sjp LDAPMessage *entry; 885c8e26105Sjp char **values = NULL; 886c8e26105Sjp char *val = NULL; 887c8e26105Sjp 888c8e26105Sjp attrs[0] = attr; 889c8e26105Sjp attrs[1] = NULL; 890c8e26105Sjp 8914d61c878SJulian Pullen if (*ld == NULL) 8924d61c878SJulian Pullen *ld = ldap_lookup_init(domainControllers); 893c8e26105Sjp 8944d61c878SJulian Pullen if (*ld == NULL) 895c8e26105Sjp return (NULL); 896c8e26105Sjp 897c8e26105Sjp for (i = 0; dn_list[i] != NULL; i++) { 898c8e26105Sjp rc = ldap_search_s(*ld, dn_list[i], scope, 899c8e26105Sjp "(objectclass=*)", attrs, 0, &results); 900c8e26105Sjp if (rc == LDAP_SUCCESS) { 901c8e26105Sjp for (entry = ldap_first_entry(*ld, results); 902c8e26105Sjp entry != NULL && values == NULL; 903c8e26105Sjp entry = ldap_next_entry(*ld, entry)) { 904c8e26105Sjp values = ldap_get_values( 905c8e26105Sjp *ld, entry, attr); 906c8e26105Sjp } 907c8e26105Sjp 908c8e26105Sjp if (values != NULL) { 909c8e26105Sjp (void) ldap_msgfree(results); 910c8e26105Sjp val = strdup(values[0]); 911c8e26105Sjp ldap_value_free(values); 912c8e26105Sjp return (val); 913c8e26105Sjp } 914c8e26105Sjp } 915c8e26105Sjp if (results != NULL) { 916c8e26105Sjp (void) ldap_msgfree(results); 917c8e26105Sjp results = NULL; 918c8e26105Sjp } 919c8e26105Sjp } 920c8e26105Sjp 921c8e26105Sjp return (NULL); 922c8e26105Sjp } 923c8e26105Sjp 924c8e26105Sjp 9254d61c878SJulian Pullen /* 9264d61c878SJulian Pullen * Lookup the trusted domains in the global catalog. 9274d61c878SJulian Pullen * 9284d61c878SJulian Pullen * Returns: 9294d61c878SJulian Pullen * array of trusted domains which is terminated by 9304d61c878SJulian Pullen * an empty trusted domain. 9314d61c878SJulian Pullen * NULL an error occured 9324d61c878SJulian Pullen */ 9334d61c878SJulian Pullen ad_disc_trusteddomains_t * 9344d61c878SJulian Pullen ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, 9354d61c878SJulian Pullen char *base_dn) 9364d61c878SJulian Pullen { 9374d61c878SJulian Pullen int scope = LDAP_SCOPE_SUBTREE; 9384d61c878SJulian Pullen char *attrs[3]; 9394d61c878SJulian Pullen int rc; 9404d61c878SJulian Pullen LDAPMessage *results = NULL; 9414d61c878SJulian Pullen LDAPMessage *entry; 9424d61c878SJulian Pullen char *filter; 9434d61c878SJulian Pullen char **partner = NULL; 9444d61c878SJulian Pullen char **direction = NULL; 9454d61c878SJulian Pullen int num = 0; 9464d61c878SJulian Pullen ad_disc_trusteddomains_t *trusted_domains = NULL; 9474d61c878SJulian Pullen 9484d61c878SJulian Pullen 9494d61c878SJulian Pullen if (*ld == NULL) 9504d61c878SJulian Pullen *ld = ldap_lookup_init(globalCatalog); 9514d61c878SJulian Pullen 9524d61c878SJulian Pullen if (*ld == NULL) 9534d61c878SJulian Pullen return (NULL); 9544d61c878SJulian Pullen 9554d61c878SJulian Pullen attrs[0] = "trustPartner"; 9564d61c878SJulian Pullen attrs[1] = "trustDirection"; 9574d61c878SJulian Pullen attrs[2] = NULL; 9584d61c878SJulian Pullen 9594d61c878SJulian Pullen /* trustDirection values - inbound = 1 and bidirectional = 3 */ 9604d61c878SJulian Pullen filter = "(&(objectclass=trustedDomain)" 9614d61c878SJulian Pullen "(|(trustDirection=3)(trustDirection=1)))"; 9624d61c878SJulian Pullen 9634d61c878SJulian Pullen rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); 9644d61c878SJulian Pullen if (rc == LDAP_SUCCESS) { 9654d61c878SJulian Pullen for (entry = ldap_first_entry(*ld, results); 9664d61c878SJulian Pullen entry != NULL; entry = ldap_next_entry(*ld, entry)) { 9674d61c878SJulian Pullen partner = ldap_get_values(*ld, entry, "trustPartner"); 9684d61c878SJulian Pullen direction = ldap_get_values( 9694d61c878SJulian Pullen *ld, entry, "trustDirection"); 9704d61c878SJulian Pullen 9714d61c878SJulian Pullen if (partner != NULL && direction != NULL) { 9724d61c878SJulian Pullen num++; 9734d61c878SJulian Pullen trusted_domains = realloc(trusted_domains, 9744d61c878SJulian Pullen (num + 1) * 9754d61c878SJulian Pullen sizeof (ad_disc_trusteddomains_t)); 9764d61c878SJulian Pullen if (trusted_domains == NULL) { 9774d61c878SJulian Pullen ldap_value_free(partner); 9784d61c878SJulian Pullen ldap_value_free(direction); 9794d61c878SJulian Pullen ldap_msgfree(results); 9804d61c878SJulian Pullen return (NULL); 9814d61c878SJulian Pullen } 9824d61c878SJulian Pullen /* Last element should be zero */ 9834d61c878SJulian Pullen memset(&trusted_domains[num], 0, 9844d61c878SJulian Pullen sizeof (ad_disc_trusteddomains_t)); 9854d61c878SJulian Pullen strcpy(trusted_domains[num - 1].domain, 9864d61c878SJulian Pullen partner[0]); 9874d61c878SJulian Pullen trusted_domains[num - 1].direction = 9884d61c878SJulian Pullen atoi(direction[0]); 9894d61c878SJulian Pullen } 9904d61c878SJulian Pullen if (partner != NULL) 9914d61c878SJulian Pullen ldap_value_free(partner); 9924d61c878SJulian Pullen if (direction != NULL) 9934d61c878SJulian Pullen ldap_value_free(direction); 9944d61c878SJulian Pullen } 9954d61c878SJulian Pullen } else if (rc == LDAP_NO_RESULTS_RETURNED) { 9964d61c878SJulian Pullen /* This is not an error - return empty trusted domain */ 9974d61c878SJulian Pullen trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); 9984d61c878SJulian Pullen } 9994d61c878SJulian Pullen if (results != NULL) 10004d61c878SJulian Pullen ldap_msgfree(results); 10014d61c878SJulian Pullen 10024d61c878SJulian Pullen return (trusted_domains); 10034d61c878SJulian Pullen } 10044d61c878SJulian Pullen 10054d61c878SJulian Pullen 10064d61c878SJulian Pullen /* 10074d61c878SJulian Pullen * This functions finds all the domains in a forest. 10084d61c878SJulian Pullen */ 10094d61c878SJulian Pullen ad_disc_domainsinforest_t * 10104d61c878SJulian Pullen ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) 10114d61c878SJulian Pullen { 1012928e1f97SJordan Brown static char *attrs[] = { 1013928e1f97SJordan Brown "objectSid", 1014928e1f97SJordan Brown NULL, 1015928e1f97SJordan Brown }; 10164d61c878SJulian Pullen int rc; 10174d61c878SJulian Pullen LDAPMessage *result = NULL; 10184d61c878SJulian Pullen LDAPMessage *entry; 1019928e1f97SJordan Brown int ndomains = 0; 1020928e1f97SJordan Brown int nresults; 10214d61c878SJulian Pullen ad_disc_domainsinforest_t *domains = NULL; 10224d61c878SJulian Pullen 10234d61c878SJulian Pullen if (*ld == NULL) 10244d61c878SJulian Pullen *ld = ldap_lookup_init(globalCatalogs); 10254d61c878SJulian Pullen 10267a8a68f5SJulian Pullen if (*ld == NULL) 10274d61c878SJulian Pullen return (NULL); 10284d61c878SJulian Pullen 1029928e1f97SJordan Brown logger(LOG_DEBUG, "Looking for domains in forest..."); 1030928e1f97SJordan Brown /* Find domains */ 1031928e1f97SJordan Brown rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, 1032928e1f97SJordan Brown "(objectClass=Domain)", attrs, 0, &result); 1033928e1f97SJordan Brown if (rc != LDAP_SUCCESS) 1034928e1f97SJordan Brown goto err; 1035928e1f97SJordan Brown 1036928e1f97SJordan Brown nresults = ldap_count_entries(*ld, result); 1037928e1f97SJordan Brown domains = calloc(nresults + 1, sizeof (*domains)); 1038928e1f97SJordan Brown if (domains == NULL) 1039928e1f97SJordan Brown goto err; 1040928e1f97SJordan Brown 1041928e1f97SJordan Brown for (entry = ldap_first_entry(*ld, result); 1042928e1f97SJordan Brown entry != NULL; 1043928e1f97SJordan Brown entry = ldap_next_entry(*ld, entry)) { 1044928e1f97SJordan Brown struct berval **sid_ber; 1045928e1f97SJordan Brown adutils_sid_t sid; 1046928e1f97SJordan Brown char *sid_str; 1047928e1f97SJordan Brown char *name; 1048928e1f97SJordan Brown 1049928e1f97SJordan Brown sid_ber = ldap_get_values_len(*ld, entry, 1050928e1f97SJordan Brown "objectSid"); 1051928e1f97SJordan Brown if (sid_ber == NULL) 1052928e1f97SJordan Brown continue; 10534d61c878SJulian Pullen 1054928e1f97SJordan Brown rc = adutils_getsid(sid_ber[0], &sid); 1055928e1f97SJordan Brown ldap_value_free_len(sid_ber); 1056928e1f97SJordan Brown if (rc < 0) 1057928e1f97SJordan Brown goto err; 10584d61c878SJulian Pullen 1059928e1f97SJordan Brown if ((sid_str = adutils_sid2txt(&sid)) == NULL) 1060928e1f97SJordan Brown goto err; 10614d61c878SJulian Pullen 1062928e1f97SJordan Brown strcpy(domains[ndomains].sid, sid_str); 1063928e1f97SJordan Brown free(sid_str); 1064928e1f97SJordan Brown 1065928e1f97SJordan Brown name = DN_to_DNS(ldap_get_dn(*ld, entry)); 1066928e1f97SJordan Brown if (name == NULL) 1067928e1f97SJordan Brown goto err; 1068928e1f97SJordan Brown 1069928e1f97SJordan Brown strcpy(domains[ndomains].domain, name); 1070928e1f97SJordan Brown free(name); 1071928e1f97SJordan Brown 1072928e1f97SJordan Brown logger(LOG_DEBUG, " found %s", domains[ndomains].domain); 1073928e1f97SJordan Brown 1074928e1f97SJordan Brown ndomains++; 10754d61c878SJulian Pullen } 10764d61c878SJulian Pullen 1077928e1f97SJordan Brown if (ndomains == 0) 1078928e1f97SJordan Brown goto err; 1079928e1f97SJordan Brown 1080928e1f97SJordan Brown if (ndomains < nresults) { 1081928e1f97SJordan Brown ad_disc_domainsinforest_t *tmp; 10821fcced4cSJordan Brown tmp = realloc(domains, (ndomains + 1) * sizeof (*domains)); 1083928e1f97SJordan Brown if (tmp == NULL) 1084928e1f97SJordan Brown goto err; 1085928e1f97SJordan Brown domains = tmp; 10864d61c878SJulian Pullen } 1087928e1f97SJordan Brown 1088928e1f97SJordan Brown if (result != NULL) 1089928e1f97SJordan Brown ldap_msgfree(result); 10904d61c878SJulian Pullen 10914d61c878SJulian Pullen return (domains); 1092928e1f97SJordan Brown 1093928e1f97SJordan Brown err: 1094928e1f97SJordan Brown free(domains); 1095928e1f97SJordan Brown if (result != NULL) 1096928e1f97SJordan Brown ldap_msgfree(result); 1097928e1f97SJordan Brown return (NULL); 10984d61c878SJulian Pullen } 10994d61c878SJulian Pullen 1100c8e26105Sjp 1101c8e26105Sjp ad_disc_t 1102c8e26105Sjp ad_disc_init(void) 1103c8e26105Sjp { 1104c8e26105Sjp struct ad_disc *ctx; 1105c8e26105Sjp ctx = calloc(1, sizeof (struct ad_disc)); 11060dcc7149Snw if (ctx != NULL) 11070dcc7149Snw DO_RES_NINIT(ctx); 11084d61c878SJulian Pullen 11094d61c878SJulian Pullen ctx->domain_name.type = AD_STRING; 11104d61c878SJulian Pullen ctx->domain_controller.type = AD_DIRECTORY; 11114d61c878SJulian Pullen ctx->site_name.type = AD_STRING; 11124d61c878SJulian Pullen ctx->forest_name.type = AD_STRING; 11134d61c878SJulian Pullen ctx->global_catalog.type = AD_DIRECTORY; 11144d61c878SJulian Pullen ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; 11154d61c878SJulian Pullen ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; 11164d61c878SJulian Pullen /* Site specific versions */ 11174d61c878SJulian Pullen ctx->site_domain_controller.type = AD_DIRECTORY; 11184d61c878SJulian Pullen ctx->site_global_catalog.type = AD_DIRECTORY; 1119c8e26105Sjp return (ctx); 1120c8e26105Sjp } 1121c8e26105Sjp 1122c8e26105Sjp 1123c8e26105Sjp void 1124c8e26105Sjp ad_disc_fini(ad_disc_t ctx) 1125c8e26105Sjp { 1126cd37da74Snw if (ctx == NULL) 1127cd37da74Snw return; 1128cd37da74Snw 11290dcc7149Snw if (ctx->res_ninitted) 11304d61c878SJulian Pullen res_ndestroy(&ctx->res_state); 1131c8e26105Sjp 1132c8e26105Sjp if (ctx->subnets != NULL) 1133c8e26105Sjp free(ctx->subnets); 1134c8e26105Sjp 11354d61c878SJulian Pullen if (ctx->domain_name.value != NULL) 11364d61c878SJulian Pullen free(ctx->domain_name.value); 11374d61c878SJulian Pullen 11384d61c878SJulian Pullen if (ctx->domain_controller.value != NULL) 11394d61c878SJulian Pullen free(ctx->domain_controller.value); 11404d61c878SJulian Pullen 11414d61c878SJulian Pullen if (ctx->site_name.value != NULL) 11424d61c878SJulian Pullen free(ctx->site_name.value); 1143c8e26105Sjp 11444d61c878SJulian Pullen if (ctx->forest_name.value != NULL) 11454d61c878SJulian Pullen free(ctx->forest_name.value); 1146c8e26105Sjp 11474d61c878SJulian Pullen if (ctx->global_catalog.value != NULL) 11484d61c878SJulian Pullen free(ctx->global_catalog.value); 1149c8e26105Sjp 11504d61c878SJulian Pullen if (ctx->domains_in_forest.value != NULL) 11514d61c878SJulian Pullen free(ctx->domains_in_forest.value); 1152c8e26105Sjp 11534d61c878SJulian Pullen if (ctx->trusted_domains.value != NULL) 11544d61c878SJulian Pullen free(ctx->trusted_domains.value); 1155c8e26105Sjp 11564d61c878SJulian Pullen /* Site specific versions */ 11574d61c878SJulian Pullen if (ctx->site_domain_controller.value != NULL) 11584d61c878SJulian Pullen free(ctx->site_domain_controller.value); 1159c8e26105Sjp 11604d61c878SJulian Pullen if (ctx->site_global_catalog.value != NULL) 11614d61c878SJulian Pullen free(ctx->site_global_catalog.value); 1162c8e26105Sjp 1163c8e26105Sjp free(ctx); 1164c8e26105Sjp } 1165c8e26105Sjp 1166c8e26105Sjp void 1167c8e26105Sjp ad_disc_refresh(ad_disc_t ctx) 1168c8e26105Sjp { 11690dcc7149Snw if (ctx->res_ninitted) 11704d61c878SJulian Pullen res_ndestroy(&ctx->res_state); 11714d61c878SJulian Pullen (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); 11724d61c878SJulian Pullen ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; 1173c8e26105Sjp 11744d61c878SJulian Pullen if (ctx->domain_name.state == AD_STATE_AUTO) 11754d61c878SJulian Pullen ctx->domain_name.state = AD_STATE_INVALID; 1176c8e26105Sjp 11774d61c878SJulian Pullen if (ctx->domain_controller.state == AD_STATE_AUTO) 11784d61c878SJulian Pullen ctx->domain_controller.state = AD_STATE_INVALID; 1179c8e26105Sjp 11804d61c878SJulian Pullen if (ctx->site_name.state == AD_STATE_AUTO) 11814d61c878SJulian Pullen ctx->site_name.state = AD_STATE_INVALID; 1182c8e26105Sjp 11834d61c878SJulian Pullen if (ctx->forest_name.state == AD_STATE_AUTO) 11844d61c878SJulian Pullen ctx->forest_name.state = AD_STATE_INVALID; 1185c8e26105Sjp 11864d61c878SJulian Pullen if (ctx->global_catalog.state == AD_STATE_AUTO) 11874d61c878SJulian Pullen ctx->global_catalog.state = AD_STATE_INVALID; 1188c8e26105Sjp 11894d61c878SJulian Pullen if (ctx->domains_in_forest.state == AD_STATE_AUTO) 11904d61c878SJulian Pullen ctx->domains_in_forest.state = AD_STATE_INVALID; 1191c8e26105Sjp 11924d61c878SJulian Pullen if (ctx->trusted_domains.state == AD_STATE_AUTO) 11934d61c878SJulian Pullen ctx->trusted_domains.state = AD_STATE_INVALID; 11944d61c878SJulian Pullen 11954d61c878SJulian Pullen if (ctx->site_domain_controller.state == AD_STATE_AUTO) 11964d61c878SJulian Pullen ctx->site_domain_controller.state = AD_STATE_INVALID; 11974d61c878SJulian Pullen 11984d61c878SJulian Pullen if (ctx->site_global_catalog.state == AD_STATE_AUTO) 11994d61c878SJulian Pullen ctx->site_global_catalog.state = AD_STATE_INVALID; 1200c8e26105Sjp } 1201c8e26105Sjp 1202c8e26105Sjp 1203c8e26105Sjp 12040dcc7149Snw /* Discover joined Active Directory domainName */ 12054d61c878SJulian Pullen static ad_item_t * 1206c8e26105Sjp validate_DomainName(ad_disc_t ctx) 1207c8e26105Sjp { 1208479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 1209c8e26105Sjp char *dname, *srvname; 1210c8e26105Sjp uint32_t ttl = 0; 1211928e1f97SJordan Brown int len; 1212c8e26105Sjp 12134d61c878SJulian Pullen if (is_valid(&ctx->domain_name)) 12144d61c878SJulian Pullen return (&ctx->domain_name); 1215c8e26105Sjp 1216c8e26105Sjp 1217c8e26105Sjp /* Try to find our domain by searching for DCs for it */ 12180dcc7149Snw DO_RES_NINIT(ctx); 12194d61c878SJulian Pullen domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD 12204d61c878SJulian Pullen DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); 1221c8e26105Sjp 1222c8e26105Sjp /* 1223c8e26105Sjp * If we can't find DCs by via res_nsearch() then there's no 1224c8e26105Sjp * point in trying anything else to discover the AD domain name. 1225c8e26105Sjp */ 1226c8e26105Sjp if (domain_controller == NULL) 12274d61c878SJulian Pullen return (NULL); 1228c8e26105Sjp 1229c8e26105Sjp free(domain_controller); 1230c8e26105Sjp /* 1231c8e26105Sjp * We have the FQDN of the SRV RR name, so now we extract the 1232c8e26105Sjp * domainname suffix from it. 1233c8e26105Sjp */ 1234c8e26105Sjp dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) + 1235c8e26105Sjp 1 /* for the dot between RR name and domainname */); 1236c8e26105Sjp 1237c8e26105Sjp free(srvname); 1238c8e26105Sjp 1239c8e26105Sjp if (dname == NULL) { 12407a8a68f5SJulian Pullen logger(LOG_ERR, "Out of memory"); 12414d61c878SJulian Pullen return (NULL); 1242c8e26105Sjp } 1243c8e26105Sjp 1244c8e26105Sjp /* Eat any trailing dot */ 1245928e1f97SJordan Brown len = strlen(dname); 12461fcced4cSJordan Brown if (len > 0 && dname[len - 1] == '.') 12471fcced4cSJordan Brown dname[len - 1] = '\0'; 1248c8e26105Sjp 12494d61c878SJulian Pullen update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); 12504d61c878SJulian Pullen 12514d61c878SJulian Pullen return (&ctx->domain_name); 1252c8e26105Sjp } 1253c8e26105Sjp 1254c8e26105Sjp 1255c8e26105Sjp char * 12567a8a68f5SJulian Pullen ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) 1257c8e26105Sjp { 1258c8e26105Sjp char *domain_name = NULL; 12594d61c878SJulian Pullen ad_item_t *domain_name_item; 1260c8e26105Sjp 12614d61c878SJulian Pullen domain_name_item = validate_DomainName(ctx); 12624d61c878SJulian Pullen 12634d61c878SJulian Pullen if (domain_name_item) { 12644d61c878SJulian Pullen domain_name = strdup(domain_name_item->value); 12654d61c878SJulian Pullen if (auto_discovered != NULL) 12664d61c878SJulian Pullen *auto_discovered = 12674d61c878SJulian Pullen (domain_name_item->state == AD_STATE_AUTO); 12684d61c878SJulian Pullen } else if (auto_discovered != NULL) 12697a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 1270c8e26105Sjp 1271c8e26105Sjp return (domain_name); 1272c8e26105Sjp } 1273c8e26105Sjp 1274c8e26105Sjp 12750dcc7149Snw /* Discover domain controllers */ 12764d61c878SJulian Pullen static ad_item_t * 1277c8e26105Sjp validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) 1278c8e26105Sjp { 1279c8e26105Sjp uint32_t ttl = 0; 1280479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 12817a8a68f5SJulian Pullen boolean_t validate_global = B_FALSE; 12827a8a68f5SJulian Pullen boolean_t validate_site = B_FALSE; 12834d61c878SJulian Pullen ad_item_t *domain_name_item; 12844d61c878SJulian Pullen ad_item_t *site_name_item = NULL; 1285c8e26105Sjp 12864d61c878SJulian Pullen /* If the values is fixed there will not be a site specific version */ 1287c8e26105Sjp if (is_fixed(&ctx->domain_controller)) 12884d61c878SJulian Pullen return (&ctx->domain_controller); 12894d61c878SJulian Pullen 12904d61c878SJulian Pullen domain_name_item = validate_DomainName(ctx); 12914d61c878SJulian Pullen if (domain_name_item == NULL) 12924d61c878SJulian Pullen return (NULL); 1293c8e26105Sjp 1294c8e26105Sjp if (req == AD_DISC_GLOBAL) 12957a8a68f5SJulian Pullen validate_global = B_TRUE; 1296c8e26105Sjp else { 12974d61c878SJulian Pullen site_name_item = validate_SiteName(ctx); 12984d61c878SJulian Pullen if (site_name_item != NULL) 12997a8a68f5SJulian Pullen validate_site = B_TRUE; 13004d61c878SJulian Pullen else if (req == AD_DISC_PREFER_SITE) 13017a8a68f5SJulian Pullen validate_global = B_TRUE; 1302c8e26105Sjp } 1303c8e26105Sjp 13044d61c878SJulian Pullen if (validate_global) { 13054d61c878SJulian Pullen if (!is_valid(&ctx->domain_controller) || 13064d61c878SJulian Pullen is_changed(&ctx->domain_controller, PARAM1, 13074d61c878SJulian Pullen domain_name_item)) { 1308c8e26105Sjp /* 13090dcc7149Snw * Lookup DNS SRV RR named 13100dcc7149Snw * _ldap._tcp.dc._msdcs.<DomainName> 1311c8e26105Sjp */ 13120dcc7149Snw DO_RES_NINIT(ctx); 13134d61c878SJulian Pullen domain_controller = srv_query(&ctx->res_state, 1314c8e26105Sjp LDAP_SRV_HEAD DC_SRV_TAIL, 13154d61c878SJulian Pullen domain_name_item->value, NULL, &ttl); 1316c8e26105Sjp 13174d61c878SJulian Pullen if (domain_controller == NULL) 13184d61c878SJulian Pullen return (NULL); 1319c8e26105Sjp 13204d61c878SJulian Pullen update_item(&ctx->domain_controller, domain_controller, 13214d61c878SJulian Pullen AD_STATE_AUTO, ttl); 13224d61c878SJulian Pullen update_version(&ctx->domain_controller, PARAM1, 13234d61c878SJulian Pullen domain_name_item); 13244d61c878SJulian Pullen } 13254d61c878SJulian Pullen return (&ctx->domain_controller); 13264d61c878SJulian Pullen } 1327c8e26105Sjp 13284d61c878SJulian Pullen if (validate_site) { 13294d61c878SJulian Pullen if (!is_valid(&ctx->site_domain_controller) || 13304d61c878SJulian Pullen is_changed(&ctx->site_domain_controller, PARAM1, 13314d61c878SJulian Pullen domain_name_item) || 13324d61c878SJulian Pullen is_changed(&ctx->site_domain_controller, PARAM2, 13334d61c878SJulian Pullen site_name_item)) { 1334c8e26105Sjp char rr_name[DNS_MAX_NAME]; 1335c8e26105Sjp /* 13360dcc7149Snw * Lookup DNS SRV RR named 13370dcc7149Snw * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> 1338c8e26105Sjp */ 1339c8e26105Sjp (void) snprintf(rr_name, sizeof (rr_name), 1340c8e26105Sjp LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, 13414d61c878SJulian Pullen site_name_item->value); 13420dcc7149Snw DO_RES_NINIT(ctx); 13434d61c878SJulian Pullen domain_controller = srv_query(&ctx->res_state, rr_name, 13444d61c878SJulian Pullen domain_name_item->value, NULL, &ttl); 13454d61c878SJulian Pullen if (domain_controller == NULL) 13464d61c878SJulian Pullen return (NULL); 13474d61c878SJulian Pullen 13484d61c878SJulian Pullen update_item(&ctx->site_domain_controller, 13494d61c878SJulian Pullen domain_controller, AD_STATE_AUTO, ttl); 13504d61c878SJulian Pullen update_version(&ctx->site_domain_controller, PARAM1, 13514d61c878SJulian Pullen domain_name_item); 13524d61c878SJulian Pullen update_version(&ctx->site_domain_controller, PARAM2, 13534d61c878SJulian Pullen site_name_item); 1354c8e26105Sjp } 13554d61c878SJulian Pullen return (&ctx->site_domain_controller); 1356c8e26105Sjp } 13574d61c878SJulian Pullen return (NULL); 1358c8e26105Sjp } 1359c8e26105Sjp 1360479ac375Sdm idmap_ad_disc_ds_t * 13614d61c878SJulian Pullen ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, 13627a8a68f5SJulian Pullen boolean_t *auto_discovered) 1363c8e26105Sjp { 13644d61c878SJulian Pullen ad_item_t *domain_controller_item; 1365479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 1366c8e26105Sjp 13674d61c878SJulian Pullen domain_controller_item = validate_DomainController(ctx, req); 13684d61c878SJulian Pullen 13694d61c878SJulian Pullen if (domain_controller_item != NULL) { 13704d61c878SJulian Pullen domain_controller = ds_dup(domain_controller_item->value); 13714d61c878SJulian Pullen if (auto_discovered != NULL) 13724d61c878SJulian Pullen *auto_discovered = 13734d61c878SJulian Pullen (domain_controller_item->state == AD_STATE_AUTO); 13744d61c878SJulian Pullen } else if (auto_discovered != NULL) 13757a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 13764d61c878SJulian Pullen 1377c8e26105Sjp return (domain_controller); 1378c8e26105Sjp } 1379c8e26105Sjp 1380c8e26105Sjp 13810dcc7149Snw /* Discover site name (for multi-homed systems the first one found wins) */ 13824d61c878SJulian Pullen static ad_item_t * 1383c8e26105Sjp validate_SiteName(ad_disc_t ctx) 1384c8e26105Sjp { 1385c8e26105Sjp LDAP *ld = NULL; 1386c8e26105Sjp ad_subnet_t *subnets = NULL; 1387c8e26105Sjp char **dn_subnets = NULL; 1388c8e26105Sjp char *dn_root[2]; 1389c8e26105Sjp char *config_naming_context = NULL; 1390c8e26105Sjp char *site_object = NULL; 1391c8e26105Sjp char *site_name = NULL; 1392c8e26105Sjp char *forest_name; 1393c8e26105Sjp int len; 1394c8e26105Sjp int i; 13957a8a68f5SJulian Pullen boolean_t update_required = B_FALSE; 13964d61c878SJulian Pullen ad_item_t *domain_controller_item; 1397c8e26105Sjp 1398c8e26105Sjp if (is_fixed(&ctx->site_name)) 13994d61c878SJulian Pullen return (&ctx->site_name); 1400c8e26105Sjp 14010dcc7149Snw /* Can't rely on site-specific DCs */ 14024d61c878SJulian Pullen domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 14034d61c878SJulian Pullen if (domain_controller_item == NULL) 14044d61c878SJulian Pullen return (NULL); 1405c8e26105Sjp 14064d61c878SJulian Pullen if (!is_valid(&ctx->site_name) || 1407c8e26105Sjp is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) || 1408c8e26105Sjp ctx->subnets == NULL || ctx->subnets_changed) { 1409c8e26105Sjp subnets = find_subnets(); 1410c8e26105Sjp ctx->subnets_last_check = time(NULL); 14117a8a68f5SJulian Pullen update_required = B_TRUE; 1412c8e26105Sjp } else if (ctx->subnets_last_check + 60 < time(NULL)) { 1413c8e26105Sjp subnets = find_subnets(); 1414c8e26105Sjp ctx->subnets_last_check = time(NULL); 1415c8e26105Sjp if (cmpsubnets(ctx->subnets, subnets) != 0) 14167a8a68f5SJulian Pullen update_required = B_TRUE; 1417c8e26105Sjp } 1418c8e26105Sjp 14190dcc7149Snw if (!update_required) { 14200dcc7149Snw free(subnets); 14214d61c878SJulian Pullen return (&ctx->site_name); 14220dcc7149Snw } 1423c8e26105Sjp 14244d61c878SJulian Pullen if (subnets == NULL) 14254d61c878SJulian Pullen return (NULL); 14260dcc7149Snw 14274d61c878SJulian Pullen dn_root[0] = ""; 14284d61c878SJulian Pullen dn_root[1] = NULL; 14290dcc7149Snw 14304d61c878SJulian Pullen config_naming_context = ldap_lookup_entry_attr( 14314d61c878SJulian Pullen &ld, ctx->domain_controller.value, 14324d61c878SJulian Pullen dn_root, "configurationNamingContext"); 14334d61c878SJulian Pullen if (config_naming_context == NULL) 14344d61c878SJulian Pullen goto out; 14354d61c878SJulian Pullen /* 14364d61c878SJulian Pullen * configurationNamingContext also provides the Forest 14374d61c878SJulian Pullen * Name. 14384d61c878SJulian Pullen */ 14394d61c878SJulian Pullen if (!is_fixed(&ctx->forest_name)) { 14400dcc7149Snw /* 14414d61c878SJulian Pullen * The configurationNamingContext should be of 14424d61c878SJulian Pullen * form: 14434d61c878SJulian Pullen * CN=Configuration,<DNforestName> 14444d61c878SJulian Pullen * Remove the first part and convert to DNS form 14454d61c878SJulian Pullen * (replace ",DC=" with ".") 14460dcc7149Snw */ 14474d61c878SJulian Pullen char *str = "CN=Configuration,"; 14484d61c878SJulian Pullen int len = strlen(str); 14494d61c878SJulian Pullen if (strncasecmp(config_naming_context, str, len) == 0) { 14504d61c878SJulian Pullen forest_name = DN_to_DNS(config_naming_context + len); 14514d61c878SJulian Pullen update_item(&ctx->forest_name, forest_name, 14524d61c878SJulian Pullen AD_STATE_AUTO, 0); 14530dcc7149Snw } 14544d61c878SJulian Pullen } 14554d61c878SJulian Pullen 14564d61c878SJulian Pullen dn_subnets = subnets_to_DNs(subnets, config_naming_context); 14574d61c878SJulian Pullen if (dn_subnets == NULL) 14584d61c878SJulian Pullen goto out; 14594d61c878SJulian Pullen 14604d61c878SJulian Pullen site_object = ldap_lookup_entry_attr( 14614d61c878SJulian Pullen &ld, domain_controller_item->value, 14624d61c878SJulian Pullen dn_subnets, "siteobject"); 14634d61c878SJulian Pullen if (site_object != NULL) { 14644d61c878SJulian Pullen /* 14654d61c878SJulian Pullen * The site object should be of the form 14664d61c878SJulian Pullen * CN=<site>,CN=Sites,CN=Configuration, 14674d61c878SJulian Pullen * <DN Domain> 14684d61c878SJulian Pullen */ 14694d61c878SJulian Pullen if (strncasecmp(site_object, "CN=", 3) == 0) { 14704d61c878SJulian Pullen for (len = 0; site_object[len + 3] != ','; len++) 14710dcc7149Snw ; 14724d61c878SJulian Pullen site_name = malloc(len + 1); 14734d61c878SJulian Pullen (void) strncpy(site_name, &site_object[3], len); 14744d61c878SJulian Pullen site_name[len] = '\0'; 14754d61c878SJulian Pullen update_item(&ctx->site_name, site_name, 14764d61c878SJulian Pullen AD_STATE_AUTO, 0); 14770dcc7149Snw } 14784d61c878SJulian Pullen } 1479c8e26105Sjp 14804d61c878SJulian Pullen if (ctx->subnets != NULL) { 14814d61c878SJulian Pullen free(ctx->subnets); 14824d61c878SJulian Pullen ctx->subnets = NULL; 14830dcc7149Snw } 14844d61c878SJulian Pullen ctx->subnets = subnets; 14854d61c878SJulian Pullen subnets = NULL; 14867a8a68f5SJulian Pullen ctx->subnets_changed = B_FALSE; 14874d61c878SJulian Pullen 1488c8e26105Sjp out: 14890dcc7149Snw if (ld != NULL) 14900dcc7149Snw (void) ldap_unbind(ld); 1491c8e26105Sjp 14920dcc7149Snw if (dn_subnets != NULL) { 14930dcc7149Snw for (i = 0; dn_subnets[i] != NULL; i++) 14940dcc7149Snw free(dn_subnets[i]); 14950dcc7149Snw free(dn_subnets); 14960dcc7149Snw } 14970dcc7149Snw if (config_naming_context != NULL) 14980dcc7149Snw free(config_naming_context); 14990dcc7149Snw if (site_object != NULL) 15000dcc7149Snw free(site_object); 15010dcc7149Snw 15020dcc7149Snw free(subnets); 15034d61c878SJulian Pullen if (site_name == NULL) 15044d61c878SJulian Pullen return (NULL); 15054d61c878SJulian Pullen return (&ctx->site_name); 1506c8e26105Sjp 1507c8e26105Sjp } 1508c8e26105Sjp 1509c8e26105Sjp 1510c8e26105Sjp char * 15117a8a68f5SJulian Pullen ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) 1512c8e26105Sjp { 15134d61c878SJulian Pullen ad_item_t *site_name_item; 1514c8e26105Sjp char *site_name = NULL; 1515c8e26105Sjp 15164d61c878SJulian Pullen site_name_item = validate_SiteName(ctx); 15174d61c878SJulian Pullen if (site_name_item != NULL) { 15184d61c878SJulian Pullen site_name = strdup(site_name_item->value); 15194d61c878SJulian Pullen if (auto_discovered != NULL) 15204d61c878SJulian Pullen *auto_discovered = 15214d61c878SJulian Pullen (site_name_item->state == AD_STATE_AUTO); 15224d61c878SJulian Pullen } else if (auto_discovered != NULL) 15237a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 15244d61c878SJulian Pullen 1525c8e26105Sjp return (site_name); 1526c8e26105Sjp } 1527c8e26105Sjp 1528c8e26105Sjp 1529c8e26105Sjp 15300dcc7149Snw /* Discover forest name */ 15314d61c878SJulian Pullen static ad_item_t * 1532c8e26105Sjp validate_ForestName(ad_disc_t ctx) 1533c8e26105Sjp { 1534c8e26105Sjp LDAP *ld = NULL; 1535c8e26105Sjp char *config_naming_context; 1536c8e26105Sjp char *forest_name = NULL; 1537c8e26105Sjp char *dn_list[2]; 15384d61c878SJulian Pullen ad_item_t *domain_controller_item; 1539c8e26105Sjp 1540c8e26105Sjp if (is_fixed(&ctx->forest_name)) 15414d61c878SJulian Pullen return (&ctx->forest_name); 1542c8e26105Sjp /* 15430dcc7149Snw * We may not have a site name yet, so we won't rely on 15440dcc7149Snw * site-specific DCs. (But maybe we could replace 15450dcc7149Snw * validate_ForestName() with validate_siteName()?) 1546c8e26105Sjp */ 15474d61c878SJulian Pullen domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 15484d61c878SJulian Pullen if (domain_controller_item == NULL) 15494d61c878SJulian Pullen return (NULL); 1550c8e26105Sjp 15514d61c878SJulian Pullen if (!is_valid(&ctx->forest_name) || 15524d61c878SJulian Pullen is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { 15534d61c878SJulian Pullen 15544d61c878SJulian Pullen dn_list[0] = ""; 15554d61c878SJulian Pullen dn_list[1] = NULL; 15564d61c878SJulian Pullen config_naming_context = ldap_lookup_entry_attr( 15574d61c878SJulian Pullen &ld, ctx->domain_controller.value, 15584d61c878SJulian Pullen dn_list, "configurationNamingContext"); 15594d61c878SJulian Pullen if (config_naming_context != NULL) { 15604d61c878SJulian Pullen /* 15614d61c878SJulian Pullen * The configurationNamingContext should be of 15624d61c878SJulian Pullen * form: 15634d61c878SJulian Pullen * CN=Configuration,<DNforestName> 15644d61c878SJulian Pullen * Remove the first part and convert to DNS form 15654d61c878SJulian Pullen * (replace ",DC=" with ".") 15664d61c878SJulian Pullen */ 15674d61c878SJulian Pullen char *str = "CN=Configuration,"; 15684d61c878SJulian Pullen int len = strlen(str); 15694d61c878SJulian Pullen if (strncasecmp(config_naming_context, str, len) == 0) { 15704d61c878SJulian Pullen forest_name = DN_to_DNS( 15714d61c878SJulian Pullen config_naming_context + len); 1572c8e26105Sjp } 15734d61c878SJulian Pullen free(config_naming_context); 1574c8e26105Sjp } 15754d61c878SJulian Pullen if (ld != NULL) 15764d61c878SJulian Pullen (void) ldap_unbind(ld); 15774d61c878SJulian Pullen 15784d61c878SJulian Pullen if (forest_name == NULL) 15794d61c878SJulian Pullen return (NULL); 15804d61c878SJulian Pullen 15814d61c878SJulian Pullen update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); 15824d61c878SJulian Pullen update_version(&ctx->forest_name, PARAM1, 15834d61c878SJulian Pullen domain_controller_item); 1584c8e26105Sjp } 15854d61c878SJulian Pullen return (&ctx->forest_name); 1586c8e26105Sjp } 1587c8e26105Sjp 1588c8e26105Sjp 1589c8e26105Sjp char * 15907a8a68f5SJulian Pullen ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) 1591c8e26105Sjp { 15924d61c878SJulian Pullen ad_item_t *forest_name_item; 1593c8e26105Sjp char *forest_name = NULL; 1594c8e26105Sjp 15954d61c878SJulian Pullen forest_name_item = validate_ForestName(ctx); 15964d61c878SJulian Pullen 15974d61c878SJulian Pullen if (forest_name_item != NULL) { 15984d61c878SJulian Pullen forest_name = strdup(forest_name_item->value); 15994d61c878SJulian Pullen if (auto_discovered != NULL) 16004d61c878SJulian Pullen *auto_discovered = 16014d61c878SJulian Pullen (forest_name_item->state == AD_STATE_AUTO); 16024d61c878SJulian Pullen } else if (auto_discovered != NULL) 16037a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 1604c8e26105Sjp 1605c8e26105Sjp return (forest_name); 1606c8e26105Sjp } 1607c8e26105Sjp 1608c8e26105Sjp 16090dcc7149Snw /* Discover global catalog servers */ 16104d61c878SJulian Pullen static ad_item_t * 1611c8e26105Sjp validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) 1612c8e26105Sjp { 1613479ac375Sdm idmap_ad_disc_ds_t *global_catalog = NULL; 16140dcc7149Snw uint32_t ttl = 0; 16157a8a68f5SJulian Pullen boolean_t validate_global = B_FALSE; 16167a8a68f5SJulian Pullen boolean_t validate_site = B_FALSE; 16174d61c878SJulian Pullen ad_item_t *forest_name_item; 16184d61c878SJulian Pullen ad_item_t *site_name_item; 1619c8e26105Sjp 16204d61c878SJulian Pullen /* If the values is fixed there will not be a site specific version */ 1621c8e26105Sjp if (is_fixed(&ctx->global_catalog)) 16224d61c878SJulian Pullen return (&ctx->global_catalog); 16234d61c878SJulian Pullen 16244d61c878SJulian Pullen forest_name_item = validate_ForestName(ctx); 16254d61c878SJulian Pullen if (forest_name_item == NULL) 16264d61c878SJulian Pullen return (NULL); 1627c8e26105Sjp 1628c8e26105Sjp if (req == AD_DISC_GLOBAL) 16297a8a68f5SJulian Pullen validate_global = B_TRUE; 1630c8e26105Sjp else { 16314d61c878SJulian Pullen site_name_item = validate_SiteName(ctx); 16324d61c878SJulian Pullen if (site_name_item != NULL) 16337a8a68f5SJulian Pullen validate_site = B_TRUE; 16344d61c878SJulian Pullen else if (req == AD_DISC_PREFER_SITE) 16357a8a68f5SJulian Pullen validate_global = B_TRUE; 1636c8e26105Sjp } 1637c8e26105Sjp 16384d61c878SJulian Pullen if (validate_global) { 16394d61c878SJulian Pullen if (!is_valid(&ctx->global_catalog) || 16404d61c878SJulian Pullen is_changed(&ctx->global_catalog, PARAM1, 16414d61c878SJulian Pullen forest_name_item)) { 1642c8e26105Sjp /* 16430dcc7149Snw * Lookup DNS SRV RR named 16440dcc7149Snw * _ldap._tcp.gc._msdcs.<ForestName> 1645c8e26105Sjp */ 16460dcc7149Snw DO_RES_NINIT(ctx); 1647c8e26105Sjp global_catalog = 16484d61c878SJulian Pullen srv_query(&ctx->res_state, 16494d61c878SJulian Pullen LDAP_SRV_HEAD GC_SRV_TAIL, 16504d61c878SJulian Pullen ctx->forest_name.value, NULL, &ttl); 1651c8e26105Sjp 16524d61c878SJulian Pullen if (global_catalog == NULL) 16534d61c878SJulian Pullen return (NULL); 1654c8e26105Sjp 16554d61c878SJulian Pullen update_item(&ctx->global_catalog, global_catalog, 16564d61c878SJulian Pullen AD_STATE_AUTO, ttl); 16574d61c878SJulian Pullen update_version(&ctx->global_catalog, PARAM1, 16584d61c878SJulian Pullen forest_name_item); 16594d61c878SJulian Pullen } 16604d61c878SJulian Pullen return (&ctx->global_catalog); 16614d61c878SJulian Pullen } 1662c8e26105Sjp 16634d61c878SJulian Pullen if (validate_site) { 16644d61c878SJulian Pullen if (!is_valid(&ctx->site_global_catalog) || 16654d61c878SJulian Pullen is_changed(&ctx->site_global_catalog, PARAM1, 16664d61c878SJulian Pullen forest_name_item) || 16674d61c878SJulian Pullen is_changed(&ctx->site_global_catalog, PARAM2, 16684d61c878SJulian Pullen site_name_item)) { 1669c8e26105Sjp char rr_name[DNS_MAX_NAME]; 16704d61c878SJulian Pullen 1671c8e26105Sjp /* 16720dcc7149Snw * Lookup DNS SRV RR named: 16730dcc7149Snw * _ldap._tcp.<siteName>._sites.gc. 16740dcc7149Snw * _msdcs.<ForestName> 1675c8e26105Sjp */ 1676c8e26105Sjp (void) snprintf(rr_name, 1677c8e26105Sjp sizeof (rr_name), 1678c8e26105Sjp LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, 16794d61c878SJulian Pullen ctx->site_name.value); 16800dcc7149Snw DO_RES_NINIT(ctx); 16814d61c878SJulian Pullen global_catalog = srv_query(&ctx->res_state, rr_name, 16824d61c878SJulian Pullen ctx->forest_name.value, NULL, &ttl); 16834d61c878SJulian Pullen 16844d61c878SJulian Pullen if (global_catalog == NULL) 16854d61c878SJulian Pullen return (NULL); 16864d61c878SJulian Pullen update_item(&ctx->site_global_catalog, global_catalog, 16874d61c878SJulian Pullen AD_STATE_AUTO, ttl); 16884d61c878SJulian Pullen update_version(&ctx->site_global_catalog, PARAM1, 16894d61c878SJulian Pullen forest_name_item); 16904d61c878SJulian Pullen update_version(&ctx->site_global_catalog, PARAM2, 16914d61c878SJulian Pullen site_name_item); 1692c8e26105Sjp } 16934d61c878SJulian Pullen return (&ctx->site_global_catalog); 1694c8e26105Sjp } 16954d61c878SJulian Pullen return (NULL); 1696c8e26105Sjp } 1697c8e26105Sjp 1698c8e26105Sjp 1699479ac375Sdm idmap_ad_disc_ds_t * 17004d61c878SJulian Pullen ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, 17017a8a68f5SJulian Pullen boolean_t *auto_discovered) 1702c8e26105Sjp { 1703479ac375Sdm idmap_ad_disc_ds_t *global_catalog = NULL; 17044d61c878SJulian Pullen ad_item_t *global_catalog_item; 17054d61c878SJulian Pullen 17064d61c878SJulian Pullen global_catalog_item = validate_GlobalCatalog(ctx, req); 1707c8e26105Sjp 17084d61c878SJulian Pullen if (global_catalog_item != NULL) { 17094d61c878SJulian Pullen global_catalog = ds_dup(global_catalog_item->value); 17104d61c878SJulian Pullen if (auto_discovered != NULL) 17114d61c878SJulian Pullen *auto_discovered = 17124d61c878SJulian Pullen (global_catalog_item->state == AD_STATE_AUTO); 17134d61c878SJulian Pullen } else if (auto_discovered != NULL) 17147a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 1715c8e26105Sjp 1716c8e26105Sjp return (global_catalog); 1717c8e26105Sjp } 1718c8e26105Sjp 1719c8e26105Sjp 17204d61c878SJulian Pullen static ad_item_t * 17214d61c878SJulian Pullen validate_TrustedDomains(ad_disc_t ctx) 17224d61c878SJulian Pullen { 17234d61c878SJulian Pullen LDAP *ld = NULL; 17244d61c878SJulian Pullen ad_item_t *global_catalog_item; 17254d61c878SJulian Pullen ad_item_t *forest_name_item; 17264d61c878SJulian Pullen ad_disc_trusteddomains_t *trusted_domains; 17274d61c878SJulian Pullen char *dn = NULL; 17284d61c878SJulian Pullen char *forest_name_dn; 17294d61c878SJulian Pullen int len; 17304d61c878SJulian Pullen int num_parts; 17314d61c878SJulian Pullen 17324d61c878SJulian Pullen if (is_fixed(&ctx->trusted_domains)) 17334d61c878SJulian Pullen return (&ctx->trusted_domains); 17344d61c878SJulian Pullen 17354d61c878SJulian Pullen global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 17364d61c878SJulian Pullen if (global_catalog_item == NULL) 17374d61c878SJulian Pullen return (NULL); 17384d61c878SJulian Pullen 17394d61c878SJulian Pullen forest_name_item = validate_ForestName(ctx); 17404d61c878SJulian Pullen if (forest_name_item == NULL) 17414d61c878SJulian Pullen return (NULL); 17424d61c878SJulian Pullen 17434d61c878SJulian Pullen if (!is_valid(&ctx->trusted_domains) || 17444d61c878SJulian Pullen is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || 17454d61c878SJulian Pullen is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { 17464d61c878SJulian Pullen 17474d61c878SJulian Pullen forest_name_dn = ldap_dns_to_dn(forest_name_item->value, 17484d61c878SJulian Pullen &num_parts); 17494d61c878SJulian Pullen if (forest_name_dn == NULL) 17504d61c878SJulian Pullen return (NULL); 17514d61c878SJulian Pullen 17524d61c878SJulian Pullen len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; 17534d61c878SJulian Pullen dn = malloc(len); 17544d61c878SJulian Pullen if (dn == NULL) { 17554d61c878SJulian Pullen free(forest_name_dn); 17564d61c878SJulian Pullen return (NULL); 17574d61c878SJulian Pullen } 17584d61c878SJulian Pullen (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); 17594d61c878SJulian Pullen free(forest_name_dn); 17604d61c878SJulian Pullen 17614d61c878SJulian Pullen trusted_domains = ldap_lookup_trusted_domains( 17624d61c878SJulian Pullen &ld, global_catalog_item->value, dn); 17634d61c878SJulian Pullen 17644d61c878SJulian Pullen if (ld != NULL) 17654d61c878SJulian Pullen (void) ldap_unbind(ld); 17664d61c878SJulian Pullen free(dn); 17674d61c878SJulian Pullen 17684d61c878SJulian Pullen if (trusted_domains == NULL) 17694d61c878SJulian Pullen return (NULL); 17704d61c878SJulian Pullen 17714d61c878SJulian Pullen update_item(&ctx->trusted_domains, trusted_domains, 17724d61c878SJulian Pullen AD_STATE_AUTO, 0); 17734d61c878SJulian Pullen update_version(&ctx->trusted_domains, PARAM1, 17744d61c878SJulian Pullen global_catalog_item); 17754d61c878SJulian Pullen update_version(&ctx->trusted_domains, PARAM2, 17764d61c878SJulian Pullen forest_name_item); 17774d61c878SJulian Pullen } 17784d61c878SJulian Pullen 17794d61c878SJulian Pullen return (&ctx->trusted_domains); 17804d61c878SJulian Pullen } 17814d61c878SJulian Pullen 17824d61c878SJulian Pullen 17834d61c878SJulian Pullen ad_disc_trusteddomains_t * 17847a8a68f5SJulian Pullen ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered) 17854d61c878SJulian Pullen { 17864d61c878SJulian Pullen ad_disc_trusteddomains_t *trusted_domains = NULL; 17874d61c878SJulian Pullen ad_item_t *trusted_domains_item; 17884d61c878SJulian Pullen 17894d61c878SJulian Pullen trusted_domains_item = validate_TrustedDomains(ctx); 17904d61c878SJulian Pullen 17914d61c878SJulian Pullen if (trusted_domains_item != NULL) { 17924d61c878SJulian Pullen trusted_domains = td_dup(trusted_domains_item->value); 17934d61c878SJulian Pullen if (auto_discovered != NULL) 17944d61c878SJulian Pullen *auto_discovered = 17954d61c878SJulian Pullen (trusted_domains_item->state == AD_STATE_AUTO); 17964d61c878SJulian Pullen } else if (auto_discovered != NULL) 17977a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 17984d61c878SJulian Pullen 17994d61c878SJulian Pullen return (trusted_domains); 18004d61c878SJulian Pullen } 18014d61c878SJulian Pullen 18024d61c878SJulian Pullen 18034d61c878SJulian Pullen static ad_item_t * 18044d61c878SJulian Pullen validate_DomainsInForest(ad_disc_t ctx) 18054d61c878SJulian Pullen { 18064d61c878SJulian Pullen ad_item_t *global_catalog_item; 18074d61c878SJulian Pullen LDAP *ld = NULL; 18084d61c878SJulian Pullen ad_disc_domainsinforest_t *domains_in_forest; 18094d61c878SJulian Pullen 18104d61c878SJulian Pullen if (is_fixed(&ctx->domains_in_forest)) 18114d61c878SJulian Pullen return (&ctx->domains_in_forest); 18124d61c878SJulian Pullen 18134d61c878SJulian Pullen global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 18144d61c878SJulian Pullen if (global_catalog_item == NULL) 18154d61c878SJulian Pullen return (NULL); 18164d61c878SJulian Pullen 18174d61c878SJulian Pullen if (!is_valid(&ctx->domains_in_forest) || 18184d61c878SJulian Pullen is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { 18194d61c878SJulian Pullen 18204d61c878SJulian Pullen domains_in_forest = ldap_lookup_domains_in_forest( 18214d61c878SJulian Pullen &ld, global_catalog_item->value); 18224d61c878SJulian Pullen 18234d61c878SJulian Pullen if (ld != NULL) 18244d61c878SJulian Pullen (void) ldap_unbind(ld); 18254d61c878SJulian Pullen 18264d61c878SJulian Pullen if (domains_in_forest == NULL) 18274d61c878SJulian Pullen return (NULL); 18284d61c878SJulian Pullen 18294d61c878SJulian Pullen update_item(&ctx->domains_in_forest, domains_in_forest, 18304d61c878SJulian Pullen AD_STATE_AUTO, 0); 18314d61c878SJulian Pullen update_version(&ctx->domains_in_forest, PARAM1, 18324d61c878SJulian Pullen global_catalog_item); 18334d61c878SJulian Pullen } 18344d61c878SJulian Pullen return (&ctx->domains_in_forest); 18354d61c878SJulian Pullen } 18364d61c878SJulian Pullen 18374d61c878SJulian Pullen 18384d61c878SJulian Pullen ad_disc_domainsinforest_t * 18397a8a68f5SJulian Pullen ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) 18404d61c878SJulian Pullen { 18414d61c878SJulian Pullen ad_disc_domainsinforest_t *domains_in_forest = NULL; 18424d61c878SJulian Pullen ad_item_t *domains_in_forest_item; 18434d61c878SJulian Pullen 18444d61c878SJulian Pullen domains_in_forest_item = validate_DomainsInForest(ctx); 18454d61c878SJulian Pullen 18464d61c878SJulian Pullen if (domains_in_forest_item != NULL) { 18474d61c878SJulian Pullen domains_in_forest = df_dup(domains_in_forest_item->value); 18484d61c878SJulian Pullen if (auto_discovered != NULL) 18494d61c878SJulian Pullen *auto_discovered = 18504d61c878SJulian Pullen (domains_in_forest_item->state == AD_STATE_AUTO); 18514d61c878SJulian Pullen } else if (auto_discovered != NULL) 18527a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 18534d61c878SJulian Pullen 18544d61c878SJulian Pullen return (domains_in_forest); 18554d61c878SJulian Pullen } 18564d61c878SJulian Pullen 18574d61c878SJulian Pullen 18584d61c878SJulian Pullen 18594d61c878SJulian Pullen 1860c8e26105Sjp int 1861c8e26105Sjp ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) 1862c8e26105Sjp { 1863c8e26105Sjp char *domain_name = NULL; 1864c8e26105Sjp if (domainName != NULL) { 1865c8e26105Sjp domain_name = strdup(domainName); 1866c8e26105Sjp if (domain_name == NULL) 1867c8e26105Sjp return (-1); 18684d61c878SJulian Pullen update_item(&ctx->domain_name, domain_name, 18694d61c878SJulian Pullen AD_STATE_FIXED, 0); 18704d61c878SJulian Pullen } else if (ctx->domain_name.state == AD_STATE_FIXED) 18714d61c878SJulian Pullen ctx->domain_name.state = AD_STATE_INVALID; 1872c8e26105Sjp return (0); 1873c8e26105Sjp } 1874c8e26105Sjp 1875c8e26105Sjp 1876c8e26105Sjp int 1877c8e26105Sjp ad_disc_set_DomainController(ad_disc_t ctx, 1878479ac375Sdm const idmap_ad_disc_ds_t *domainController) 1879c8e26105Sjp { 1880479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 1881c8e26105Sjp if (domainController != NULL) { 18824d61c878SJulian Pullen domain_controller = ds_dup(domainController); 1883c8e26105Sjp if (domain_controller == NULL) 1884c8e26105Sjp return (-1); 18854d61c878SJulian Pullen update_item(&ctx->domain_controller, domain_controller, 18864d61c878SJulian Pullen AD_STATE_FIXED, 0); 18874d61c878SJulian Pullen } else if (ctx->domain_controller.state == AD_STATE_FIXED) 18884d61c878SJulian Pullen ctx->domain_controller.state = AD_STATE_INVALID; 1889c8e26105Sjp return (0); 1890c8e26105Sjp } 1891c8e26105Sjp 1892c8e26105Sjp 1893c8e26105Sjp int 1894c8e26105Sjp ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) 1895c8e26105Sjp { 1896c8e26105Sjp char *site_name = NULL; 1897c8e26105Sjp if (siteName != NULL) { 1898c8e26105Sjp site_name = strdup(siteName); 1899c8e26105Sjp if (site_name == NULL) 1900c8e26105Sjp return (-1); 19014d61c878SJulian Pullen update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); 19024d61c878SJulian Pullen } else if (ctx->site_name.state == AD_STATE_FIXED) 19034d61c878SJulian Pullen ctx->site_name.state = AD_STATE_INVALID; 1904c8e26105Sjp return (0); 1905c8e26105Sjp } 1906c8e26105Sjp 1907c8e26105Sjp int 1908c8e26105Sjp ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) 1909c8e26105Sjp { 1910c8e26105Sjp char *forest_name = NULL; 1911c8e26105Sjp if (forestName != NULL) { 1912c8e26105Sjp forest_name = strdup(forestName); 1913c8e26105Sjp if (forest_name == NULL) 1914c8e26105Sjp return (-1); 19154d61c878SJulian Pullen update_item(&ctx->forest_name, forest_name, 19164d61c878SJulian Pullen AD_STATE_FIXED, 0); 19174d61c878SJulian Pullen } else if (ctx->forest_name.state == AD_STATE_FIXED) 19184d61c878SJulian Pullen ctx->forest_name.state = AD_STATE_INVALID; 1919c8e26105Sjp return (0); 1920c8e26105Sjp } 1921c8e26105Sjp 1922c8e26105Sjp int 1923479ac375Sdm ad_disc_set_GlobalCatalog(ad_disc_t ctx, 1924479ac375Sdm const idmap_ad_disc_ds_t *globalCatalog) 1925c8e26105Sjp { 1926479ac375Sdm idmap_ad_disc_ds_t *global_catalog = NULL; 1927c8e26105Sjp if (globalCatalog != NULL) { 19284d61c878SJulian Pullen global_catalog = ds_dup(globalCatalog); 1929c8e26105Sjp if (global_catalog == NULL) 1930c8e26105Sjp return (-1); 19314d61c878SJulian Pullen update_item(&ctx->global_catalog, global_catalog, 19324d61c878SJulian Pullen AD_STATE_FIXED, 0); 19334d61c878SJulian Pullen } else if (ctx->global_catalog.state == AD_STATE_FIXED) 19344d61c878SJulian Pullen ctx->global_catalog.state = AD_STATE_INVALID; 1935c8e26105Sjp return (0); 1936c8e26105Sjp } 1937c8e26105Sjp 1938c8e26105Sjp 1939c8e26105Sjp int 1940c8e26105Sjp ad_disc_unset(ad_disc_t ctx) 1941c8e26105Sjp { 19424d61c878SJulian Pullen if (ctx->domain_name.state == AD_STATE_FIXED) 19434d61c878SJulian Pullen ctx->domain_name.state = AD_STATE_INVALID; 1944c8e26105Sjp 19454d61c878SJulian Pullen if (ctx->domain_controller.state == AD_STATE_FIXED) 19464d61c878SJulian Pullen ctx->domain_controller.state = AD_STATE_INVALID; 1947c8e26105Sjp 19484d61c878SJulian Pullen if (ctx->site_name.state == AD_STATE_FIXED) 19494d61c878SJulian Pullen ctx->site_name.state = AD_STATE_INVALID; 1950c8e26105Sjp 19514d61c878SJulian Pullen if (ctx->forest_name.state == AD_STATE_FIXED) 19524d61c878SJulian Pullen ctx->forest_name.state = AD_STATE_INVALID; 1953c8e26105Sjp 19544d61c878SJulian Pullen if (ctx->global_catalog.state == AD_STATE_FIXED) 19554d61c878SJulian Pullen ctx->global_catalog.state = AD_STATE_INVALID; 1956c8e26105Sjp 1957c8e26105Sjp return (0); 1958c8e26105Sjp } 1959c8e26105Sjp 1960c8e26105Sjp /* 1961c8e26105Sjp * ad_disc_get_TTL 1962c8e26105Sjp * 1963c8e26105Sjp * This routines the time to live for AD 1964c8e26105Sjp * auto discovered items. 1965c8e26105Sjp * 1966c8e26105Sjp * Returns: 1967c8e26105Sjp * -1 if there are no TTL items 1968c8e26105Sjp * 0 if there are expired items 1969c8e26105Sjp * else the number of seconds 19700dcc7149Snw * 19710dcc7149Snw * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it 19720dcc7149Snw * is positive -- min() greater than zero. 1973c8e26105Sjp */ 19740dcc7149Snw #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ 19750dcc7149Snw (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) 1976c8e26105Sjp int 1977c8e26105Sjp ad_disc_get_TTL(ad_disc_t ctx) 1978c8e26105Sjp { 1979c8e26105Sjp int ttl; 1980c8e26105Sjp 19810dcc7149Snw ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl); 19820dcc7149Snw ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl); 19830dcc7149Snw ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl); 1984c8e26105Sjp 19850dcc7149Snw if (ttl == -1) 1986c8e26105Sjp return (-1); 1987c8e26105Sjp ttl -= time(NULL); 1988c8e26105Sjp if (ttl < 0) 19890dcc7149Snw return (0); 1990c8e26105Sjp return (ttl); 1991c8e26105Sjp } 1992c8e26105Sjp 19937a8a68f5SJulian Pullen boolean_t 1994c8e26105Sjp ad_disc_SubnetChanged(ad_disc_t ctx) 1995c8e26105Sjp { 1996c8e26105Sjp ad_subnet_t *subnets; 1997c8e26105Sjp 1998c8e26105Sjp if (ctx->subnets_changed || ctx->subnets == NULL) 19997a8a68f5SJulian Pullen return (B_TRUE); 2000c8e26105Sjp 2001c8e26105Sjp if ((subnets = find_subnets()) != NULL) { 2002c8e26105Sjp if (cmpsubnets(subnets, ctx->subnets) != 0) 20037a8a68f5SJulian Pullen ctx->subnets_changed = B_TRUE; 2004c8e26105Sjp free(subnets); 2005c8e26105Sjp } 2006c8e26105Sjp 2007c8e26105Sjp return (ctx->subnets_changed); 2008c8e26105Sjp } 2009