diff options
author | nw141292 <none@none> | 2008-02-04 15:57:37 -0800 |
---|---|---|
committer | nw141292 <none@none> | 2008-02-04 15:57:37 -0800 |
commit | 0dcc71495bad040a0c83830efc85acf8d897350d (patch) | |
tree | cd85551cfd283f7a9e9b29d0d2a928acd83f588d /usr | |
parent | cbfb650a37bf2b1cd913645ee5ab0d1a13ef6246 (diff) | |
download | illumos-gate-0dcc71495bad040a0c83830efc85acf8d897350d.tar.gz |
6646649 idmapd auto-discovery should detect network config changes
6648394 idmapd should validate all UTF-8 string inputs with u8_validate()
6649667 idmapd needs a restart after boot on multi-homed domain-mode systems
6656141 some idmapd_priv.h macros need macro hygiene
6657342 improve timeout handling
Diffstat (limited to 'usr')
-rw-r--r-- | usr/src/cmd/idmap/idmapd/addisc.c | 335 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/adutils.c | 122 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/adutils.h | 5 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/dbutils.c | 54 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmap_config.c | 338 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmap_config.h | 6 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmapd.c | 16 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmapd.h | 17 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/init.c | 33 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/server.c | 79 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/idmap_api.c | 5 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/idmap_priv.h | 8 | ||||
-rw-r--r-- | usr/src/uts/common/sys/idmap.h | 3 |
13 files changed, 588 insertions, 433 deletions
diff --git a/usr/src/cmd/idmap/idmapd/addisc.c b/usr/src/cmd/idmap/idmapd/addisc.c index f7663a540c..5cf7d915cb 100644 --- a/usr/src/cmd/idmap/idmapd/addisc.c +++ b/usr/src/cmd/idmap/idmapd/addisc.c @@ -20,13 +20,64 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - #pragma ident "%Z%%M% %I% %E% SMI" +/* + * Active Directory Auto-Discovery. + * + * This [project private] API allows the caller to provide whatever + * details it knows a priori (i.e., provided via configuration so as to + * override auto-discovery) and in any order. Then the caller can ask + * for any of the auto-discoverable parameters in any order. + * + * But there is an actual order in which discovery must be done. Given + * the discovery mechanism implemented here, that order is: + * + * - the domain name joined must be discovered first + * - then the domain controllers + * - then the forest name and site name + * - then the global catalog servers, and site-specific domain + * controllers and global catalog servers. + * + * The API does not require it be called in the same order because there + * may be other discovery mechanisms in the future, and exposing + * ordering requirements of the current mechanism now can create trouble + * down the line. Also, this makes the API easier to use now, which + * means less work to do some day when we make this a public API. + * + * Domain discovery is done by res_nsearch() of the DNS SRV RR name for + * domain controllers. As long as the joined domain appears in the DNS + * resolver's search list then we'll find it. + * + * Domain controller discovery is a matter of formatting the DNS SRV RR + * FQDN for domain controllers and doing a lookup for them. Knowledge + * of the domain name is not fundamentally required, but we separate the + * two processes, which in practice can lead to one more DNS lookup than + * is strictly required. + * + * Forest and site name discovery require an LDAP search of the AD + * "configuration partition" at a domain controller for the joined + * domain. Forest and site name discovery depend on knowing the joined + * domain name and domain controllers for that domain. + * + * Global catalog server discovery requires knowledge of the forest + * name in order to format the DNS SRV RR FQDN to lookup. Site-specific + * domain controller discovery depends on knowing the site name (and, + * therefore, joined domain, ...). Site-specific global catalog server + * discovery depends on knowledge of the forest and site names, which + * depend on... + * + * All the work of discovering particular items is done by functions + * named validate_<item>(). Each such function calls validate_<item>() + * for any items that it depends on. + * + * This API is not thread-safe. + */ + #include <stdio.h> #include <string.h> @@ -102,6 +153,7 @@ typedef struct ad_subnet { struct ad_disc { struct __res_state state; + int res_ninitted; ad_subnet_t *subnets; int subnets_changed; time_t subnets_last_check; @@ -115,17 +167,19 @@ struct ad_disc { ad_item_t site_global_catalog; }; -#define min(x, y) (((x) > (y)) ? (y) : (x)) - +/* + * We try res_ninit() whenever we don't have one. res_ninit() fails if + * idmapd is running before the network is up! + */ +#define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \ + (ctx)->res_ninitted = res_ninit(&ctx->state) != -1 #define is_fixed(item) \ ((item)->type == AD_TYPE_FIXED) - #define is_changed(item, num, param) \ ((item)->param_version[num] != (param)->version) - #define is_valid(item) \ ((item)->type != AD_TYPE_INVALID && (item)->value.str != NULL) @@ -445,8 +499,9 @@ subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) return (results); } +/* Compare DS lists */ int -ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2) +ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2) { int i, j; int num_ds1; @@ -477,8 +532,9 @@ ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2) } +/* Copy a list of DSs */ static ad_disc_ds_t * -dsdup(const ad_disc_ds_t *srv) +dsdup(const ad_disc_ds_t *srv) { int i; int size; @@ -542,6 +598,9 @@ srv_query(res_state state, const char *svc_name, const char *dname, char *query_type; char namebuf[NS_MAXDNAME]; + if (state == NULL) + return (NULL); + /* Set negative result TTL */ *ttl = 5 * 60; @@ -684,7 +743,7 @@ ldap_lookup_entry_attr(LDAP **ld, ad_disc_ds_t *domainControllers, int i; int rc, ldversion; int zero = 0; - int timeoutms = 30 * 1000; + int timeoutms = 5 * 1000; char *saslmech = "GSSAPI"; uint32_t saslflags = LDAP_SASL_INTERACTIVE; int scope = LDAP_SCOPE_BASE; @@ -783,15 +842,8 @@ ad_disc_init(void) { struct ad_disc *ctx; ctx = calloc(1, sizeof (struct ad_disc)); - if (ctx != NULL) { - if (res_ninit(&ctx->state) == -1) { - idmapdlog(LOG_ERR, - "%s: Could not load DNS resolver configuration", - me); - free(ctx); - return (NULL); - } - } + if (ctx != NULL) + DO_RES_NINIT(ctx); return (ctx); } @@ -802,7 +854,8 @@ ad_disc_fini(ad_disc_t ctx) if (ctx == NULL) return; - res_ndestroy(&ctx->state); + if (ctx->res_ninitted) + res_ndestroy(&ctx->state); if (ctx->subnets != NULL) free(ctx->subnets); @@ -834,9 +887,10 @@ ad_disc_fini(ad_disc_t ctx) void ad_disc_refresh(ad_disc_t ctx) { - res_ndestroy(&ctx->state); + if (ctx->res_ninitted) + res_ndestroy(&ctx->state); (void) memset(&ctx->state, 0, sizeof (ctx->state)); - (void) res_ninit(&ctx->state); + ctx->res_ninitted = res_ninit(&ctx->state) != -1; if (ctx->domain_name.type == AD_TYPE_AUTO) ctx->domain_name.type = AD_TYPE_INVALID; @@ -862,10 +916,7 @@ ad_disc_refresh(ad_disc_t ctx) -/* - * Find DNS domainName - * - */ +/* Discover joined Active Directory domainName */ static void validate_DomainName(ad_disc_t ctx) { @@ -880,6 +931,7 @@ validate_DomainName(ad_disc_t ctx) return; /* Try to find our domain by searching for DCs for it */ + DO_RES_NINIT(ctx); domain_controller = srv_query(&ctx->state, LDAP_SRV_HEAD DC_SRV_TAIL, ctx->domain_name.value.str, &srvname, &ttl); @@ -926,10 +978,7 @@ ad_disc_get_DomainName(ad_disc_t ctx) } -/* - * Find a Domain Controller - * - */ +/* Discover domain controllers */ static void validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) { @@ -960,9 +1009,10 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) if (is_valid(&ctx->domain_name)) { /* - * The DNS SRV record for - * _ldap._tcp.dc._msdcs.<DnsDominName> + * Lookup DNS SRV RR named + * _ldap._tcp.dc._msdcs.<DomainName> */ + DO_RES_NINIT(ctx); domain_controller = srv_query(&ctx->state, LDAP_SRV_HEAD DC_SRV_TAIL, ctx->domain_name.value.str, NULL, &ttl); @@ -985,12 +1035,13 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) if (is_valid(&ctx->domain_name)) { char rr_name[DNS_MAX_NAME]; /* - * The DNS SRV record for - * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DnsDominName> + * Lookup DNS SRV RR named + * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> */ (void) snprintf(rr_name, sizeof (rr_name), LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, ctx->site_name.value.str); + DO_RES_NINIT(ctx); domain_controller = srv_query(&ctx->state, rr_name, ctx->domain_name.value.str, NULL, &ttl); } @@ -1014,6 +1065,7 @@ ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req) } +/* Discover site name (for multi-homed systems the first one found wins) */ static void validate_SiteName(ad_disc_t ctx) { @@ -1032,10 +1084,7 @@ validate_SiteName(ad_disc_t ctx) if (is_fixed(&ctx->site_name)) return; - /* - * We dont want to cause a recursive loop by validating - * the site specific domain controller. - */ + /* Can't rely on site-specific DCs */ validate_DomainController(ctx, AD_DISC_GLOBAL); if (is_expired(&ctx->site_name) || @@ -1051,95 +1100,99 @@ validate_SiteName(ad_disc_t ctx) update_required = TRUE; } - if (update_required) { - update_version(&ctx->site_name, PARAM1, - &ctx->domain_controller); - - if (is_valid(&ctx->domain_name) && - is_valid(&ctx->domain_controller) && - subnets != NULL) { - dn_root[0] = ""; - dn_root[1] = NULL; + if (!update_required) { + free(subnets); + return; + } - config_naming_context = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value.ds, - dn_root, "configurationNamingContext"); - if (config_naming_context == NULL) - goto out; + update_version(&ctx->site_name, PARAM1, &ctx->domain_controller); + + if (is_valid(&ctx->domain_name) && + is_valid(&ctx->domain_controller) && + subnets != NULL) { + dn_root[0] = ""; + dn_root[1] = NULL; + + config_naming_context = ldap_lookup_entry_attr( + &ld, ctx->domain_controller.value.ds, + dn_root, "configurationNamingContext"); + if (config_naming_context == NULL) + goto out; + /* + * configurationNamingContext also provides the Forest + * Name. + */ + if (!is_fixed(&ctx->forest_name)) { /* - * configurationNamingContext also provides - * the Forest Name. + * The configurationNamingContext should be of + * form: + * CN=Configuration,<DNforestName> + * Remove the first part and convert to DNS form + * (replace ",DC=" with ".") */ - if (!is_fixed(&ctx->forest_name)) { - /* - * The configurationNamingContext should be of - * form: - * CN=Configuration,<DNforestName> - * Remove the first part and convert to DNS form - * (replace ",DC=" with ".") - */ - char *str = "CN=Configuration,"; - int len = strlen(str); - if (strncasecmp(config_naming_context, - str, len) == 0) { - forest_name = DN_to_DNS( - config_naming_context + len); - update_string(&ctx->forest_name, - forest_name, AD_TYPE_AUTO, 0); - } + char *str = "CN=Configuration,"; + int len = strlen(str); + if (strncasecmp(config_naming_context, str, len) == 0) { + forest_name = DN_to_DNS( + config_naming_context + len); + update_string(&ctx->forest_name, + forest_name, AD_TYPE_AUTO, 0); } - dn_subnets = - subnets_to_DNs(subnets, config_naming_context); - if (dn_subnets == NULL) - goto out; - - site_object = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value.ds, - dn_subnets, "siteobject"); - if (site_object != NULL) { - /* - * The site object should be of the form - * CN=<site>,CN=Sites,CN=Configuration, - * <DN Domain> - */ - if (strncasecmp(site_object, "CN=", 3) == 0) { - for (len = 0; - site_object[len + 3] != ','; len++) - ; - site_name = malloc(len + 1); - (void) strncpy(site_name, - &site_object[3], len); - site_name[len] = '\0'; - } + } + dn_subnets = subnets_to_DNs(subnets, config_naming_context); + if (dn_subnets == NULL) + goto out; + + site_object = ldap_lookup_entry_attr( + &ld, ctx->domain_controller.value.ds, + dn_subnets, "siteobject"); + if (site_object != NULL) { + /* + * The site object should be of the form + * CN=<site>,CN=Sites,CN=Configuration, + * <DN Domain> + */ + if (strncasecmp(site_object, "CN=", 3) == 0) { + for (len = 0; + site_object[len + 3] != ','; len++) + ; + site_name = malloc(len + 1); + (void) strncpy(site_name, &site_object[3], len); + site_name[len] = '\0'; } + } - if (ctx->subnets != NULL) { - free(ctx->subnets); - ctx->subnets = NULL; - } - ctx->subnets = subnets; - subnets = NULL; - ctx->subnets_changed = FALSE; + if (ctx->subnets != NULL) { + free(ctx->subnets); + ctx->subnets = NULL; } + ctx->subnets = subnets; + subnets = NULL; + ctx->subnets_changed = FALSE; + } out: - if (ld != NULL) - (void) ldap_unbind(ld); + if (ld != NULL) + (void) ldap_unbind(ld); - update_string(&ctx->site_name, site_name, AD_TYPE_AUTO, 0); + update_string(&ctx->site_name, site_name, AD_TYPE_AUTO, 0); - if (dn_subnets != NULL) { - for (i = 0; dn_subnets[i] != NULL; i++) - free(dn_subnets[i]); - free(dn_subnets); - } - if (config_naming_context != NULL) - free(config_naming_context); - if (site_object != NULL) - free(site_object); + if (site_name == NULL || *site_name == '\0') { + /* No site name -> no site-specific DSs */ + update_ds(&ctx->site_domain_controller, NULL, AD_TYPE_AUTO, 0); + update_ds(&ctx->site_global_catalog, NULL, AD_TYPE_AUTO, 0); } - if (subnets != NULL) - free(subnets); + if (dn_subnets != NULL) { + for (i = 0; dn_subnets[i] != NULL; i++) + free(dn_subnets[i]); + free(dn_subnets); + } + if (config_naming_context != NULL) + free(config_naming_context); + if (site_object != NULL) + free(site_object); + + free(subnets); } @@ -1157,6 +1210,7 @@ ad_disc_get_SiteName(ad_disc_t ctx) +/* Discover forest name */ static void validate_ForestName(ad_disc_t ctx) { @@ -1168,8 +1222,9 @@ validate_ForestName(ad_disc_t ctx) if (is_fixed(&ctx->forest_name)) return; /* - * We dont want to cause a recursive loop by validating - * the site specific domain controller. + * We may not have a site name yet, so we won't rely on + * site-specific DCs. (But maybe we could replace + * validate_ForestName() with validate_siteName()?) */ validate_DomainController(ctx, AD_DISC_GLOBAL); if (is_expired(&ctx->forest_name) || @@ -1223,11 +1278,12 @@ ad_disc_get_ForestName(ad_disc_t ctx) } +/* Discover global catalog servers */ static void validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) { ad_disc_ds_t *global_catalog = NULL; - uint32_t ttl; + uint32_t ttl = 0; int validate_global = FALSE; int validate_site = FALSE; @@ -1252,11 +1308,10 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) if (is_valid(&ctx->forest_name)) { /* - * The DNS SRV record for the non-site - * Global Catalogue is: - * - * _ldap._tcp.gc._msdcs.<DnsForestName> + * Lookup DNS SRV RR named + * _ldap._tcp.gc._msdcs.<ForestName> */ + DO_RES_NINIT(ctx); global_catalog = srv_query(&ctx->state, LDAP_SRV_HEAD GC_SRV_TAIL, ctx->forest_name.value.str, NULL, &ttl); @@ -1277,16 +1332,15 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) if (is_valid(&ctx->forest_name) && is_valid(&ctx->site_name)) { char rr_name[DNS_MAX_NAME]; /* - * The DNS SRV record for the site - * Global Catalogue is: - * - * _ldap._tcp.<sitename>._sites.gc. - * _msdcs.<DnsForestName> + * Lookup DNS SRV RR named: + * _ldap._tcp.<siteName>._sites.gc. + * _msdcs.<ForestName> */ (void) snprintf(rr_name, sizeof (rr_name), LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, ctx->site_name.value.str); + DO_RES_NINIT(ctx); global_catalog = srv_query(&ctx->state, rr_name, ctx->forest_name.value.str, NULL, &ttl); @@ -1421,35 +1475,26 @@ ad_disc_unset(ad_disc_t ctx) * -1 if there are no TTL items * 0 if there are expired items * else the number of seconds + * + * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it + * is positive -- min() greater than zero. */ +#define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ + (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) int ad_disc_get_TTL(ad_disc_t ctx) { int ttl; - ttl = ctx->domain_controller.ttl; - - if (ttl == 0) - ttl = ctx->global_catalog.ttl; - else - ttl = min(ttl, ctx->global_catalog.ttl); - - if (ttl == 0) - ttl = ctx->site_domain_controller.ttl; - else - ttl = min(ttl, ctx->domain_controller.ttl); + ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl); + ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl); + ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl); - if (ttl == 0) - ttl = ctx->site_global_catalog.ttl; - else - ttl = min(ttl, ctx->site_global_catalog.ttl); - - if (ttl == 0) + if (ttl == -1) return (-1); ttl -= time(NULL); if (ttl < 0) - ttl = 0; - + return (0); return (ttl); } diff --git a/usr/src/cmd/idmap/idmapd/adutils.c b/usr/src/cmd/idmap/idmapd/adutils.c index 5f275dcc64..53f4c37f9f 100644 --- a/usr/src/cmd/idmap/idmapd/adutils.c +++ b/usr/src/cmd/idmap/idmapd/adutils.c @@ -454,11 +454,11 @@ idmap_ad_free(ad_t **ad) static int -idmap_open_conn(ad_host_t *adh) +idmap_open_conn(ad_host_t *adh, int timeoutsecs) { int zero = 0; - int timeoutms = 30 * 1000; int ldversion, rc; + int timeoutms = timeoutsecs * 1000; if (adh == NULL) return (0); @@ -528,73 +528,76 @@ ad_host_t * idmap_get_conn(ad_t *ad) { ad_host_t *adh = NULL; - ad_host_t *first_adh, *next_adh; - int seen_last; - int tries = -1; + int tries; + int dscount = 0; + int timeoutsecs = IDMAPD_LDAP_OPEN_TIMEOUT; retry: (void) pthread_mutex_lock(&adhostlock); - if (host_head == NULL) + if (host_head == NULL) { + (void) pthread_mutex_unlock(&adhostlock); goto out; + } + + if (dscount == 0) { + /* + * First try: count the number of DSes. + * + * Integer overflow is not an issue -- we can't have so many + * DSes because they won't fit even DNS over TCP, and SMF + * shouldn't let you set so many. + */ + for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) { + if (adh->owner == ad) + dscount++; + } + + if (dscount == 0) { + (void) pthread_mutex_unlock(&adhostlock); + goto out; + } - /* Try as many DSs as we have, once each; count them once */ - if (tries < 0) { - for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) - tries++; + tries = dscount * 3; /* three tries per-ds */ + + /* + * Begin round-robin at the next DS in the list after the last + * one that we had a connection to, else start with the first + * DS in the list. + */ + adh = ad->last_adh; } /* - * Find a suitable ad_host_t (one associated with this ad_t, - * preferably one that's already connected and not dead), - * possibly round-robining through the ad_host_t list. - * - * If we can't find a non-dead ad_host_t and we've had one - * before (ad->last_adh != NULL) then pick the next one or, if - * there is no next one, the first one (i.e., round-robin). - * - * If we've never had one then (ad->last_adh == NULL) then pick - * the first one. - * - * If we ever want to be more clever, such as preferring DSes - * with better average response times, adjusting for weights - * from SRV RRs, and so on, then this is the place to do it. + * Round-robin -- pick the next one on the list; if the list + * changes on us, no big deal, we'll just potentially go + * around the wrong number of times. */ - - for (adh = host_head, first_adh = NULL, next_adh = NULL, seen_last = 0; - adh != NULL; adh = adh->next) { - if (adh->owner != ad) - continue; - if (first_adh == NULL) - first_adh = adh; - if (adh == ad->last_adh) - seen_last++; - else if (seen_last) - next_adh = adh; - - /* First time or current adh is live -> done */ - if (ad->last_adh == NULL || (!adh->dead && adh->ld != NULL)) + for (;;) { + if (adh == NULL || (adh = adh->next) == NULL) + adh = host_head; + if (adh->owner == ad) break; } - /* Round-robin */ - if (adh == NULL) - adh = (next_adh != NULL) ? next_adh : first_adh; - - if (adh != NULL) - ad->last_adh = adh; - -out: + ad->last_adh = adh; (void) pthread_mutex_unlock(&adhostlock); + /* Found suitable DS, open it if not already opened */ - if (idmap_open_conn(adh)) + if (idmap_open_conn(adh, timeoutsecs)) return (adh); - if (tries-- > 0) + tries--; + + if ((tries % dscount) == 0) + timeoutsecs *= 2; + + if (tries > 0) goto retry; - idmapdlog(LOG_ERR, "Couldn't open an LDAP connection to any global " +out: + idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global " "catalog server!"); return (NULL); @@ -1001,7 +1004,7 @@ idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state) adh = idmap_get_conn(ad); if (adh == NULL) - return (IDMAP_ERR_OTHER); + return (IDMAP_ERR_RETRIABLE_NET_ERR); new_state = calloc(1, sizeof (idmap_query_state_t) + (nqueries - 1) * sizeof (idmap_q_t)); @@ -1425,9 +1428,8 @@ idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout) /* Get one result */ rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res); - if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM || - rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN || - rc == LDAP_BUSY) + if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) || + rc < 0) adh->dead = 1; (void) pthread_mutex_unlock(&adh->lock); @@ -1560,27 +1562,25 @@ idmap_lookup_release_batch(idmap_query_state_t **state) } idmap_retcode -idmap_lookup_batch_end(idmap_query_state_t **state, - struct timeval *timeout) +idmap_lookup_batch_end(idmap_query_state_t **state) { int rc = LDAP_SUCCESS; idmap_retcode retcode = IDMAP_SUCCESS; + struct timeval timeout; (*state)->qdead = 1; + timeout.tv_sec = IDMAPD_SEARCH_TIMEOUT; + timeout.tv_usec = 0; /* Process results until done or until timeout, if given */ while ((*state)->qinflight > 0) { if ((rc = idmap_get_adobject_batch((*state)->qadh, - timeout)) != 0) + &timeout)) != 0) break; } - if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM || - rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN || - rc == LDAP_BUSY) { + if (rc != 0) retcode = IDMAP_ERR_RETRIABLE_NET_ERR; - (*state)->qadh->dead = 1; - } idmap_lookup_release_batch(state); diff --git a/usr/src/cmd/idmap/idmapd/adutils.h b/usr/src/cmd/idmap/idmapd/adutils.h index 9c5c589e39..8c27c88983 100644 --- a/usr/src/cmd/idmap/idmapd/adutils.h +++ b/usr/src/cmd/idmap/idmapd/adutils.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -136,8 +136,7 @@ idmap_retcode idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state); /* End a batch and release its idmap_query_state_t object */ -idmap_retcode idmap_lookup_batch_end(idmap_query_state_t **state, - struct timeval *timeout); +idmap_retcode idmap_lookup_batch_end(idmap_query_state_t **state); /* Abandon a batch and release its idmap_query_state_t object */ void idmap_lookup_release_batch(idmap_query_state_t **state); diff --git a/usr/src/cmd/idmap/idmapd/dbutils.c b/usr/src/cmd/idmap/idmapd/dbutils.c index f3d7ba725d..6a6c0718a0 100644 --- a/usr/src/cmd/idmap/idmapd/dbutils.c +++ b/usr/src/cmd/idmap/idmapd/dbutils.c @@ -211,22 +211,28 @@ get_fmri(void) * have a successful AD name<->SID lookup. */ void -degrade_svc(void) +degrade_svc(const char *reason) { const char *fmri; + /* + * If the config update thread is in a state where auto-discovery could + * be re-tried, then this will make it try it -- a sort of auto-refresh. + */ + idmap_cfg_poke_updates(); + if ((fmri = get_fmri()) == NULL) return; membar_consumer(); if (degraded) return; - (void) smf_degrade_instance(fmri, 0); membar_producer(); degraded = 1; + (void) smf_degrade_instance(fmri, 0); + idmapdlog(LOG_ERR, "idmapd: Degraded operation (%s)", reason); } - void restore_svc(void) { @@ -1737,9 +1743,9 @@ retry: retcode = idmap_lookup_batch_start(_idmapdstate.ad, state->ad_nqueries, &qs); if (retcode != IDMAP_SUCCESS) { - degrade_svc(); - idmapdlog(LOG_ERR, - "Failed to create batch for AD lookup"); + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + goto retry; + degrade_svc("failed to create batch for AD lookup"); goto out; } @@ -1856,12 +1862,12 @@ retry: } if (retcode == IDMAP_SUCCESS && add) - retcode = idmap_lookup_batch_end(&qs, NULL); + retcode = idmap_lookup_batch_end(&qs); if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) goto retry; else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) - degrade_svc(); + degrade_svc("some AD lookups timed out repeatedly"); if (retcode != IDMAP_SUCCESS) idmapdlog(LOG_NOTICE, "Failed to batch AD lookup requests"); @@ -3178,9 +3184,10 @@ ad_lookup_by_winname(lookup_state_t *state, retry: retcode = idmap_lookup_batch_start(_idmapdstate.ad, 1, &qs); if (retcode != IDMAP_SUCCESS) { - degrade_svc(); - idmapdlog(LOG_ERR, - "Failed to create request for AD lookup by winname"); + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + goto retry; + degrade_svc("failed to create request for AD lookup " + "by winname"); return (retcode); } @@ -3196,12 +3203,12 @@ retry: if (retcode != IDMAP_SUCCESS) idmap_lookup_release_batch(&qs); else - retcode = idmap_lookup_batch_end(&qs, NULL); + retcode = idmap_lookup_batch_end(&qs); if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) goto retry; else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) - degrade_svc(); + degrade_svc("some AD lookups timed out repeatedly"); if (retcode != IDMAP_SUCCESS) { idmapdlog(LOG_NOTICE, "AD lookup by winname failed"); @@ -3653,9 +3660,9 @@ ad_lookup_by_sid(lookup_state_t *state, retry: retcode = idmap_lookup_batch_start(_idmapdstate.ad, 1, &qs); if (retcode != IDMAP_SUCCESS) { - degrade_svc(); - idmapdlog(LOG_ERR, - "Failed to create request for AD lookup by SID"); + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + goto retry; + degrade_svc("failed to create request for AD lookup by SID"); return (retcode); } @@ -3671,12 +3678,12 @@ retry: if (retcode != IDMAP_SUCCESS) idmap_lookup_release_batch(&qs); else - retcode = idmap_lookup_batch_end(&qs, NULL); + retcode = idmap_lookup_batch_end(&qs); if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) goto retry; else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) - degrade_svc(); + degrade_svc("some AD lookups timed out repeatedly"); if (retcode != IDMAP_SUCCESS) { idmapdlog(LOG_NOTICE, "AD lookup by SID failed"); @@ -4010,9 +4017,10 @@ ad_lookup_by_unixname(lookup_state_t *state, retry: retcode = idmap_lookup_batch_start(_idmapdstate.ad, 1, &qs); if (retcode != IDMAP_SUCCESS) { - degrade_svc(); - idmapdlog(LOG_ERR, - "Failed to create request for AD lookup by unixname"); + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + goto retry; + degrade_svc("failed to create request for AD lookup " + "by unixname"); return (IDMAP_ERR_INTERNAL); } @@ -4028,12 +4036,12 @@ retry: if (retcode != IDMAP_SUCCESS) idmap_lookup_release_batch(&qs); else - retcode = idmap_lookup_batch_end(&qs, NULL); + retcode = idmap_lookup_batch_end(&qs); if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) goto retry; else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) - degrade_svc(); + degrade_svc("some AD lookups timed out repeatedly"); if (retcode != IDMAP_SUCCESS) { idmapdlog(LOG_NOTICE, "AD lookup by unixname failed"); diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.c b/usr/src/cmd/idmap/idmapd/idmap_config.c index ccfef13f2e..649662c8f0 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.c +++ b/usr/src/cmd/idmap/idmapd/idmap_config.c @@ -40,14 +40,15 @@ #include <uuid/uuid.h> #include <pthread.h> #include <port.h> +#include <net/route.h> #include "addisc.h" -#define MACHINE_SID_LEN (9 + UUID_LEN/4 * 11) -#define FMRI_BASE "svc:/system/idmap" -#define CONFIG_PG "config" -#define GENERAL_PG "general" -/* initial length of the array for policy options/attributes: */ -#define DEF_ARRAY_LENGTH 16 +#define MACHINE_SID_LEN (9 + UUID_LEN/4 * 11) +#define FMRI_BASE "svc:/system/idmap" +#define CONFIG_PG "config" +#define GENERAL_PG "general" +#define RECONFIGURE 1 +#define POKE_AUTO_DISCOVERY 2 /*LINTLIBRARY*/ @@ -57,8 +58,8 @@ static const char *me = "idmapd"; static pthread_t update_thread_handle = 0; -int hup_ev_port = -1; -extern int hupped; +static int idmapd_ev_port = -1; +static int rt_sock = -1; static int generate_machine_sid(char **machine_sid) @@ -434,10 +435,12 @@ update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name) { int i; - if (*new == NULL) + if (*value == *new) + /* Nothing to do */ return (FALSE); - if (*value != NULL && ad_disc_compare_ds(*value, *new) == 0) { + if (*value != NULL && *new != NULL && + ad_disc_compare_ds(*value, *new) == 0) { free(*new); *new = NULL; return (FALSE); @@ -446,93 +449,119 @@ update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name) if (*value) free(*value); - for (i = 0; (*new)[i].host[0] != '\0'; i++) - idmapdlog(LOG_INFO, "%s: change %s=%s port=%d", me, name, - (*new)[i].host, (*new)[i].port); *value = *new; *new = NULL; + + if (*value == NULL) { + /* We're unsetting this DS property */ + idmapdlog(LOG_INFO, "%s: change %s=<none>", me, name); + return (TRUE); + } + + /* List all the new DSs */ + for (i = 0; (*value)[i].host[0] != '\0'; i++) + idmapdlog(LOG_INFO, "%s: change %s=%s port=%d", me, name, + (*value)[i].host, (*value)[i].port); return (TRUE); } -#define SUBNET_CHECK_TIME (2 * 60) -#define MAX_CHECK_TIME (10 * 60) +#define MAX_CHECK_TIME (20 * 60) /* - * Returns 1 if SIGHUP has been received (see hup_handler elsewhere), 0 - * otherwise. Uses an event port and a user-defined event. + * Returns 1 if the PF_ROUTE socket event indicates that we should rescan the + * interfaces. * - * Note that port_get() does not update its timeout argument when EINTR, - * unlike nanosleep(). We probably don't care very much here because - * the only signals we expect are ones that will lead to idmapd dying or - * SIGHUP, and we intend for the latter to cause this function to - * return. But if we did care then we could always use a timer event - * (see timer_create(3RT)) and associate it with the same event port, - * then we could get accurate waiting regardless of EINTRs. + * Shamelessly based on smb_nics_changed() and other PF_ROUTE uses in ON. */ static int -wait_for_ttl(struct timespec *timeout) +pfroute_event_is_interesting(int rt_sock) { - port_event_t pe; - int retries = 1; + int nbytes; + int64_t msg[2048 / 8]; + struct rt_msghdr *rtm; + int is_interesting = FALSE; - /* - * If event port creation failed earlier and fails now then we - * simply don't learn about SIGHUPs in a timely fashion. No big - * deal - */ - if (hup_ev_port == -1 && (hup_ev_port = port_create()) < 0) { - (void) nanosleep(timeout, NULL); - return (0); + for (;;) { + if ((nbytes = read(rt_sock, msg, sizeof (msg))) <= 0) + break; + rtm = (struct rt_msghdr *)msg; + if (rtm->rtm_version != RTM_VERSION) + continue; + if (nbytes < rtm->rtm_msglen) + continue; + switch (rtm->rtm_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_IFINFO: + is_interesting = TRUE; + break; + default: + break; + } } + return (is_interesting); +} + +/* + * Returns 1 if SIGHUP has been received (see hup_handler() elsewhere) or if an + * interface address was added or removed; otherwise it returns 0. + * + * Note that port_get() does not update its timeout argument when EINTR, unlike + * nanosleep(). We probably don't care very much here, but if we did care then + * we could always use a timer event and associate it with the same event port, + * then we could get accurate waiting regardless of EINTRs. + */ +static +int +wait_for_event(int poke_is_interesting, struct timespec *timeout) +{ + port_event_t pe; retry: - if (port_get(hup_ev_port, &pe, timeout) != 0) { + memset(&pe, 0, sizeof (pe)); + if (port_get(idmapd_ev_port, &pe, timeout) != 0) { switch (errno) { - case EBADF: - case EBADFD: - hup_ev_port = -1; - (void) nanosleep(timeout, NULL); - break; - case EINVAL: - /* - * Shouldn't happen, except, perhaps, near the - * end of time - */ - timeout->tv_nsec = 0; - timeout->tv_sec = MAX_CHECK_TIME; - if (retries-- > 0) - goto retry; - /* NOTREACHED */ - break; case EINTR: - if (!hupped) - goto retry; - break; + goto retry; case ETIME: /* Timeout */ - break; + return (FALSE); default: - /* EFAULT */ - (void) nanosleep(timeout, NULL); + /* EBADF, EBADFD, EFAULT, EINVAL (end of time?)? */ + idmapdlog(LOG_ERR, "Event port failed: %s", + strerror(errno)); + exit(1); + /* NOTREACHED */ break; } } - /* - * We only have one event that we care about, a user event, so - * there's nothing to check or clean up about pe. - * - * If we get here it's either because we had a SIGHUP and a user - * event was sent to our port, or because the port_get() timed - * out (or even both!). - */ + if (pe.portev_source == PORT_SOURCE_USER && + pe.portev_events == POKE_AUTO_DISCOVERY) + return (poke_is_interesting ? TRUE : FALSE); + + if (pe.portev_source == PORT_SOURCE_FD && pe.portev_object == rt_sock) { + /* PF_ROUTE socket read event, re-associate fd, handle event */ + if (port_associate(idmapd_ev_port, PORT_SOURCE_FD, rt_sock, + POLLIN, NULL) != 0) { + idmapdlog(LOG_ERR, "Failed to re-associate the " + "routing socket with the event port: %s", + strerror(errno)); + exit(1); + } + /* + * The network configuration may still be in flux. No matter, + * the resolver will re-transmit and timout if need be. + */ + return (pfroute_event_is_interesting(rt_sock)); + } - if (hupped) { + if (pe.portev_source == PORT_SOURCE_USER && + pe.portev_events == RECONFIGURE) { int rc; - hupped = 0; /* * Blow away the ccache, we might have re-joined the * domain or joined a new one @@ -553,10 +582,10 @@ retry: "idmapd: Various errors re-loading configuration " "may cause AD lookups to fail"); UNLOCK_CONFIG(); - return (1); + return (TRUE); } - return (0); + return (FALSE); } void * @@ -564,114 +593,78 @@ idmap_cfg_update_thread(void *arg) { idmap_pg_config_t new_cfg; - int ttl, changed; + int ttl, changed, poke_is_interesting; idmap_cfg_handles_t *handles = &_idmapdstate.cfg->handles; idmap_pg_config_t *live_cfg = &_idmapdstate.cfg->pgcfg; ad_disc_t ad_ctx = handles->ad_ctx; - struct timespec delay; - int first = 1; + struct timespec timeout; (void) memset(&new_cfg, 0, sizeof (new_cfg)); - for (;;) { - changed = FALSE; - - if (first) { - ttl = 1; - first = 0; - } else { - ttl = ad_disc_get_TTL(ad_ctx); - } - - if (ttl > MAX_CHECK_TIME) + poke_is_interesting = 1; + for (ttl = 0, changed = TRUE; ; ttl = ad_disc_get_TTL(ad_ctx)) { + if (ttl < 0 || ttl > MAX_CHECK_TIME) { ttl = MAX_CHECK_TIME; - while (ttl > 0 || ttl == -1) { - if (ttl == -1) { - wait_for_ttl(NULL); - } else if (ttl > SUBNET_CHECK_TIME) { - /* - * We really ought to just monitor - * network interfaces with a PF_ROUTE - * socket... This crude method of - * discovering subnet changes will do - * for now. Though might even not want - * to bother: subnet changes leading to - * sitename changes ought never happen, - * and requiring a refresh when they do - * should be no problem (SMF/NWAM ought - * to be able to refresh us). - */ - delay.tv_sec = SUBNET_CHECK_TIME; - delay.tv_nsec = 0; - if (wait_for_ttl(&delay)) { - /* Got SIGHUP, re-discover */ - ttl = 0; - changed = TRUE; - break; - } - ttl -= SUBNET_CHECK_TIME; - if (ad_disc_SubnetChanged(ad_ctx)) - break; - } else { - delay.tv_sec = ttl; - delay.tv_nsec = 0; - if (wait_for_ttl(&delay)) - changed = TRUE; - ttl = 0; - } } + timeout.tv_sec = ttl; + timeout.tv_nsec = 0; + changed = wait_for_event(poke_is_interesting, &timeout); + + /* + * If there are no interesting events, and this is not the first + * time through the loop, and we haven't waited the most that + * we're willing to wait, so do nothing but wait some more. + */ + if (changed == FALSE && ttl > 0 && ttl < MAX_CHECK_TIME) + continue; + + (void) ad_disc_SubnetChanged(ad_ctx); /* * Load configuration data into a private copy. * - * The fixed values (i.e., from SMF) have already been - * set in AD auto discovery, so if all values have been - * set in SMF and they haven't been changed or the - * service been refreshed then the rest of this loop's - * body is one big no-op. + * The fixed values (i.e., from SMF) have already been set in AD + * auto discovery, so if all values have been set in SMF and + * they haven't been changed or the service been refreshed then + * the rest of this loop's body is one big no-op. */ - pthread_mutex_lock(&handles->mutex); - + ad_disc_refresh(ad_ctx); new_cfg.default_domain = ad_disc_get_DomainName(ad_ctx); - if (new_cfg.default_domain == NULL) { + if (new_cfg.default_domain == NULL) idmapdlog(LOG_INFO, "%s: unable to discover Default Domain", me); - } new_cfg.domain_name = ad_disc_get_DomainName(ad_ctx); - if (new_cfg.domain_name == NULL) { + if (new_cfg.domain_name == NULL) idmapdlog(LOG_INFO, "%s: unable to discover Domain Name", me); - } new_cfg.domain_controller = ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE); - if (new_cfg.domain_controller == NULL) { + if (new_cfg.domain_controller == NULL) idmapdlog(LOG_INFO, "%s: unable to discover Domain Controller", me); - } new_cfg.forest_name = ad_disc_get_ForestName(ad_ctx); - if (new_cfg.forest_name == NULL) { + if (new_cfg.forest_name == NULL) idmapdlog(LOG_INFO, "%s: unable to discover Forest Name", me); - } new_cfg.site_name = ad_disc_get_SiteName(ad_ctx); - if (new_cfg.site_name == NULL) { + if (new_cfg.site_name == NULL) idmapdlog(LOG_INFO, "%s: unable to discover Site Name", me); - } new_cfg.global_catalog = ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE); if (new_cfg.global_catalog == NULL) { idmapdlog(LOG_INFO, "%s: unable to discover Global Catalog", me); + poke_is_interesting = 1; + } else { + poke_is_interesting = 0; } - pthread_mutex_unlock(&handles->mutex); - if (new_cfg.default_domain == NULL && new_cfg.domain_name == NULL && new_cfg.domain_controller == NULL && @@ -680,8 +673,6 @@ idmap_cfg_update_thread(void *arg) idmapdlog(LOG_NOTICE, "%s: Could not auto-discover AD " "domain and forest names nor domain controllers " "and global catalog servers", me); - idmap_cfg_unload(&new_cfg); - continue; } /* @@ -696,9 +687,14 @@ idmap_cfg_update_thread(void *arg) } /* + * Update running config and decide whether we want to call + * reload_ad() (i.e., if the default_domain changed or the GC + * list changed). + * * If default_domain came from SMF then we must not * auto-discover it. */ + changed = FALSE; if (live_cfg->dflt_dom_set_in_smf == FALSE && update_value(&live_cfg->default_domain, &new_cfg.default_domain, "default_domain") == TRUE) @@ -741,19 +737,52 @@ idmap_cfg_update_thread(void *arg) return (NULL); } - int -idmap_cfg_start_updates(idmap_cfg_t *cfg) +idmap_cfg_start_updates(void) { - /* Don't check for failure -- see wait_for_ttl() */ - hup_ev_port = port_create(); + const char *me = "idmap_cfg_start_updates"; - errno = pthread_create(&update_thread_handle, NULL, - idmap_cfg_update_thread, NULL); - if (errno == 0) - return (0); - else + if ((idmapd_ev_port = port_create()) < 0) { + idmapdlog(LOG_ERR, "%s: Failed to create event port: %s", + me, strerror(errno)); return (-1); + } + + if ((rt_sock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + idmapdlog(LOG_ERR, "%s: Failed to open routing socket: %s", + me, strerror(errno)); + (void) close(idmapd_ev_port); + return (-1); + } + + if (fcntl(rt_sock, F_SETFL, O_NDELAY|O_NONBLOCK) < 0) { + idmapdlog(LOG_ERR, "%s: Failed to set routing socket flags: %s", + me, strerror(errno)); + (void) close(rt_sock); + (void) close(idmapd_ev_port); + return (-1); + } + + if (port_associate(idmapd_ev_port, PORT_SOURCE_FD, + rt_sock, POLLIN, NULL) != 0) { + idmapdlog(LOG_ERR, "%s: Failed to associate the routing " + "socket with the event port: %s", me, strerror(errno)); + (void) close(rt_sock); + (void) close(idmapd_ev_port); + return (-1); + } + + if ((errno = pthread_create(&update_thread_handle, NULL, + idmap_cfg_update_thread, NULL)) != 0) { + idmapdlog(LOG_ERR, "%s: Failed to start update thread: %s", + me, strerror(errno)); + (void) port_dissociate(idmapd_ev_port, PORT_SOURCE_FD, rt_sock); + (void) close(rt_sock); + (void) close(idmapd_ev_port); + return (-1); + } + + return (0); } @@ -1161,3 +1190,16 @@ idmap_cfg_fini(idmap_cfg_t *cfg) return (0); } + +void +idmap_cfg_poke_updates(void) +{ + (void) port_send(idmapd_ev_port, POKE_AUTO_DISCOVERY, NULL); +} + +/*ARGSUSED*/ +void +idmap_cfg_hup_handler(int sig) { + if (idmapd_ev_port >= 0) + (void) port_send(idmapd_ev_port, RECONFIGURE, NULL); +} diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.h b/usr/src/cmd/idmap/idmapd/idmap_config.h index 28d3cd778c..bf3038fbca 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.h +++ b/usr/src/cmd/idmap/idmapd/idmap_config.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -94,7 +94,9 @@ extern int idmap_cfg_load(idmap_cfg_handles_t *, idmap_pg_config_t *, int); extern idmap_cfg_t *idmap_cfg_init(void); extern int idmap_cfg_fini(idmap_cfg_t *); -extern int idmap_cfg_start_updates(idmap_cfg_t *); +extern int idmap_cfg_start_updates(void); +extern void idmap_cfg_poke_updates(void); +extern void idmap_cfg_hup_handler(int); #ifdef __cplusplus } diff --git a/usr/src/cmd/idmap/idmapd/idmapd.c b/usr/src/cmd/idmap/idmapd/idmapd.c index b9e4791a22..98407ac1a1 100644 --- a/usr/src/cmd/idmap/idmapd/idmapd.c +++ b/usr/src/cmd/idmap/idmapd/idmapd.c @@ -55,7 +55,6 @@ #include <sys/sid.h> #include <sys/idmap.h> -static void hup_handler(int); static void term_handler(int); static void init_idmapd(); static void fini_idmapd(); @@ -71,9 +70,6 @@ int _rpcsvccount = 0; /* Number of requests being serviced */ mutex_t _svcstate_lock; /* lock for _rpcsvcstate, _rpcsvccount */ idmapd_state_t _idmapdstate; -int hupped; -extern int hup_ev_port; - SVCXPRT *xprt = NULL; static int dfd = -1; /* our door server fildes, for unregistration */ @@ -114,16 +110,6 @@ app_krb5_user_uid(void) /*ARGSUSED*/ static void -hup_handler(int sig) -{ - hupped = 1; - if (hup_ev_port >= 0) - (void) port_send(hup_ev_port, 1, &sig /* any ptr will do */); -} - - -/*ARGSUSED*/ -static void term_handler(int sig) { (void) idmapdlog(LOG_INFO, "idmapd: Terminating."); @@ -240,7 +226,7 @@ main(int argc, char **argv) /* signal handlers that should run only after we're initialized */ (void) sigset(SIGTERM, term_handler); - (void) sigset(SIGHUP, hup_handler); + (void) sigset(SIGHUP, idmap_cfg_hup_handler); if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, DAEMON_UID, DAEMON_GID, diff --git a/usr/src/cmd/idmap/idmapd/idmapd.h b/usr/src/cmd/idmap/idmapd/idmapd.h index 35ce9f806a..f39c028d3f 100644 --- a/usr/src/cmd/idmap/idmapd/idmapd.h +++ b/usr/src/cmd/idmap/idmapd/idmapd.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -51,7 +51,7 @@ extern "C" { #define _IDLE 0 #define _SERVED 1 -#define CHECK_NULL(s) s?s:"null" +#define CHECK_NULL(s) (s?s:"null") #define SENTINEL_PID UINT32_MAX @@ -146,6 +146,9 @@ typedef struct msg_table { const char *msg; } msg_table_t; +#define IDMAPD_SEARCH_TIMEOUT 3 /* seconds */ +#define IDMAPD_LDAP_OPEN_TIMEOUT 1 /* secs; initial, w/ exp backoff */ + /* * The following flags are used by idmapd while processing a * given mapping request. Note that idmapd uses multiple passes to @@ -193,10 +196,10 @@ typedef struct msg_table { batch.idmap_mapping_batch_val[i].id1.idtype == IDMAP_GSID) #define IS_BATCH_UID(batch, i) \ - batch.idmap_mapping_batch_val[i].id1.idtype == IDMAP_UID + (batch.idmap_mapping_batch_val[i].id1.idtype == IDMAP_UID) #define IS_BATCH_GID(batch, i) \ - batch.idmap_mapping_batch_val[i].id1.idtype == IDMAP_GID + (batch.idmap_mapping_batch_val[i].id1.idtype == IDMAP_GID) #define IS_REQUEST_SID(req, n) \ ((req).id##n.idtype == IDMAP_SID || \ @@ -205,10 +208,10 @@ typedef struct msg_table { #define IS_REQUEST_UID(request) \ - (request).id1.idtype == IDMAP_UID + ((request).id1.idtype == IDMAP_UID) #define IS_REQUEST_GID(request) \ - (request).id1.idtype == IDMAP_GID + ((request).id1.idtype == IDMAP_GID) typedef idmap_retcode (*update_list_res_cb)(void *, const char **, uint64_t); typedef int (*list_svc_cb)(void *, int, char **, char **); @@ -222,7 +225,7 @@ extern int create_directory(const char *, uid_t, gid_t); extern int load_config(); extern int reload_ad(); extern int idmap_init_tsd_key(void); -extern void degrade_svc(void); +extern void degrade_svc(const char *); extern void restore_svc(void); diff --git a/usr/src/cmd/idmap/idmapd/init.c b/usr/src/cmd/idmap/idmapd/init.c index ce116b83be..36bcd9c484 100644 --- a/usr/src/cmd/idmap/idmapd/init.c +++ b/usr/src/cmd/idmap/idmapd/init.c @@ -76,8 +76,7 @@ load_config() int rc; idmap_pg_config_t *pgcfg; if ((_idmapdstate.cfg = idmap_cfg_init()) == NULL) { - idmapdlog(LOG_ERR, "%s: failed to initialize config", me); - degrade_svc(); + degrade_svc("failed to initialize config"); return (-1); } pgcfg = &_idmapdstate.cfg->pgcfg; @@ -86,9 +85,7 @@ load_config() &_idmapdstate.cfg->pgcfg, 0); if (rc < -1) { /* Total failure */ - degrade_svc(); - idmapdlog(LOG_ERR, "%s: Fatal error while loading " - "configuration", me); + degrade_svc("fatal error while loading configuration"); return (rc); } @@ -99,23 +96,24 @@ load_config() if (pgcfg->global_catalog == NULL || pgcfg->global_catalog[0].host[0] == '\0') { - degrade_svc(); - idmapdlog(LOG_INFO, - "%s: Global catalog server is not configured; AD lookup " + degrade_svc( + "global catalog server is not configured; AD lookup " "will fail until one or more global catalog server names " "are configured or discovered; auto-discovery will begin " - "shortly", me); + "shortly"); } else { restore_svc(); } (void) reload_ad(); - if (idmap_cfg_start_updates(_idmapdstate.cfg) < 0) - idmapdlog(LOG_ERR, "%s: could not start config updater", - me); + if ((rc = idmap_cfg_start_updates()) < 0) { + /* Total failure */ + degrade_svc("could not start config updater"); + return (rc); + } - idmapdlog(LOG_DEBUG, "%s: initial configuration loaded", me); + idmapdlog(LOG_DEBUG, "%s: Initial configuration loaded", me); return (0); } @@ -143,9 +141,7 @@ reload_ad() if (idmap_ad_alloc(&new, pgcfg->default_domain, IDMAP_AD_GLOBAL_CATALOG) != 0) { - if (old == NULL) - degrade_svc(); - idmapdlog(LOG_ERR, "%s: could not initialize AD context", me); + degrade_svc("could not initialize AD context"); return (-1); } @@ -154,10 +150,7 @@ reload_ad() pgcfg->global_catalog[i].host, pgcfg->global_catalog[i].port) != 0) { idmap_ad_free(&new); - if (old == NULL) - degrade_svc(); - idmapdlog(LOG_ERR, - "%s: could not initialize AD DS context", me); + degrade_svc("could not initialize AD GC context"); return (-1); } } diff --git a/usr/src/cmd/idmap/idmapd/server.c b/usr/src/cmd/idmap/idmapd/server.c index d8808426a4..052f6b16ae 100644 --- a/usr/src/cmd/idmap/idmapd/server.c +++ b/usr/src/cmd/idmap/idmapd/server.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,6 +44,7 @@ #include <pwd.h> #include <auth_attr.h> #include <secdb.h> +#include <sys/u8_textprep.h> #define _VALIDATE_LIST_CB_DATA(col, val, siz)\ retcode = validate_list_cb_data(cb_data, argc, argv, col,\ @@ -98,6 +99,66 @@ sanitize_mapping_request(idmap_mapping *req) req->direction = _IDMAP_F_DONE; } +static +int +validate_mapped_id_by_name_req(idmap_mapping *req) +{ + int e; + + if (IS_REQUEST_UID(*req) || IS_REQUEST_GID(*req)) + return (IDMAP_SUCCESS); + + if (IS_REQUEST_SID(*req, 1)) { + if (!EMPTY_STRING(req->id1name) && + u8_validate(req->id1name, strlen(req->id1name), + NULL, U8_VALIDATE_ENTIRE, &e) < 0) + return (IDMAP_ERR_BAD_UTF8); + if (!EMPTY_STRING(req->id1domain) && + u8_validate(req->id1domain, strlen(req->id1domain), + NULL, U8_VALIDATE_ENTIRE, &e) < 0) + return (IDMAP_ERR_BAD_UTF8); + } + + return (IDMAP_SUCCESS); +} + +static +int +validate_rule(idmap_namerule *rule) +{ + int e; + + if (!EMPTY_STRING(rule->winname) && + u8_validate(rule->winname, strlen(rule->winname), + NULL, U8_VALIDATE_ENTIRE, &e) < 0) + return (IDMAP_ERR_BAD_UTF8); + + if (!EMPTY_STRING(rule->windomain) && + u8_validate(rule->windomain, strlen(rule->windomain), + NULL, U8_VALIDATE_ENTIRE, &e) < 0) + return (IDMAP_ERR_BAD_UTF8); + + return (IDMAP_SUCCESS); + +} + +static +bool_t +validate_rules(idmap_update_batch *batch) +{ + idmap_update_op *up; + int i; + + for (i = 0; i < batch->idmap_update_batch_len; i++) { + up = &(batch->idmap_update_batch_val[i]); + if (validate_rule(&(up->idmap_update_op_u.rule)) + != IDMAP_SUCCESS) + return (IDMAP_ERR_BAD_UTF8); + } + + return (IDMAP_SUCCESS); +} + /* ARGSUSED */ bool_t idmap_get_mapped_ids_1_svc(idmap_mapping_batch batch, @@ -560,6 +621,10 @@ idmap_list_namerules_1_svc(idmap_namerule rule, uint64_t lastrowid, (void) memset(result, 0, sizeof (*result)); lbuf[0] = rbuf[0] = 0; + result->retcode = validate_rule(&rule); + if (result->retcode != IDMAP_SUCCESS) + goto out; + RDLOCK_CONFIG(); maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit; UNLOCK_CONFIG(); @@ -585,8 +650,8 @@ idmap_list_namerules_1_svc(idmap_namerule rule, uint64_t lastrowid, (void) snprintf(u2wbuf, sizeof (u2wbuf), "AND u2w_order > 0"); } - retcode = gen_sql_expr_from_rule(&rule, &expr); - if (retcode != IDMAP_SUCCESS) + result->retcode = gen_sql_expr_from_rule(&rule, &expr); + if (result->retcode != IDMAP_SUCCESS) goto out; /* Create LIMIT expression. */ @@ -707,6 +772,10 @@ idmap_update_1_svc(idmap_update_batch batch, idmap_update_res *res, goto out; } + res->retcode = validate_rules(&batch); + if (res->retcode != IDMAP_SUCCESS) + goto out; + /* Get db handle */ res->retcode = get_db_handle(&db); if (res->retcode != IDMAP_SUCCESS) @@ -781,6 +850,10 @@ idmap_get_mapped_id_by_name_1_svc(idmap_mapping request, /* Init */ (void) memset(result, 0, sizeof (*result)); + result->retcode = validate_mapped_id_by_name_req(&request); + if (result->retcode != IDMAP_SUCCESS) + goto out; + /* Get cache handle */ result->retcode = get_cache_handle(&cache); if (result->retcode != IDMAP_SUCCESS) diff --git a/usr/src/lib/libidmap/common/idmap_api.c b/usr/src/lib/libidmap/common/idmap_api.c index 1eb5095385..5dfeb56116 100644 --- a/usr/src/lib/libidmap/common/idmap_api.c +++ b/usr/src/lib/libidmap/common/idmap_api.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1701,6 +1701,9 @@ static stat_table_t stattable[] = { {IDMAP_ERR_U2W_NAMERULE_CONFLICT, gettext("Duplicate rule or conflicts with an existing " "Unix to Windows name-based rule"), EINVAL}, + {IDMAP_ERR_BAD_UTF8, + gettext("Invalid or illegal UTF-8 sequence found in " + "a given Windows entity name or domain name"), EINVAL}, {-1, NULL, 0} }; #undef gettext diff --git a/usr/src/lib/libidmap/common/idmap_priv.h b/usr/src/lib/libidmap/common/idmap_priv.h index 813db74da6..8e92c21763 100644 --- a/usr/src/lib/libidmap/common/idmap_priv.h +++ b/usr/src/lib/libidmap/common/idmap_priv.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -41,9 +41,9 @@ extern "C" { #define IDMAP_MAX_NAME_LEN 512 -#define IDMAP_ERROR(rc) rc != IDMAP_SUCCESS && rc != IDMAP_NEXT -#define IDMAP_FATAL_ERROR(rc) rc == IDMAP_ERR_MEMORY ||\ - rc == IDMAP_ERR_DB +#define IDMAP_ERROR(rc) (rc != IDMAP_SUCCESS && rc != IDMAP_NEXT) +#define IDMAP_FATAL_ERROR(rc) (rc == IDMAP_ERR_MEMORY ||\ + rc == IDMAP_ERR_DB) /* Direction in which mapping is valid */ #define IDMAP_DIRECTION_UNDEF -1 /* not defined */ diff --git a/usr/src/uts/common/sys/idmap.h b/usr/src/uts/common/sys/idmap.h index 8993de65d9..a42da84766 100644 --- a/usr/src/uts/common/sys/idmap.h +++ b/usr/src/uts/common/sys/idmap.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -63,6 +63,7 @@ #define IDMAP_ERR_RETRIABLE_NET_ERR -9971 #define IDMAP_ERR_W2U_NAMERULE_CONFLICT -9970 #define IDMAP_ERR_U2W_NAMERULE_CONFLICT -9969 +#define IDMAP_ERR_BAD_UTF8 -9968 /* Reserved GIDs for some well-known SIDs */ #define IDMAP_WK_LOCAL_SYSTEM_GID 2147483648U |