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++) { 332*1fcced4cSJordan 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++) { 388*1fcced4cSJordan 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) { 711c8e26105Sjp len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 712c8e26105Sjp msg.buf, sizeof (msg.buf)); 7137a8a68f5SJulian Pullen logger(LOG_DEBUG, 7147a8a68f5SJulian Pullen "Querying DNS for SRV RRs named '%s' for '%s' ", 7157a8a68f5SJulian Pullen svc_name, dname); 716c8e26105Sjp 7177a8a68f5SJulian Pullen if (len < 0) { 7187a8a68f5SJulian Pullen logger(LOG_DEBUG, 7197a8a68f5SJulian Pullen "DNS query for '%s' for '%s' failed (%s)", 7207a8a68f5SJulian Pullen svc_name, dname, hstrerror(state->res_h_errno)); 7217a8a68f5SJulian Pullen return (NULL); 7227a8a68f5SJulian Pullen } 723c8e26105Sjp } 7247a8a68f5SJulian Pullen 725c8e26105Sjp if (len > sizeof (msg.buf)) { 7267a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query %ib message doesn't fit" 727c8e26105Sjp " into %ib buffer", 72871590c90Snw len, sizeof (msg.buf)); 729c8e26105Sjp return (NULL); 730c8e26105Sjp } 731c8e26105Sjp 732c8e26105Sjp /* 2. parse the reply, skip header and question sections */ 733c8e26105Sjp 734c8e26105Sjp ptr = msg.buf + sizeof (msg.hdr); 735c8e26105Sjp eom = msg.buf + len; 736c8e26105Sjp qdcount = ntohs(msg.hdr.qdcount); 737c8e26105Sjp ancount = ntohs(msg.hdr.ancount); 738c8e26105Sjp 739c8e26105Sjp for (cnt = qdcount; cnt > 0; --cnt) { 740c8e26105Sjp if ((len = dn_skipname(ptr, eom)) < 0) { 7417a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid message format"); 742c8e26105Sjp return (NULL); 743c8e26105Sjp } 744c8e26105Sjp ptr += len + QFIXEDSZ; 745c8e26105Sjp } 746c8e26105Sjp 747c8e26105Sjp /* 3. walk through the answer section */ 748c8e26105Sjp 749479ac375Sdm srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); 750c8e26105Sjp *ttl = (uint32_t)-1; 751c8e26105Sjp 752c8e26105Sjp for (srv = srv_res, cnt = ancount; 753c8e26105Sjp cnt > 0; --cnt, srv++) { 754c8e26105Sjp 755c8e26105Sjp len = dn_expand(msg.buf, eom, ptr, namebuf, 756c8e26105Sjp sizeof (namebuf)); 757c8e26105Sjp if (len < 0) { 7587a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid message format"); 759c8e26105Sjp return (NULL); 760c8e26105Sjp } 761c8e26105Sjp if (rrname != NULL && *rrname == NULL) 762c8e26105Sjp *rrname = strdup(namebuf); 763c8e26105Sjp ptr += len; 764c8e26105Sjp NS_GET16(type, ptr); 765c8e26105Sjp NS_GET16(class, ptr); 766c8e26105Sjp NS_GET32(rttl, ptr); 767c8e26105Sjp NS_GET16(size, ptr); 768c8e26105Sjp if ((end = ptr + size) > eom) { 7697a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid message format"); 770c8e26105Sjp return (NULL); 771c8e26105Sjp } 772c8e26105Sjp 773c8e26105Sjp if (type != T_SRV) { 774c8e26105Sjp ptr = end; 775c8e26105Sjp continue; 776c8e26105Sjp } 777c8e26105Sjp 778c8e26105Sjp NS_GET16(srv->priority, ptr); 779c8e26105Sjp NS_GET16(srv->weight, ptr); 780c8e26105Sjp NS_GET16(srv->port, ptr); 781c8e26105Sjp len = dn_expand(msg.buf, eom, ptr, srv->host, 782c8e26105Sjp sizeof (srv->host)); 783c8e26105Sjp if (len < 0) { 7847a8a68f5SJulian Pullen logger(LOG_ERR, "DNS query invalid SRV record"); 785c8e26105Sjp return (NULL); 786c8e26105Sjp } 787c8e26105Sjp 788c8e26105Sjp if (rttl < *ttl) 789c8e26105Sjp *ttl = rttl; 790c8e26105Sjp 7917a8a68f5SJulian Pullen logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d", 79271590c90Snw namebuf, rttl, srv->priority, srv->weight, srv->host, 79371590c90Snw srv->port); 794c8e26105Sjp 795c8e26105Sjp /* 3. move ptr to the end of current record */ 796c8e26105Sjp 797c8e26105Sjp ptr = end; 798c8e26105Sjp } 799c8e26105Sjp 800c8e26105Sjp if (ancount > 1) 801c8e26105Sjp qsort(srv_res, ancount, sizeof (*srv_res), 802c8e26105Sjp (int (*)(const void *, const void *))srvcmp); 803c8e26105Sjp 804c8e26105Sjp return (srv_res); 805c8e26105Sjp } 806c8e26105Sjp 807c8e26105Sjp 8084d61c878SJulian Pullen /* 8094d61c878SJulian Pullen * A utility function to bind to a Directory server 8104d61c878SJulian Pullen */ 8114d61c878SJulian Pullen 8124d61c878SJulian Pullen static LDAP* 8134d61c878SJulian Pullen ldap_lookup_init(idmap_ad_disc_ds_t *ds) 8144d61c878SJulian Pullen { 8154d61c878SJulian Pullen int i; 8164d61c878SJulian Pullen int rc, ldversion; 8174d61c878SJulian Pullen int zero = 0; 8184d61c878SJulian Pullen int timeoutms = 5 * 1000; 8194d61c878SJulian Pullen char *saslmech = "GSSAPI"; 8204d61c878SJulian Pullen uint32_t saslflags = LDAP_SASL_INTERACTIVE; 8214d61c878SJulian Pullen LDAP *ld = NULL; 8224d61c878SJulian Pullen 8234d61c878SJulian Pullen for (i = 0; ds[i].host[0] != '\0'; i++) { 8244d61c878SJulian Pullen ld = ldap_init(ds[i].host, ds[i].port); 8254d61c878SJulian Pullen if (ld == NULL) { 8267a8a68f5SJulian Pullen logger(LOG_DEBUG, "Couldn't connect to " 8274d61c878SJulian Pullen "AD DC %s:%d (%s)", 8284d61c878SJulian Pullen ds[i].host, ds[i].port, 8294d61c878SJulian Pullen strerror(errno)); 8304d61c878SJulian Pullen continue; 8314d61c878SJulian Pullen } 8324d61c878SJulian Pullen 8334d61c878SJulian Pullen ldversion = LDAP_VERSION3; 8344d61c878SJulian Pullen (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 8354d61c878SJulian Pullen &ldversion); 8364d61c878SJulian Pullen 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 8474d61c878SJulian Pullen rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, 8484d61c878SJulian Pullen saslmech, NULL, NULL, saslflags, &saslcallback, 8494d61c878SJulian Pullen NULL /* defaults */); 8504d61c878SJulian Pullen if (rc == LDAP_SUCCESS) 8514d61c878SJulian Pullen break; 8524d61c878SJulian Pullen 8537a8a68f5SJulian Pullen logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", 8544d61c878SJulian Pullen ds[i].host, ds[i].port, ldap_err2string(rc)); 8554d61c878SJulian Pullen (void) ldap_unbind(ld); 8564d61c878SJulian Pullen ld = NULL; 8574d61c878SJulian Pullen } 8584d61c878SJulian Pullen return (ld); 8594d61c878SJulian Pullen } 8604d61c878SJulian Pullen 861c8e26105Sjp 8627a8a68f5SJulian Pullen 863c8e26105Sjp /* 864c8e26105Sjp * A utility function to get the value of some attribute of one of one 865c8e26105Sjp * or more AD LDAP objects named by the dn_list; first found one wins. 866c8e26105Sjp */ 867c8e26105Sjp static char * 868479ac375Sdm ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, 869c8e26105Sjp char **dn_list, char *attr) 870c8e26105Sjp { 871c8e26105Sjp int i; 8724d61c878SJulian Pullen int rc; 873c8e26105Sjp int scope = LDAP_SCOPE_BASE; 874c8e26105Sjp char *attrs[2]; 875c8e26105Sjp LDAPMessage *results = NULL; 876c8e26105Sjp LDAPMessage *entry; 877c8e26105Sjp char **values = NULL; 878c8e26105Sjp char *val = NULL; 879c8e26105Sjp 880c8e26105Sjp attrs[0] = attr; 881c8e26105Sjp attrs[1] = NULL; 882c8e26105Sjp 8834d61c878SJulian Pullen if (*ld == NULL) 8844d61c878SJulian Pullen *ld = ldap_lookup_init(domainControllers); 885c8e26105Sjp 8864d61c878SJulian Pullen if (*ld == NULL) 887c8e26105Sjp return (NULL); 888c8e26105Sjp 889c8e26105Sjp for (i = 0; dn_list[i] != NULL; i++) { 890c8e26105Sjp rc = ldap_search_s(*ld, dn_list[i], scope, 891c8e26105Sjp "(objectclass=*)", attrs, 0, &results); 892c8e26105Sjp if (rc == LDAP_SUCCESS) { 893c8e26105Sjp for (entry = ldap_first_entry(*ld, results); 894c8e26105Sjp entry != NULL && values == NULL; 895c8e26105Sjp entry = ldap_next_entry(*ld, entry)) { 896c8e26105Sjp values = ldap_get_values( 897c8e26105Sjp *ld, entry, attr); 898c8e26105Sjp } 899c8e26105Sjp 900c8e26105Sjp if (values != NULL) { 901c8e26105Sjp (void) ldap_msgfree(results); 902c8e26105Sjp val = strdup(values[0]); 903c8e26105Sjp ldap_value_free(values); 904c8e26105Sjp return (val); 905c8e26105Sjp } 906c8e26105Sjp } 907c8e26105Sjp if (results != NULL) { 908c8e26105Sjp (void) ldap_msgfree(results); 909c8e26105Sjp results = NULL; 910c8e26105Sjp } 911c8e26105Sjp } 912c8e26105Sjp 913c8e26105Sjp return (NULL); 914c8e26105Sjp } 915c8e26105Sjp 916c8e26105Sjp 9174d61c878SJulian Pullen /* 9184d61c878SJulian Pullen * Lookup the trusted domains in the global catalog. 9194d61c878SJulian Pullen * 9204d61c878SJulian Pullen * Returns: 9214d61c878SJulian Pullen * array of trusted domains which is terminated by 9224d61c878SJulian Pullen * an empty trusted domain. 9234d61c878SJulian Pullen * NULL an error occured 9244d61c878SJulian Pullen */ 9254d61c878SJulian Pullen ad_disc_trusteddomains_t * 9264d61c878SJulian Pullen ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, 9274d61c878SJulian Pullen char *base_dn) 9284d61c878SJulian Pullen { 9294d61c878SJulian Pullen int scope = LDAP_SCOPE_SUBTREE; 9304d61c878SJulian Pullen char *attrs[3]; 9314d61c878SJulian Pullen int rc; 9324d61c878SJulian Pullen LDAPMessage *results = NULL; 9334d61c878SJulian Pullen LDAPMessage *entry; 9344d61c878SJulian Pullen char *filter; 9354d61c878SJulian Pullen char **partner = NULL; 9364d61c878SJulian Pullen char **direction = NULL; 9374d61c878SJulian Pullen int num = 0; 9384d61c878SJulian Pullen ad_disc_trusteddomains_t *trusted_domains = NULL; 9394d61c878SJulian Pullen 9404d61c878SJulian Pullen 9414d61c878SJulian Pullen if (*ld == NULL) 9424d61c878SJulian Pullen *ld = ldap_lookup_init(globalCatalog); 9434d61c878SJulian Pullen 9444d61c878SJulian Pullen if (*ld == NULL) 9454d61c878SJulian Pullen return (NULL); 9464d61c878SJulian Pullen 9474d61c878SJulian Pullen attrs[0] = "trustPartner"; 9484d61c878SJulian Pullen attrs[1] = "trustDirection"; 9494d61c878SJulian Pullen attrs[2] = NULL; 9504d61c878SJulian Pullen 9514d61c878SJulian Pullen /* trustDirection values - inbound = 1 and bidirectional = 3 */ 9524d61c878SJulian Pullen filter = "(&(objectclass=trustedDomain)" 9534d61c878SJulian Pullen "(|(trustDirection=3)(trustDirection=1)))"; 9544d61c878SJulian Pullen 9554d61c878SJulian Pullen rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); 9564d61c878SJulian Pullen if (rc == LDAP_SUCCESS) { 9574d61c878SJulian Pullen for (entry = ldap_first_entry(*ld, results); 9584d61c878SJulian Pullen entry != NULL; entry = ldap_next_entry(*ld, entry)) { 9594d61c878SJulian Pullen partner = ldap_get_values(*ld, entry, "trustPartner"); 9604d61c878SJulian Pullen direction = ldap_get_values( 9614d61c878SJulian Pullen *ld, entry, "trustDirection"); 9624d61c878SJulian Pullen 9634d61c878SJulian Pullen if (partner != NULL && direction != NULL) { 9644d61c878SJulian Pullen num++; 9654d61c878SJulian Pullen trusted_domains = realloc(trusted_domains, 9664d61c878SJulian Pullen (num + 1) * 9674d61c878SJulian Pullen sizeof (ad_disc_trusteddomains_t)); 9684d61c878SJulian Pullen if (trusted_domains == NULL) { 9694d61c878SJulian Pullen ldap_value_free(partner); 9704d61c878SJulian Pullen ldap_value_free(direction); 9714d61c878SJulian Pullen ldap_msgfree(results); 9724d61c878SJulian Pullen return (NULL); 9734d61c878SJulian Pullen } 9744d61c878SJulian Pullen /* Last element should be zero */ 9754d61c878SJulian Pullen memset(&trusted_domains[num], 0, 9764d61c878SJulian Pullen sizeof (ad_disc_trusteddomains_t)); 9774d61c878SJulian Pullen strcpy(trusted_domains[num - 1].domain, 9784d61c878SJulian Pullen partner[0]); 9794d61c878SJulian Pullen trusted_domains[num - 1].direction = 9804d61c878SJulian Pullen atoi(direction[0]); 9814d61c878SJulian Pullen } 9824d61c878SJulian Pullen if (partner != NULL) 9834d61c878SJulian Pullen ldap_value_free(partner); 9844d61c878SJulian Pullen if (direction != NULL) 9854d61c878SJulian Pullen ldap_value_free(direction); 9864d61c878SJulian Pullen } 9874d61c878SJulian Pullen } else if (rc == LDAP_NO_RESULTS_RETURNED) { 9884d61c878SJulian Pullen /* This is not an error - return empty trusted domain */ 9894d61c878SJulian Pullen trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); 9904d61c878SJulian Pullen } 9914d61c878SJulian Pullen if (results != NULL) 9924d61c878SJulian Pullen ldap_msgfree(results); 9934d61c878SJulian Pullen 9944d61c878SJulian Pullen return (trusted_domains); 9954d61c878SJulian Pullen } 9964d61c878SJulian Pullen 9974d61c878SJulian Pullen 9984d61c878SJulian Pullen /* 9994d61c878SJulian Pullen * This functions finds all the domains in a forest. 10004d61c878SJulian Pullen */ 10014d61c878SJulian Pullen ad_disc_domainsinforest_t * 10024d61c878SJulian Pullen ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) 10034d61c878SJulian Pullen { 1004928e1f97SJordan Brown static char *attrs[] = { 1005928e1f97SJordan Brown "objectSid", 1006928e1f97SJordan Brown NULL, 1007928e1f97SJordan Brown }; 10084d61c878SJulian Pullen int rc; 10094d61c878SJulian Pullen LDAPMessage *result = NULL; 10104d61c878SJulian Pullen LDAPMessage *entry; 1011928e1f97SJordan Brown int ndomains = 0; 1012928e1f97SJordan Brown int nresults; 10134d61c878SJulian Pullen ad_disc_domainsinforest_t *domains = NULL; 10144d61c878SJulian Pullen 10154d61c878SJulian Pullen if (*ld == NULL) 10164d61c878SJulian Pullen *ld = ldap_lookup_init(globalCatalogs); 10174d61c878SJulian Pullen 10187a8a68f5SJulian Pullen if (*ld == NULL) 10194d61c878SJulian Pullen return (NULL); 10204d61c878SJulian Pullen 1021928e1f97SJordan Brown logger(LOG_DEBUG, "Looking for domains in forest..."); 1022928e1f97SJordan Brown /* Find domains */ 1023928e1f97SJordan Brown rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, 1024928e1f97SJordan Brown "(objectClass=Domain)", attrs, 0, &result); 1025928e1f97SJordan Brown if (rc != LDAP_SUCCESS) 1026928e1f97SJordan Brown goto err; 1027928e1f97SJordan Brown 1028928e1f97SJordan Brown nresults = ldap_count_entries(*ld, result); 1029928e1f97SJordan Brown domains = calloc(nresults + 1, sizeof (*domains)); 1030928e1f97SJordan Brown if (domains == NULL) 1031928e1f97SJordan Brown goto err; 1032928e1f97SJordan Brown 1033928e1f97SJordan Brown for (entry = ldap_first_entry(*ld, result); 1034928e1f97SJordan Brown entry != NULL; 1035928e1f97SJordan Brown entry = ldap_next_entry(*ld, entry)) { 1036928e1f97SJordan Brown struct berval **sid_ber; 1037928e1f97SJordan Brown adutils_sid_t sid; 1038928e1f97SJordan Brown char *sid_str; 1039928e1f97SJordan Brown char *name; 1040928e1f97SJordan Brown 1041928e1f97SJordan Brown sid_ber = ldap_get_values_len(*ld, entry, 1042928e1f97SJordan Brown "objectSid"); 1043928e1f97SJordan Brown if (sid_ber == NULL) 1044928e1f97SJordan Brown continue; 10454d61c878SJulian Pullen 1046928e1f97SJordan Brown rc = adutils_getsid(sid_ber[0], &sid); 1047928e1f97SJordan Brown ldap_value_free_len(sid_ber); 1048928e1f97SJordan Brown if (rc < 0) 1049928e1f97SJordan Brown goto err; 10504d61c878SJulian Pullen 1051928e1f97SJordan Brown if ((sid_str = adutils_sid2txt(&sid)) == NULL) 1052928e1f97SJordan Brown goto err; 10534d61c878SJulian Pullen 1054928e1f97SJordan Brown strcpy(domains[ndomains].sid, sid_str); 1055928e1f97SJordan Brown free(sid_str); 1056928e1f97SJordan Brown 1057928e1f97SJordan Brown name = DN_to_DNS(ldap_get_dn(*ld, entry)); 1058928e1f97SJordan Brown if (name == NULL) 1059928e1f97SJordan Brown goto err; 1060928e1f97SJordan Brown 1061928e1f97SJordan Brown strcpy(domains[ndomains].domain, name); 1062928e1f97SJordan Brown free(name); 1063928e1f97SJordan Brown 1064928e1f97SJordan Brown logger(LOG_DEBUG, " found %s", domains[ndomains].domain); 1065928e1f97SJordan Brown 1066928e1f97SJordan Brown ndomains++; 10674d61c878SJulian Pullen } 10684d61c878SJulian Pullen 1069928e1f97SJordan Brown if (ndomains == 0) 1070928e1f97SJordan Brown goto err; 1071928e1f97SJordan Brown 1072928e1f97SJordan Brown if (ndomains < nresults) { 1073928e1f97SJordan Brown ad_disc_domainsinforest_t *tmp; 1074*1fcced4cSJordan Brown tmp = realloc(domains, (ndomains + 1) * sizeof (*domains)); 1075928e1f97SJordan Brown if (tmp == NULL) 1076928e1f97SJordan Brown goto err; 1077928e1f97SJordan Brown domains = tmp; 10784d61c878SJulian Pullen } 1079928e1f97SJordan Brown 1080928e1f97SJordan Brown if (result != NULL) 1081928e1f97SJordan Brown ldap_msgfree(result); 10824d61c878SJulian Pullen 10834d61c878SJulian Pullen return (domains); 1084928e1f97SJordan Brown 1085928e1f97SJordan Brown err: 1086928e1f97SJordan Brown free(domains); 1087928e1f97SJordan Brown if (result != NULL) 1088928e1f97SJordan Brown ldap_msgfree(result); 1089928e1f97SJordan Brown return (NULL); 10904d61c878SJulian Pullen } 10914d61c878SJulian Pullen 1092c8e26105Sjp 1093c8e26105Sjp ad_disc_t 1094c8e26105Sjp ad_disc_init(void) 1095c8e26105Sjp { 1096c8e26105Sjp struct ad_disc *ctx; 1097c8e26105Sjp ctx = calloc(1, sizeof (struct ad_disc)); 10980dcc7149Snw if (ctx != NULL) 10990dcc7149Snw DO_RES_NINIT(ctx); 11004d61c878SJulian Pullen 11014d61c878SJulian Pullen ctx->domain_name.type = AD_STRING; 11024d61c878SJulian Pullen ctx->domain_controller.type = AD_DIRECTORY; 11034d61c878SJulian Pullen ctx->site_name.type = AD_STRING; 11044d61c878SJulian Pullen ctx->forest_name.type = AD_STRING; 11054d61c878SJulian Pullen ctx->global_catalog.type = AD_DIRECTORY; 11064d61c878SJulian Pullen ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; 11074d61c878SJulian Pullen ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; 11084d61c878SJulian Pullen /* Site specific versions */ 11094d61c878SJulian Pullen ctx->site_domain_controller.type = AD_DIRECTORY; 11104d61c878SJulian Pullen ctx->site_global_catalog.type = AD_DIRECTORY; 1111c8e26105Sjp return (ctx); 1112c8e26105Sjp } 1113c8e26105Sjp 1114c8e26105Sjp 1115c8e26105Sjp void 1116c8e26105Sjp ad_disc_fini(ad_disc_t ctx) 1117c8e26105Sjp { 1118cd37da74Snw if (ctx == NULL) 1119cd37da74Snw return; 1120cd37da74Snw 11210dcc7149Snw if (ctx->res_ninitted) 11224d61c878SJulian Pullen res_ndestroy(&ctx->res_state); 1123c8e26105Sjp 1124c8e26105Sjp if (ctx->subnets != NULL) 1125c8e26105Sjp free(ctx->subnets); 1126c8e26105Sjp 11274d61c878SJulian Pullen if (ctx->domain_name.value != NULL) 11284d61c878SJulian Pullen free(ctx->domain_name.value); 11294d61c878SJulian Pullen 11304d61c878SJulian Pullen if (ctx->domain_controller.value != NULL) 11314d61c878SJulian Pullen free(ctx->domain_controller.value); 11324d61c878SJulian Pullen 11334d61c878SJulian Pullen if (ctx->site_name.value != NULL) 11344d61c878SJulian Pullen free(ctx->site_name.value); 1135c8e26105Sjp 11364d61c878SJulian Pullen if (ctx->forest_name.value != NULL) 11374d61c878SJulian Pullen free(ctx->forest_name.value); 1138c8e26105Sjp 11394d61c878SJulian Pullen if (ctx->global_catalog.value != NULL) 11404d61c878SJulian Pullen free(ctx->global_catalog.value); 1141c8e26105Sjp 11424d61c878SJulian Pullen if (ctx->domains_in_forest.value != NULL) 11434d61c878SJulian Pullen free(ctx->domains_in_forest.value); 1144c8e26105Sjp 11454d61c878SJulian Pullen if (ctx->trusted_domains.value != NULL) 11464d61c878SJulian Pullen free(ctx->trusted_domains.value); 1147c8e26105Sjp 11484d61c878SJulian Pullen /* Site specific versions */ 11494d61c878SJulian Pullen if (ctx->site_domain_controller.value != NULL) 11504d61c878SJulian Pullen free(ctx->site_domain_controller.value); 1151c8e26105Sjp 11524d61c878SJulian Pullen if (ctx->site_global_catalog.value != NULL) 11534d61c878SJulian Pullen free(ctx->site_global_catalog.value); 1154c8e26105Sjp 1155c8e26105Sjp free(ctx); 1156c8e26105Sjp } 1157c8e26105Sjp 1158c8e26105Sjp void 1159c8e26105Sjp ad_disc_refresh(ad_disc_t ctx) 1160c8e26105Sjp { 11610dcc7149Snw if (ctx->res_ninitted) 11624d61c878SJulian Pullen res_ndestroy(&ctx->res_state); 11634d61c878SJulian Pullen (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); 11644d61c878SJulian Pullen ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; 1165c8e26105Sjp 11664d61c878SJulian Pullen if (ctx->domain_name.state == AD_STATE_AUTO) 11674d61c878SJulian Pullen ctx->domain_name.state = AD_STATE_INVALID; 1168c8e26105Sjp 11694d61c878SJulian Pullen if (ctx->domain_controller.state == AD_STATE_AUTO) 11704d61c878SJulian Pullen ctx->domain_controller.state = AD_STATE_INVALID; 1171c8e26105Sjp 11724d61c878SJulian Pullen if (ctx->site_name.state == AD_STATE_AUTO) 11734d61c878SJulian Pullen ctx->site_name.state = AD_STATE_INVALID; 1174c8e26105Sjp 11754d61c878SJulian Pullen if (ctx->forest_name.state == AD_STATE_AUTO) 11764d61c878SJulian Pullen ctx->forest_name.state = AD_STATE_INVALID; 1177c8e26105Sjp 11784d61c878SJulian Pullen if (ctx->global_catalog.state == AD_STATE_AUTO) 11794d61c878SJulian Pullen ctx->global_catalog.state = AD_STATE_INVALID; 1180c8e26105Sjp 11814d61c878SJulian Pullen if (ctx->domains_in_forest.state == AD_STATE_AUTO) 11824d61c878SJulian Pullen ctx->domains_in_forest.state = AD_STATE_INVALID; 1183c8e26105Sjp 11844d61c878SJulian Pullen if (ctx->trusted_domains.state == AD_STATE_AUTO) 11854d61c878SJulian Pullen ctx->trusted_domains.state = AD_STATE_INVALID; 11864d61c878SJulian Pullen 11874d61c878SJulian Pullen if (ctx->site_domain_controller.state == AD_STATE_AUTO) 11884d61c878SJulian Pullen ctx->site_domain_controller.state = AD_STATE_INVALID; 11894d61c878SJulian Pullen 11904d61c878SJulian Pullen if (ctx->site_global_catalog.state == AD_STATE_AUTO) 11914d61c878SJulian Pullen ctx->site_global_catalog.state = AD_STATE_INVALID; 1192c8e26105Sjp } 1193c8e26105Sjp 1194c8e26105Sjp 1195c8e26105Sjp 11960dcc7149Snw /* Discover joined Active Directory domainName */ 11974d61c878SJulian Pullen static ad_item_t * 1198c8e26105Sjp validate_DomainName(ad_disc_t ctx) 1199c8e26105Sjp { 1200479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 1201c8e26105Sjp char *dname, *srvname; 1202c8e26105Sjp uint32_t ttl = 0; 1203928e1f97SJordan Brown int len; 1204c8e26105Sjp 12054d61c878SJulian Pullen if (is_valid(&ctx->domain_name)) 12064d61c878SJulian Pullen return (&ctx->domain_name); 1207c8e26105Sjp 1208c8e26105Sjp 1209c8e26105Sjp /* Try to find our domain by searching for DCs for it */ 12100dcc7149Snw DO_RES_NINIT(ctx); 12114d61c878SJulian Pullen domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD 12124d61c878SJulian Pullen DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); 1213c8e26105Sjp 1214c8e26105Sjp /* 1215c8e26105Sjp * If we can't find DCs by via res_nsearch() then there's no 1216c8e26105Sjp * point in trying anything else to discover the AD domain name. 1217c8e26105Sjp */ 1218c8e26105Sjp if (domain_controller == NULL) 12194d61c878SJulian Pullen return (NULL); 1220c8e26105Sjp 1221c8e26105Sjp free(domain_controller); 1222c8e26105Sjp /* 1223c8e26105Sjp * We have the FQDN of the SRV RR name, so now we extract the 1224c8e26105Sjp * domainname suffix from it. 1225c8e26105Sjp */ 1226c8e26105Sjp dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) + 1227c8e26105Sjp 1 /* for the dot between RR name and domainname */); 1228c8e26105Sjp 1229c8e26105Sjp free(srvname); 1230c8e26105Sjp 1231c8e26105Sjp if (dname == NULL) { 12327a8a68f5SJulian Pullen logger(LOG_ERR, "Out of memory"); 12334d61c878SJulian Pullen return (NULL); 1234c8e26105Sjp } 1235c8e26105Sjp 1236c8e26105Sjp /* Eat any trailing dot */ 1237928e1f97SJordan Brown len = strlen(dname); 1238*1fcced4cSJordan Brown if (len > 0 && dname[len - 1] == '.') 1239*1fcced4cSJordan Brown dname[len - 1] = '\0'; 1240c8e26105Sjp 12414d61c878SJulian Pullen update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); 12424d61c878SJulian Pullen 12434d61c878SJulian Pullen return (&ctx->domain_name); 1244c8e26105Sjp } 1245c8e26105Sjp 1246c8e26105Sjp 1247c8e26105Sjp char * 12487a8a68f5SJulian Pullen ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) 1249c8e26105Sjp { 1250c8e26105Sjp char *domain_name = NULL; 12514d61c878SJulian Pullen ad_item_t *domain_name_item; 1252c8e26105Sjp 12534d61c878SJulian Pullen domain_name_item = validate_DomainName(ctx); 12544d61c878SJulian Pullen 12554d61c878SJulian Pullen if (domain_name_item) { 12564d61c878SJulian Pullen domain_name = strdup(domain_name_item->value); 12574d61c878SJulian Pullen if (auto_discovered != NULL) 12584d61c878SJulian Pullen *auto_discovered = 12594d61c878SJulian Pullen (domain_name_item->state == AD_STATE_AUTO); 12604d61c878SJulian Pullen } else if (auto_discovered != NULL) 12617a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 1262c8e26105Sjp 1263c8e26105Sjp return (domain_name); 1264c8e26105Sjp } 1265c8e26105Sjp 1266c8e26105Sjp 12670dcc7149Snw /* Discover domain controllers */ 12684d61c878SJulian Pullen static ad_item_t * 1269c8e26105Sjp validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) 1270c8e26105Sjp { 1271c8e26105Sjp uint32_t ttl = 0; 1272479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 12737a8a68f5SJulian Pullen boolean_t validate_global = B_FALSE; 12747a8a68f5SJulian Pullen boolean_t validate_site = B_FALSE; 12754d61c878SJulian Pullen ad_item_t *domain_name_item; 12764d61c878SJulian Pullen ad_item_t *site_name_item = NULL; 1277c8e26105Sjp 12784d61c878SJulian Pullen /* If the values is fixed there will not be a site specific version */ 1279c8e26105Sjp if (is_fixed(&ctx->domain_controller)) 12804d61c878SJulian Pullen return (&ctx->domain_controller); 12814d61c878SJulian Pullen 12824d61c878SJulian Pullen domain_name_item = validate_DomainName(ctx); 12834d61c878SJulian Pullen if (domain_name_item == NULL) 12844d61c878SJulian Pullen return (NULL); 1285c8e26105Sjp 1286c8e26105Sjp if (req == AD_DISC_GLOBAL) 12877a8a68f5SJulian Pullen validate_global = B_TRUE; 1288c8e26105Sjp else { 12894d61c878SJulian Pullen site_name_item = validate_SiteName(ctx); 12904d61c878SJulian Pullen if (site_name_item != NULL) 12917a8a68f5SJulian Pullen validate_site = B_TRUE; 12924d61c878SJulian Pullen else if (req == AD_DISC_PREFER_SITE) 12937a8a68f5SJulian Pullen validate_global = B_TRUE; 1294c8e26105Sjp } 1295c8e26105Sjp 12964d61c878SJulian Pullen if (validate_global) { 12974d61c878SJulian Pullen if (!is_valid(&ctx->domain_controller) || 12984d61c878SJulian Pullen is_changed(&ctx->domain_controller, PARAM1, 12994d61c878SJulian Pullen domain_name_item)) { 1300c8e26105Sjp /* 13010dcc7149Snw * Lookup DNS SRV RR named 13020dcc7149Snw * _ldap._tcp.dc._msdcs.<DomainName> 1303c8e26105Sjp */ 13040dcc7149Snw DO_RES_NINIT(ctx); 13054d61c878SJulian Pullen domain_controller = srv_query(&ctx->res_state, 1306c8e26105Sjp LDAP_SRV_HEAD DC_SRV_TAIL, 13074d61c878SJulian Pullen domain_name_item->value, NULL, &ttl); 1308c8e26105Sjp 13094d61c878SJulian Pullen if (domain_controller == NULL) 13104d61c878SJulian Pullen return (NULL); 1311c8e26105Sjp 13124d61c878SJulian Pullen update_item(&ctx->domain_controller, domain_controller, 13134d61c878SJulian Pullen AD_STATE_AUTO, ttl); 13144d61c878SJulian Pullen update_version(&ctx->domain_controller, PARAM1, 13154d61c878SJulian Pullen domain_name_item); 13164d61c878SJulian Pullen } 13174d61c878SJulian Pullen return (&ctx->domain_controller); 13184d61c878SJulian Pullen } 1319c8e26105Sjp 13204d61c878SJulian Pullen if (validate_site) { 13214d61c878SJulian Pullen if (!is_valid(&ctx->site_domain_controller) || 13224d61c878SJulian Pullen is_changed(&ctx->site_domain_controller, PARAM1, 13234d61c878SJulian Pullen domain_name_item) || 13244d61c878SJulian Pullen is_changed(&ctx->site_domain_controller, PARAM2, 13254d61c878SJulian Pullen site_name_item)) { 1326c8e26105Sjp char rr_name[DNS_MAX_NAME]; 1327c8e26105Sjp /* 13280dcc7149Snw * Lookup DNS SRV RR named 13290dcc7149Snw * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> 1330c8e26105Sjp */ 1331c8e26105Sjp (void) snprintf(rr_name, sizeof (rr_name), 1332c8e26105Sjp LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, 13334d61c878SJulian Pullen site_name_item->value); 13340dcc7149Snw DO_RES_NINIT(ctx); 13354d61c878SJulian Pullen domain_controller = srv_query(&ctx->res_state, rr_name, 13364d61c878SJulian Pullen domain_name_item->value, NULL, &ttl); 13374d61c878SJulian Pullen if (domain_controller == NULL) 13384d61c878SJulian Pullen return (NULL); 13394d61c878SJulian Pullen 13404d61c878SJulian Pullen update_item(&ctx->site_domain_controller, 13414d61c878SJulian Pullen domain_controller, AD_STATE_AUTO, ttl); 13424d61c878SJulian Pullen update_version(&ctx->site_domain_controller, PARAM1, 13434d61c878SJulian Pullen domain_name_item); 13444d61c878SJulian Pullen update_version(&ctx->site_domain_controller, PARAM2, 13454d61c878SJulian Pullen site_name_item); 1346c8e26105Sjp } 13474d61c878SJulian Pullen return (&ctx->site_domain_controller); 1348c8e26105Sjp } 13494d61c878SJulian Pullen return (NULL); 1350c8e26105Sjp } 1351c8e26105Sjp 1352479ac375Sdm idmap_ad_disc_ds_t * 13534d61c878SJulian Pullen ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, 13547a8a68f5SJulian Pullen boolean_t *auto_discovered) 1355c8e26105Sjp { 13564d61c878SJulian Pullen ad_item_t *domain_controller_item; 1357479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 1358c8e26105Sjp 13594d61c878SJulian Pullen domain_controller_item = validate_DomainController(ctx, req); 13604d61c878SJulian Pullen 13614d61c878SJulian Pullen if (domain_controller_item != NULL) { 13624d61c878SJulian Pullen domain_controller = ds_dup(domain_controller_item->value); 13634d61c878SJulian Pullen if (auto_discovered != NULL) 13644d61c878SJulian Pullen *auto_discovered = 13654d61c878SJulian Pullen (domain_controller_item->state == AD_STATE_AUTO); 13664d61c878SJulian Pullen } else if (auto_discovered != NULL) 13677a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 13684d61c878SJulian Pullen 1369c8e26105Sjp return (domain_controller); 1370c8e26105Sjp } 1371c8e26105Sjp 1372c8e26105Sjp 13730dcc7149Snw /* Discover site name (for multi-homed systems the first one found wins) */ 13744d61c878SJulian Pullen static ad_item_t * 1375c8e26105Sjp validate_SiteName(ad_disc_t ctx) 1376c8e26105Sjp { 1377c8e26105Sjp LDAP *ld = NULL; 1378c8e26105Sjp ad_subnet_t *subnets = NULL; 1379c8e26105Sjp char **dn_subnets = NULL; 1380c8e26105Sjp char *dn_root[2]; 1381c8e26105Sjp char *config_naming_context = NULL; 1382c8e26105Sjp char *site_object = NULL; 1383c8e26105Sjp char *site_name = NULL; 1384c8e26105Sjp char *forest_name; 1385c8e26105Sjp int len; 1386c8e26105Sjp int i; 13877a8a68f5SJulian Pullen boolean_t update_required = B_FALSE; 13884d61c878SJulian Pullen ad_item_t *domain_controller_item; 1389c8e26105Sjp 1390c8e26105Sjp if (is_fixed(&ctx->site_name)) 13914d61c878SJulian Pullen return (&ctx->site_name); 1392c8e26105Sjp 13930dcc7149Snw /* Can't rely on site-specific DCs */ 13944d61c878SJulian Pullen domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 13954d61c878SJulian Pullen if (domain_controller_item == NULL) 13964d61c878SJulian Pullen return (NULL); 1397c8e26105Sjp 13984d61c878SJulian Pullen if (!is_valid(&ctx->site_name) || 1399c8e26105Sjp is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) || 1400c8e26105Sjp ctx->subnets == NULL || ctx->subnets_changed) { 1401c8e26105Sjp subnets = find_subnets(); 1402c8e26105Sjp ctx->subnets_last_check = time(NULL); 14037a8a68f5SJulian Pullen update_required = B_TRUE; 1404c8e26105Sjp } else if (ctx->subnets_last_check + 60 < time(NULL)) { 1405c8e26105Sjp subnets = find_subnets(); 1406c8e26105Sjp ctx->subnets_last_check = time(NULL); 1407c8e26105Sjp if (cmpsubnets(ctx->subnets, subnets) != 0) 14087a8a68f5SJulian Pullen update_required = B_TRUE; 1409c8e26105Sjp } 1410c8e26105Sjp 14110dcc7149Snw if (!update_required) { 14120dcc7149Snw free(subnets); 14134d61c878SJulian Pullen return (&ctx->site_name); 14140dcc7149Snw } 1415c8e26105Sjp 14164d61c878SJulian Pullen if (subnets == NULL) 14174d61c878SJulian Pullen return (NULL); 14180dcc7149Snw 14194d61c878SJulian Pullen dn_root[0] = ""; 14204d61c878SJulian Pullen dn_root[1] = NULL; 14210dcc7149Snw 14224d61c878SJulian Pullen config_naming_context = ldap_lookup_entry_attr( 14234d61c878SJulian Pullen &ld, ctx->domain_controller.value, 14244d61c878SJulian Pullen dn_root, "configurationNamingContext"); 14254d61c878SJulian Pullen if (config_naming_context == NULL) 14264d61c878SJulian Pullen goto out; 14274d61c878SJulian Pullen /* 14284d61c878SJulian Pullen * configurationNamingContext also provides the Forest 14294d61c878SJulian Pullen * Name. 14304d61c878SJulian Pullen */ 14314d61c878SJulian Pullen if (!is_fixed(&ctx->forest_name)) { 14320dcc7149Snw /* 14334d61c878SJulian Pullen * The configurationNamingContext should be of 14344d61c878SJulian Pullen * form: 14354d61c878SJulian Pullen * CN=Configuration,<DNforestName> 14364d61c878SJulian Pullen * Remove the first part and convert to DNS form 14374d61c878SJulian Pullen * (replace ",DC=" with ".") 14380dcc7149Snw */ 14394d61c878SJulian Pullen char *str = "CN=Configuration,"; 14404d61c878SJulian Pullen int len = strlen(str); 14414d61c878SJulian Pullen if (strncasecmp(config_naming_context, str, len) == 0) { 14424d61c878SJulian Pullen forest_name = DN_to_DNS(config_naming_context + len); 14434d61c878SJulian Pullen update_item(&ctx->forest_name, forest_name, 14444d61c878SJulian Pullen AD_STATE_AUTO, 0); 14450dcc7149Snw } 14464d61c878SJulian Pullen } 14474d61c878SJulian Pullen 14484d61c878SJulian Pullen dn_subnets = subnets_to_DNs(subnets, config_naming_context); 14494d61c878SJulian Pullen if (dn_subnets == NULL) 14504d61c878SJulian Pullen goto out; 14514d61c878SJulian Pullen 14524d61c878SJulian Pullen site_object = ldap_lookup_entry_attr( 14534d61c878SJulian Pullen &ld, domain_controller_item->value, 14544d61c878SJulian Pullen dn_subnets, "siteobject"); 14554d61c878SJulian Pullen if (site_object != NULL) { 14564d61c878SJulian Pullen /* 14574d61c878SJulian Pullen * The site object should be of the form 14584d61c878SJulian Pullen * CN=<site>,CN=Sites,CN=Configuration, 14594d61c878SJulian Pullen * <DN Domain> 14604d61c878SJulian Pullen */ 14614d61c878SJulian Pullen if (strncasecmp(site_object, "CN=", 3) == 0) { 14624d61c878SJulian Pullen for (len = 0; site_object[len + 3] != ','; len++) 14630dcc7149Snw ; 14644d61c878SJulian Pullen site_name = malloc(len + 1); 14654d61c878SJulian Pullen (void) strncpy(site_name, &site_object[3], len); 14664d61c878SJulian Pullen site_name[len] = '\0'; 14674d61c878SJulian Pullen update_item(&ctx->site_name, site_name, 14684d61c878SJulian Pullen AD_STATE_AUTO, 0); 14690dcc7149Snw } 14704d61c878SJulian Pullen } 1471c8e26105Sjp 14724d61c878SJulian Pullen if (ctx->subnets != NULL) { 14734d61c878SJulian Pullen free(ctx->subnets); 14744d61c878SJulian Pullen ctx->subnets = NULL; 14750dcc7149Snw } 14764d61c878SJulian Pullen ctx->subnets = subnets; 14774d61c878SJulian Pullen subnets = NULL; 14787a8a68f5SJulian Pullen ctx->subnets_changed = B_FALSE; 14794d61c878SJulian Pullen 1480c8e26105Sjp out: 14810dcc7149Snw if (ld != NULL) 14820dcc7149Snw (void) ldap_unbind(ld); 1483c8e26105Sjp 14840dcc7149Snw if (dn_subnets != NULL) { 14850dcc7149Snw for (i = 0; dn_subnets[i] != NULL; i++) 14860dcc7149Snw free(dn_subnets[i]); 14870dcc7149Snw free(dn_subnets); 14880dcc7149Snw } 14890dcc7149Snw if (config_naming_context != NULL) 14900dcc7149Snw free(config_naming_context); 14910dcc7149Snw if (site_object != NULL) 14920dcc7149Snw free(site_object); 14930dcc7149Snw 14940dcc7149Snw free(subnets); 14954d61c878SJulian Pullen if (site_name == NULL) 14964d61c878SJulian Pullen return (NULL); 14974d61c878SJulian Pullen return (&ctx->site_name); 1498c8e26105Sjp 1499c8e26105Sjp } 1500c8e26105Sjp 1501c8e26105Sjp 1502c8e26105Sjp char * 15037a8a68f5SJulian Pullen ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) 1504c8e26105Sjp { 15054d61c878SJulian Pullen ad_item_t *site_name_item; 1506c8e26105Sjp char *site_name = NULL; 1507c8e26105Sjp 15084d61c878SJulian Pullen site_name_item = validate_SiteName(ctx); 15094d61c878SJulian Pullen if (site_name_item != NULL) { 15104d61c878SJulian Pullen site_name = strdup(site_name_item->value); 15114d61c878SJulian Pullen if (auto_discovered != NULL) 15124d61c878SJulian Pullen *auto_discovered = 15134d61c878SJulian Pullen (site_name_item->state == AD_STATE_AUTO); 15144d61c878SJulian Pullen } else if (auto_discovered != NULL) 15157a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 15164d61c878SJulian Pullen 1517c8e26105Sjp return (site_name); 1518c8e26105Sjp } 1519c8e26105Sjp 1520c8e26105Sjp 1521c8e26105Sjp 15220dcc7149Snw /* Discover forest name */ 15234d61c878SJulian Pullen static ad_item_t * 1524c8e26105Sjp validate_ForestName(ad_disc_t ctx) 1525c8e26105Sjp { 1526c8e26105Sjp LDAP *ld = NULL; 1527c8e26105Sjp char *config_naming_context; 1528c8e26105Sjp char *forest_name = NULL; 1529c8e26105Sjp char *dn_list[2]; 15304d61c878SJulian Pullen ad_item_t *domain_controller_item; 1531c8e26105Sjp 1532c8e26105Sjp if (is_fixed(&ctx->forest_name)) 15334d61c878SJulian Pullen return (&ctx->forest_name); 1534c8e26105Sjp /* 15350dcc7149Snw * We may not have a site name yet, so we won't rely on 15360dcc7149Snw * site-specific DCs. (But maybe we could replace 15370dcc7149Snw * validate_ForestName() with validate_siteName()?) 1538c8e26105Sjp */ 15394d61c878SJulian Pullen domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 15404d61c878SJulian Pullen if (domain_controller_item == NULL) 15414d61c878SJulian Pullen return (NULL); 1542c8e26105Sjp 15434d61c878SJulian Pullen if (!is_valid(&ctx->forest_name) || 15444d61c878SJulian Pullen is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { 15454d61c878SJulian Pullen 15464d61c878SJulian Pullen dn_list[0] = ""; 15474d61c878SJulian Pullen dn_list[1] = NULL; 15484d61c878SJulian Pullen config_naming_context = ldap_lookup_entry_attr( 15494d61c878SJulian Pullen &ld, ctx->domain_controller.value, 15504d61c878SJulian Pullen dn_list, "configurationNamingContext"); 15514d61c878SJulian Pullen if (config_naming_context != NULL) { 15524d61c878SJulian Pullen /* 15534d61c878SJulian Pullen * The configurationNamingContext should be of 15544d61c878SJulian Pullen * form: 15554d61c878SJulian Pullen * CN=Configuration,<DNforestName> 15564d61c878SJulian Pullen * Remove the first part and convert to DNS form 15574d61c878SJulian Pullen * (replace ",DC=" with ".") 15584d61c878SJulian Pullen */ 15594d61c878SJulian Pullen char *str = "CN=Configuration,"; 15604d61c878SJulian Pullen int len = strlen(str); 15614d61c878SJulian Pullen if (strncasecmp(config_naming_context, str, len) == 0) { 15624d61c878SJulian Pullen forest_name = DN_to_DNS( 15634d61c878SJulian Pullen config_naming_context + len); 1564c8e26105Sjp } 15654d61c878SJulian Pullen free(config_naming_context); 1566c8e26105Sjp } 15674d61c878SJulian Pullen if (ld != NULL) 15684d61c878SJulian Pullen (void) ldap_unbind(ld); 15694d61c878SJulian Pullen 15704d61c878SJulian Pullen if (forest_name == NULL) 15714d61c878SJulian Pullen return (NULL); 15724d61c878SJulian Pullen 15734d61c878SJulian Pullen update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); 15744d61c878SJulian Pullen update_version(&ctx->forest_name, PARAM1, 15754d61c878SJulian Pullen domain_controller_item); 1576c8e26105Sjp } 15774d61c878SJulian Pullen return (&ctx->forest_name); 1578c8e26105Sjp } 1579c8e26105Sjp 1580c8e26105Sjp 1581c8e26105Sjp char * 15827a8a68f5SJulian Pullen ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) 1583c8e26105Sjp { 15844d61c878SJulian Pullen ad_item_t *forest_name_item; 1585c8e26105Sjp char *forest_name = NULL; 1586c8e26105Sjp 15874d61c878SJulian Pullen forest_name_item = validate_ForestName(ctx); 15884d61c878SJulian Pullen 15894d61c878SJulian Pullen if (forest_name_item != NULL) { 15904d61c878SJulian Pullen forest_name = strdup(forest_name_item->value); 15914d61c878SJulian Pullen if (auto_discovered != NULL) 15924d61c878SJulian Pullen *auto_discovered = 15934d61c878SJulian Pullen (forest_name_item->state == AD_STATE_AUTO); 15944d61c878SJulian Pullen } else if (auto_discovered != NULL) 15957a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 1596c8e26105Sjp 1597c8e26105Sjp return (forest_name); 1598c8e26105Sjp } 1599c8e26105Sjp 1600c8e26105Sjp 16010dcc7149Snw /* Discover global catalog servers */ 16024d61c878SJulian Pullen static ad_item_t * 1603c8e26105Sjp validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) 1604c8e26105Sjp { 1605479ac375Sdm idmap_ad_disc_ds_t *global_catalog = NULL; 16060dcc7149Snw uint32_t ttl = 0; 16077a8a68f5SJulian Pullen boolean_t validate_global = B_FALSE; 16087a8a68f5SJulian Pullen boolean_t validate_site = B_FALSE; 16094d61c878SJulian Pullen ad_item_t *forest_name_item; 16104d61c878SJulian Pullen ad_item_t *site_name_item; 1611c8e26105Sjp 16124d61c878SJulian Pullen /* If the values is fixed there will not be a site specific version */ 1613c8e26105Sjp if (is_fixed(&ctx->global_catalog)) 16144d61c878SJulian Pullen return (&ctx->global_catalog); 16154d61c878SJulian Pullen 16164d61c878SJulian Pullen forest_name_item = validate_ForestName(ctx); 16174d61c878SJulian Pullen if (forest_name_item == NULL) 16184d61c878SJulian Pullen return (NULL); 1619c8e26105Sjp 1620c8e26105Sjp if (req == AD_DISC_GLOBAL) 16217a8a68f5SJulian Pullen validate_global = B_TRUE; 1622c8e26105Sjp else { 16234d61c878SJulian Pullen site_name_item = validate_SiteName(ctx); 16244d61c878SJulian Pullen if (site_name_item != NULL) 16257a8a68f5SJulian Pullen validate_site = B_TRUE; 16264d61c878SJulian Pullen else if (req == AD_DISC_PREFER_SITE) 16277a8a68f5SJulian Pullen validate_global = B_TRUE; 1628c8e26105Sjp } 1629c8e26105Sjp 16304d61c878SJulian Pullen if (validate_global) { 16314d61c878SJulian Pullen if (!is_valid(&ctx->global_catalog) || 16324d61c878SJulian Pullen is_changed(&ctx->global_catalog, PARAM1, 16334d61c878SJulian Pullen forest_name_item)) { 1634c8e26105Sjp /* 16350dcc7149Snw * Lookup DNS SRV RR named 16360dcc7149Snw * _ldap._tcp.gc._msdcs.<ForestName> 1637c8e26105Sjp */ 16380dcc7149Snw DO_RES_NINIT(ctx); 1639c8e26105Sjp global_catalog = 16404d61c878SJulian Pullen srv_query(&ctx->res_state, 16414d61c878SJulian Pullen LDAP_SRV_HEAD GC_SRV_TAIL, 16424d61c878SJulian Pullen ctx->forest_name.value, NULL, &ttl); 1643c8e26105Sjp 16444d61c878SJulian Pullen if (global_catalog == NULL) 16454d61c878SJulian Pullen return (NULL); 1646c8e26105Sjp 16474d61c878SJulian Pullen update_item(&ctx->global_catalog, global_catalog, 16484d61c878SJulian Pullen AD_STATE_AUTO, ttl); 16494d61c878SJulian Pullen update_version(&ctx->global_catalog, PARAM1, 16504d61c878SJulian Pullen forest_name_item); 16514d61c878SJulian Pullen } 16524d61c878SJulian Pullen return (&ctx->global_catalog); 16534d61c878SJulian Pullen } 1654c8e26105Sjp 16554d61c878SJulian Pullen if (validate_site) { 16564d61c878SJulian Pullen if (!is_valid(&ctx->site_global_catalog) || 16574d61c878SJulian Pullen is_changed(&ctx->site_global_catalog, PARAM1, 16584d61c878SJulian Pullen forest_name_item) || 16594d61c878SJulian Pullen is_changed(&ctx->site_global_catalog, PARAM2, 16604d61c878SJulian Pullen site_name_item)) { 1661c8e26105Sjp char rr_name[DNS_MAX_NAME]; 16624d61c878SJulian Pullen 1663c8e26105Sjp /* 16640dcc7149Snw * Lookup DNS SRV RR named: 16650dcc7149Snw * _ldap._tcp.<siteName>._sites.gc. 16660dcc7149Snw * _msdcs.<ForestName> 1667c8e26105Sjp */ 1668c8e26105Sjp (void) snprintf(rr_name, 1669c8e26105Sjp sizeof (rr_name), 1670c8e26105Sjp LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, 16714d61c878SJulian Pullen ctx->site_name.value); 16720dcc7149Snw DO_RES_NINIT(ctx); 16734d61c878SJulian Pullen global_catalog = srv_query(&ctx->res_state, rr_name, 16744d61c878SJulian Pullen ctx->forest_name.value, NULL, &ttl); 16754d61c878SJulian Pullen 16764d61c878SJulian Pullen if (global_catalog == NULL) 16774d61c878SJulian Pullen return (NULL); 16784d61c878SJulian Pullen update_item(&ctx->site_global_catalog, global_catalog, 16794d61c878SJulian Pullen AD_STATE_AUTO, ttl); 16804d61c878SJulian Pullen update_version(&ctx->site_global_catalog, PARAM1, 16814d61c878SJulian Pullen forest_name_item); 16824d61c878SJulian Pullen update_version(&ctx->site_global_catalog, PARAM2, 16834d61c878SJulian Pullen site_name_item); 1684c8e26105Sjp } 16854d61c878SJulian Pullen return (&ctx->site_global_catalog); 1686c8e26105Sjp } 16874d61c878SJulian Pullen return (NULL); 1688c8e26105Sjp } 1689c8e26105Sjp 1690c8e26105Sjp 1691479ac375Sdm idmap_ad_disc_ds_t * 16924d61c878SJulian Pullen ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, 16937a8a68f5SJulian Pullen boolean_t *auto_discovered) 1694c8e26105Sjp { 1695479ac375Sdm idmap_ad_disc_ds_t *global_catalog = NULL; 16964d61c878SJulian Pullen ad_item_t *global_catalog_item; 16974d61c878SJulian Pullen 16984d61c878SJulian Pullen global_catalog_item = validate_GlobalCatalog(ctx, req); 1699c8e26105Sjp 17004d61c878SJulian Pullen if (global_catalog_item != NULL) { 17014d61c878SJulian Pullen global_catalog = ds_dup(global_catalog_item->value); 17024d61c878SJulian Pullen if (auto_discovered != NULL) 17034d61c878SJulian Pullen *auto_discovered = 17044d61c878SJulian Pullen (global_catalog_item->state == AD_STATE_AUTO); 17054d61c878SJulian Pullen } else if (auto_discovered != NULL) 17067a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 1707c8e26105Sjp 1708c8e26105Sjp return (global_catalog); 1709c8e26105Sjp } 1710c8e26105Sjp 1711c8e26105Sjp 17124d61c878SJulian Pullen static ad_item_t * 17134d61c878SJulian Pullen validate_TrustedDomains(ad_disc_t ctx) 17144d61c878SJulian Pullen { 17154d61c878SJulian Pullen LDAP *ld = NULL; 17164d61c878SJulian Pullen ad_item_t *global_catalog_item; 17174d61c878SJulian Pullen ad_item_t *forest_name_item; 17184d61c878SJulian Pullen ad_disc_trusteddomains_t *trusted_domains; 17194d61c878SJulian Pullen char *dn = NULL; 17204d61c878SJulian Pullen char *forest_name_dn; 17214d61c878SJulian Pullen int len; 17224d61c878SJulian Pullen int num_parts; 17234d61c878SJulian Pullen 17244d61c878SJulian Pullen if (is_fixed(&ctx->trusted_domains)) 17254d61c878SJulian Pullen return (&ctx->trusted_domains); 17264d61c878SJulian Pullen 17274d61c878SJulian Pullen global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 17284d61c878SJulian Pullen if (global_catalog_item == NULL) 17294d61c878SJulian Pullen return (NULL); 17304d61c878SJulian Pullen 17314d61c878SJulian Pullen forest_name_item = validate_ForestName(ctx); 17324d61c878SJulian Pullen if (forest_name_item == NULL) 17334d61c878SJulian Pullen return (NULL); 17344d61c878SJulian Pullen 17354d61c878SJulian Pullen if (!is_valid(&ctx->trusted_domains) || 17364d61c878SJulian Pullen is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || 17374d61c878SJulian Pullen is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { 17384d61c878SJulian Pullen 17394d61c878SJulian Pullen forest_name_dn = ldap_dns_to_dn(forest_name_item->value, 17404d61c878SJulian Pullen &num_parts); 17414d61c878SJulian Pullen if (forest_name_dn == NULL) 17424d61c878SJulian Pullen return (NULL); 17434d61c878SJulian Pullen 17444d61c878SJulian Pullen len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; 17454d61c878SJulian Pullen dn = malloc(len); 17464d61c878SJulian Pullen if (dn == NULL) { 17474d61c878SJulian Pullen free(forest_name_dn); 17484d61c878SJulian Pullen return (NULL); 17494d61c878SJulian Pullen } 17504d61c878SJulian Pullen (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); 17514d61c878SJulian Pullen free(forest_name_dn); 17524d61c878SJulian Pullen 17534d61c878SJulian Pullen trusted_domains = ldap_lookup_trusted_domains( 17544d61c878SJulian Pullen &ld, global_catalog_item->value, dn); 17554d61c878SJulian Pullen 17564d61c878SJulian Pullen if (ld != NULL) 17574d61c878SJulian Pullen (void) ldap_unbind(ld); 17584d61c878SJulian Pullen free(dn); 17594d61c878SJulian Pullen 17604d61c878SJulian Pullen if (trusted_domains == NULL) 17614d61c878SJulian Pullen return (NULL); 17624d61c878SJulian Pullen 17634d61c878SJulian Pullen update_item(&ctx->trusted_domains, trusted_domains, 17644d61c878SJulian Pullen AD_STATE_AUTO, 0); 17654d61c878SJulian Pullen update_version(&ctx->trusted_domains, PARAM1, 17664d61c878SJulian Pullen global_catalog_item); 17674d61c878SJulian Pullen update_version(&ctx->trusted_domains, PARAM2, 17684d61c878SJulian Pullen forest_name_item); 17694d61c878SJulian Pullen } 17704d61c878SJulian Pullen 17714d61c878SJulian Pullen return (&ctx->trusted_domains); 17724d61c878SJulian Pullen } 17734d61c878SJulian Pullen 17744d61c878SJulian Pullen 17754d61c878SJulian Pullen ad_disc_trusteddomains_t * 17767a8a68f5SJulian Pullen ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered) 17774d61c878SJulian Pullen { 17784d61c878SJulian Pullen ad_disc_trusteddomains_t *trusted_domains = NULL; 17794d61c878SJulian Pullen ad_item_t *trusted_domains_item; 17804d61c878SJulian Pullen 17814d61c878SJulian Pullen trusted_domains_item = validate_TrustedDomains(ctx); 17824d61c878SJulian Pullen 17834d61c878SJulian Pullen if (trusted_domains_item != NULL) { 17844d61c878SJulian Pullen trusted_domains = td_dup(trusted_domains_item->value); 17854d61c878SJulian Pullen if (auto_discovered != NULL) 17864d61c878SJulian Pullen *auto_discovered = 17874d61c878SJulian Pullen (trusted_domains_item->state == AD_STATE_AUTO); 17884d61c878SJulian Pullen } else if (auto_discovered != NULL) 17897a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 17904d61c878SJulian Pullen 17914d61c878SJulian Pullen return (trusted_domains); 17924d61c878SJulian Pullen } 17934d61c878SJulian Pullen 17944d61c878SJulian Pullen 17954d61c878SJulian Pullen static ad_item_t * 17964d61c878SJulian Pullen validate_DomainsInForest(ad_disc_t ctx) 17974d61c878SJulian Pullen { 17984d61c878SJulian Pullen ad_item_t *global_catalog_item; 17994d61c878SJulian Pullen LDAP *ld = NULL; 18004d61c878SJulian Pullen ad_disc_domainsinforest_t *domains_in_forest; 18014d61c878SJulian Pullen 18024d61c878SJulian Pullen if (is_fixed(&ctx->domains_in_forest)) 18034d61c878SJulian Pullen return (&ctx->domains_in_forest); 18044d61c878SJulian Pullen 18054d61c878SJulian Pullen global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 18064d61c878SJulian Pullen if (global_catalog_item == NULL) 18074d61c878SJulian Pullen return (NULL); 18084d61c878SJulian Pullen 18094d61c878SJulian Pullen if (!is_valid(&ctx->domains_in_forest) || 18104d61c878SJulian Pullen is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { 18114d61c878SJulian Pullen 18124d61c878SJulian Pullen domains_in_forest = ldap_lookup_domains_in_forest( 18134d61c878SJulian Pullen &ld, global_catalog_item->value); 18144d61c878SJulian Pullen 18154d61c878SJulian Pullen if (ld != NULL) 18164d61c878SJulian Pullen (void) ldap_unbind(ld); 18174d61c878SJulian Pullen 18184d61c878SJulian Pullen if (domains_in_forest == NULL) 18194d61c878SJulian Pullen return (NULL); 18204d61c878SJulian Pullen 18214d61c878SJulian Pullen update_item(&ctx->domains_in_forest, domains_in_forest, 18224d61c878SJulian Pullen AD_STATE_AUTO, 0); 18234d61c878SJulian Pullen update_version(&ctx->domains_in_forest, PARAM1, 18244d61c878SJulian Pullen global_catalog_item); 18254d61c878SJulian Pullen } 18264d61c878SJulian Pullen return (&ctx->domains_in_forest); 18274d61c878SJulian Pullen } 18284d61c878SJulian Pullen 18294d61c878SJulian Pullen 18304d61c878SJulian Pullen ad_disc_domainsinforest_t * 18317a8a68f5SJulian Pullen ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) 18324d61c878SJulian Pullen { 18334d61c878SJulian Pullen ad_disc_domainsinforest_t *domains_in_forest = NULL; 18344d61c878SJulian Pullen ad_item_t *domains_in_forest_item; 18354d61c878SJulian Pullen 18364d61c878SJulian Pullen domains_in_forest_item = validate_DomainsInForest(ctx); 18374d61c878SJulian Pullen 18384d61c878SJulian Pullen if (domains_in_forest_item != NULL) { 18394d61c878SJulian Pullen domains_in_forest = df_dup(domains_in_forest_item->value); 18404d61c878SJulian Pullen if (auto_discovered != NULL) 18414d61c878SJulian Pullen *auto_discovered = 18424d61c878SJulian Pullen (domains_in_forest_item->state == AD_STATE_AUTO); 18434d61c878SJulian Pullen } else if (auto_discovered != NULL) 18447a8a68f5SJulian Pullen *auto_discovered = B_FALSE; 18454d61c878SJulian Pullen 18464d61c878SJulian Pullen return (domains_in_forest); 18474d61c878SJulian Pullen } 18484d61c878SJulian Pullen 18494d61c878SJulian Pullen 18504d61c878SJulian Pullen 18514d61c878SJulian Pullen 1852c8e26105Sjp int 1853c8e26105Sjp ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) 1854c8e26105Sjp { 1855c8e26105Sjp char *domain_name = NULL; 1856c8e26105Sjp if (domainName != NULL) { 1857c8e26105Sjp domain_name = strdup(domainName); 1858c8e26105Sjp if (domain_name == NULL) 1859c8e26105Sjp return (-1); 18604d61c878SJulian Pullen update_item(&ctx->domain_name, domain_name, 18614d61c878SJulian Pullen AD_STATE_FIXED, 0); 18624d61c878SJulian Pullen } else if (ctx->domain_name.state == AD_STATE_FIXED) 18634d61c878SJulian Pullen ctx->domain_name.state = AD_STATE_INVALID; 1864c8e26105Sjp return (0); 1865c8e26105Sjp } 1866c8e26105Sjp 1867c8e26105Sjp 1868c8e26105Sjp int 1869c8e26105Sjp ad_disc_set_DomainController(ad_disc_t ctx, 1870479ac375Sdm const idmap_ad_disc_ds_t *domainController) 1871c8e26105Sjp { 1872479ac375Sdm idmap_ad_disc_ds_t *domain_controller = NULL; 1873c8e26105Sjp if (domainController != NULL) { 18744d61c878SJulian Pullen domain_controller = ds_dup(domainController); 1875c8e26105Sjp if (domain_controller == NULL) 1876c8e26105Sjp return (-1); 18774d61c878SJulian Pullen update_item(&ctx->domain_controller, domain_controller, 18784d61c878SJulian Pullen AD_STATE_FIXED, 0); 18794d61c878SJulian Pullen } else if (ctx->domain_controller.state == AD_STATE_FIXED) 18804d61c878SJulian Pullen ctx->domain_controller.state = AD_STATE_INVALID; 1881c8e26105Sjp return (0); 1882c8e26105Sjp } 1883c8e26105Sjp 1884c8e26105Sjp 1885c8e26105Sjp int 1886c8e26105Sjp ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) 1887c8e26105Sjp { 1888c8e26105Sjp char *site_name = NULL; 1889c8e26105Sjp if (siteName != NULL) { 1890c8e26105Sjp site_name = strdup(siteName); 1891c8e26105Sjp if (site_name == NULL) 1892c8e26105Sjp return (-1); 18934d61c878SJulian Pullen update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); 18944d61c878SJulian Pullen } else if (ctx->site_name.state == AD_STATE_FIXED) 18954d61c878SJulian Pullen ctx->site_name.state = AD_STATE_INVALID; 1896c8e26105Sjp return (0); 1897c8e26105Sjp } 1898c8e26105Sjp 1899c8e26105Sjp int 1900c8e26105Sjp ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) 1901c8e26105Sjp { 1902c8e26105Sjp char *forest_name = NULL; 1903c8e26105Sjp if (forestName != NULL) { 1904c8e26105Sjp forest_name = strdup(forestName); 1905c8e26105Sjp if (forest_name == NULL) 1906c8e26105Sjp return (-1); 19074d61c878SJulian Pullen update_item(&ctx->forest_name, forest_name, 19084d61c878SJulian Pullen AD_STATE_FIXED, 0); 19094d61c878SJulian Pullen } else if (ctx->forest_name.state == AD_STATE_FIXED) 19104d61c878SJulian Pullen ctx->forest_name.state = AD_STATE_INVALID; 1911c8e26105Sjp return (0); 1912c8e26105Sjp } 1913c8e26105Sjp 1914c8e26105Sjp int 1915479ac375Sdm ad_disc_set_GlobalCatalog(ad_disc_t ctx, 1916479ac375Sdm const idmap_ad_disc_ds_t *globalCatalog) 1917c8e26105Sjp { 1918479ac375Sdm idmap_ad_disc_ds_t *global_catalog = NULL; 1919c8e26105Sjp if (globalCatalog != NULL) { 19204d61c878SJulian Pullen global_catalog = ds_dup(globalCatalog); 1921c8e26105Sjp if (global_catalog == NULL) 1922c8e26105Sjp return (-1); 19234d61c878SJulian Pullen update_item(&ctx->global_catalog, global_catalog, 19244d61c878SJulian Pullen AD_STATE_FIXED, 0); 19254d61c878SJulian Pullen } else if (ctx->global_catalog.state == AD_STATE_FIXED) 19264d61c878SJulian Pullen ctx->global_catalog.state = AD_STATE_INVALID; 1927c8e26105Sjp return (0); 1928c8e26105Sjp } 1929c8e26105Sjp 1930c8e26105Sjp 1931c8e26105Sjp int 1932c8e26105Sjp ad_disc_unset(ad_disc_t ctx) 1933c8e26105Sjp { 19344d61c878SJulian Pullen if (ctx->domain_name.state == AD_STATE_FIXED) 19354d61c878SJulian Pullen ctx->domain_name.state = AD_STATE_INVALID; 1936c8e26105Sjp 19374d61c878SJulian Pullen if (ctx->domain_controller.state == AD_STATE_FIXED) 19384d61c878SJulian Pullen ctx->domain_controller.state = AD_STATE_INVALID; 1939c8e26105Sjp 19404d61c878SJulian Pullen if (ctx->site_name.state == AD_STATE_FIXED) 19414d61c878SJulian Pullen ctx->site_name.state = AD_STATE_INVALID; 1942c8e26105Sjp 19434d61c878SJulian Pullen if (ctx->forest_name.state == AD_STATE_FIXED) 19444d61c878SJulian Pullen ctx->forest_name.state = AD_STATE_INVALID; 1945c8e26105Sjp 19464d61c878SJulian Pullen if (ctx->global_catalog.state == AD_STATE_FIXED) 19474d61c878SJulian Pullen ctx->global_catalog.state = AD_STATE_INVALID; 1948c8e26105Sjp 1949c8e26105Sjp return (0); 1950c8e26105Sjp } 1951c8e26105Sjp 1952c8e26105Sjp /* 1953c8e26105Sjp * ad_disc_get_TTL 1954c8e26105Sjp * 1955c8e26105Sjp * This routines the time to live for AD 1956c8e26105Sjp * auto discovered items. 1957c8e26105Sjp * 1958c8e26105Sjp * Returns: 1959c8e26105Sjp * -1 if there are no TTL items 1960c8e26105Sjp * 0 if there are expired items 1961c8e26105Sjp * else the number of seconds 19620dcc7149Snw * 19630dcc7149Snw * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it 19640dcc7149Snw * is positive -- min() greater than zero. 1965c8e26105Sjp */ 19660dcc7149Snw #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ 19670dcc7149Snw (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) 1968c8e26105Sjp int 1969c8e26105Sjp ad_disc_get_TTL(ad_disc_t ctx) 1970c8e26105Sjp { 1971c8e26105Sjp int ttl; 1972c8e26105Sjp 19730dcc7149Snw ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl); 19740dcc7149Snw ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl); 19750dcc7149Snw ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl); 1976c8e26105Sjp 19770dcc7149Snw if (ttl == -1) 1978c8e26105Sjp return (-1); 1979c8e26105Sjp ttl -= time(NULL); 1980c8e26105Sjp if (ttl < 0) 19810dcc7149Snw return (0); 1982c8e26105Sjp return (ttl); 1983c8e26105Sjp } 1984c8e26105Sjp 19857a8a68f5SJulian Pullen boolean_t 1986c8e26105Sjp ad_disc_SubnetChanged(ad_disc_t ctx) 1987c8e26105Sjp { 1988c8e26105Sjp ad_subnet_t *subnets; 1989c8e26105Sjp 1990c8e26105Sjp if (ctx->subnets_changed || ctx->subnets == NULL) 19917a8a68f5SJulian Pullen return (B_TRUE); 1992c8e26105Sjp 1993c8e26105Sjp if ((subnets = find_subnets()) != NULL) { 1994c8e26105Sjp if (cmpsubnets(subnets, ctx->subnets) != 0) 19957a8a68f5SJulian Pullen ctx->subnets_changed = B_TRUE; 1996c8e26105Sjp free(subnets); 1997c8e26105Sjp } 1998c8e26105Sjp 1999c8e26105Sjp return (ctx->subnets_changed); 2000c8e26105Sjp } 2001