summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authornw141292 <none@none>2008-02-04 15:57:37 -0800
committernw141292 <none@none>2008-02-04 15:57:37 -0800
commit0dcc71495bad040a0c83830efc85acf8d897350d (patch)
treecd85551cfd283f7a9e9b29d0d2a928acd83f588d /usr
parentcbfb650a37bf2b1cd913645ee5ab0d1a13ef6246 (diff)
downloadillumos-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.c335
-rw-r--r--usr/src/cmd/idmap/idmapd/adutils.c122
-rw-r--r--usr/src/cmd/idmap/idmapd/adutils.h5
-rw-r--r--usr/src/cmd/idmap/idmapd/dbutils.c54
-rw-r--r--usr/src/cmd/idmap/idmapd/idmap_config.c338
-rw-r--r--usr/src/cmd/idmap/idmapd/idmap_config.h6
-rw-r--r--usr/src/cmd/idmap/idmapd/idmapd.c16
-rw-r--r--usr/src/cmd/idmap/idmapd/idmapd.h17
-rw-r--r--usr/src/cmd/idmap/idmapd/init.c33
-rw-r--r--usr/src/cmd/idmap/idmapd/server.c79
-rw-r--r--usr/src/lib/libidmap/common/idmap_api.c5
-rw-r--r--usr/src/lib/libidmap/common/idmap_priv.h8
-rw-r--r--usr/src/uts/common/sys/idmap.h3
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