Mercurial > illumos > illumos-gate
changeset 6666:b61097837011
6633347 nscd (sparks) can give inconsistent name resolution if started without a resolv.conf file
author | sm26363 |
---|---|
date | Tue, 20 May 2008 09:43:20 -0700 |
parents | 1515bc919a93 |
children | 2d6c366a5d6c |
files | usr/src/cmd/nscd/nscd_frontend.c usr/src/cmd/nscd/nscd_switch.c usr/src/lib/nsswitch/dns/common/dns_common.c |
diffstat | 3 files changed, 127 insertions(+), 36 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/nscd/nscd_frontend.c Tue May 20 07:37:47 2008 -0700 +++ b/usr/src/cmd/nscd/nscd_frontend.c Tue May 20 09:43:20 2008 -0700 @@ -171,6 +171,24 @@ /* not much here */ } +/* + * _nscd_restart_if_cfgfile_changed() + * Restart if modification times of nsswitch.conf or resolv.conf have changed. + * + * If nsswitch.conf has changed then it is possible that sources for + * various backends have changed and therefore the current cached + * data may not be consistent with the new data sources. By + * restarting the cache will be cleared and the new configuration will + * be used. + * + * The check for resolv.conf is made as only the first call to + * res_gethostbyname() or res_getaddrbyname() causes a call to + * res_ninit() to occur which in turn parses resolv.conf. Therefore + * to benefit from changes to resolv.conf nscd must be restarted when + * resolv.conf is updated, removed or created. If res_getXbyY calls + * are removed from NSS then this check could be removed. + * + */ void _nscd_restart_if_cfgfile_changed() { @@ -178,13 +196,22 @@ static mutex_t nsswitch_lock = DEFAULTMUTEX; static timestruc_t last_nsswitch_check = { 0 }; static timestruc_t last_nsswitch_modified = { 0 }; - static timestruc_t last_resolv_modified = { 0 }; + static timestruc_t last_resolv_modified = { -1, 0 }; static mutex_t restarting_lock = DEFAULTMUTEX; static int restarting = 0; int restart = 0; time_t now = time(NULL); char *me = "_nscd_restart_if_cfgfile_changed"; +#define FLAG_RESTART_REQUIRED if (restarting == 0) {\ + (void) mutex_lock(&restarting_lock);\ + if (restarting == 0) {\ + restarting = 1;\ + restart = 1;\ + }\ + (void) mutex_unlock(&restarting_lock);\ + } + if (restarting == 1) return; @@ -202,24 +229,6 @@ (void) mutex_unlock(&nsswitch_lock); /* let others continue */ - /* - * This code keeps us from statting resolv.conf - * if it doesn't exist, yet prevents us from ignoring - * it if it happens to disappear later on for a bit. - */ - - if (last_resolv_modified.tv_sec >= 0) { - if (stat("/etc/resolv.conf", &res_buf) < 0) { - if (last_resolv_modified.tv_sec == 0) { - last_resolv_modified.tv_sec = -1; - last_resolv_modified.tv_nsec = 0; - } else - res_buf.st_mtim = last_resolv_modified; - } else if (last_resolv_modified.tv_sec == 0) { - last_resolv_modified = res_buf.st_mtim; - } - } - if (stat("/etc/nsswitch.conf", &nss_buf) < 0) { return; } else if (last_nsswitch_modified.tv_sec == 0) { @@ -228,19 +237,33 @@ if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec || (last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec && - last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec) || - (last_resolv_modified.tv_sec > 0 && - (last_resolv_modified.tv_sec < res_buf.st_mtim.tv_sec || - (last_resolv_modified.tv_sec == res_buf.st_mtim.tv_sec && - last_resolv_modified.tv_nsec < res_buf.st_mtim.tv_nsec)))) { + last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) { + FLAG_RESTART_REQUIRED; + } - if (restarting == 0) { - (void) mutex_lock(&restarting_lock); - if (restarting == 0) { - restarting = 1; - restart = 1; - } - (void) mutex_unlock(&restarting_lock); + if (restart == 0) { + if (stat("/etc/resolv.conf", &res_buf) < 0) { + /* Unable to stat file, were we previously? */ + if (last_resolv_modified.tv_sec > 0) { + /* Yes, it must have been removed. */ + FLAG_RESTART_REQUIRED; + } else if (last_resolv_modified.tv_sec == -1) { + /* No, then we've never seen it. */ + last_resolv_modified.tv_sec = 0; + } + } else if (last_resolv_modified.tv_sec == -1) { + /* We've just started and file is present. */ + last_resolv_modified = res_buf.st_mtim; + } else if (last_resolv_modified.tv_sec == 0) { + /* Wasn't there at start-up. */ + FLAG_RESTART_REQUIRED; + } else if (last_resolv_modified.tv_sec < + res_buf.st_mtim.tv_sec || + (last_resolv_modified.tv_sec == + res_buf.st_mtim.tv_sec && + last_resolv_modified.tv_nsec < + res_buf.st_mtim.tv_nsec)) { + FLAG_RESTART_REQUIRED; } }
--- a/usr/src/cmd/nscd/nscd_switch.c Tue May 20 07:37:47 2008 -0700 +++ b/usr/src/cmd/nscd/nscd_switch.c Tue May 20 09:43:20 2008 -0700 @@ -377,8 +377,9 @@ } _nscd_logit(me, "%s: database: %s, operation: %d, source: %s, " - "erange= %d, errno: %s \n", - res_str, db, op, src, arg->erange, strerror(arg->h_errno)); + "erange= %d, herrno: %s (%d)\n", + res_str, db, op, src, arg->erange, hstrerror(arg->h_errno), + arg->h_errno); } /*
--- a/usr/src/lib/nsswitch/dns/common/dns_common.c Tue May 20 07:37:47 2008 -0700 +++ b/usr/src/lib/nsswitch/dns/common/dns_common.c Tue May 20 09:43:20 2008 -0700 @@ -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,10 @@ #define DNS_ADDRLIST 1 #define DNS_MAPDLIST 2 +#ifndef tolower +#define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20 : (c)) +#endif + static int dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type) char **from_list, **to_list, **aliaspp; @@ -283,6 +287,55 @@ } /* + * name_is_alias(aliases_ptr, name_ptr) + * Verify name matches an alias in the provided aliases list. + * + * Within DNS there should be only one canonical name, aliases should + * all refer to the one canonical. However alias chains do occur and + * pre BIND 9 servers may also respond with multiple CNAMEs. This + * routine checks if a given name has been provided as a CNAME in the + * response. This assumes that the chains have been sent in-order. + * + * INPUT: + * aliases_ptr: space separated list of alias names. + * name_ptr: name to look for in aliases_ptr list. + * RETURNS: NSS_SUCCESS or NSS_ERROR + * NSS_SUCCESS indicates that the name is listed in the collected aliases. + */ +static nss_status_t +name_is_alias(char *aliases_ptr, char *name_ptr) { + char *host_ptr; + /* Loop through alias string and compare it against host string. */ + while (*aliases_ptr != '\0') { + host_ptr = name_ptr; + + /* Compare name with alias. */ + while (tolower(*host_ptr) == tolower(*aliases_ptr) && + *host_ptr != '\0') { + host_ptr++; + aliases_ptr++; + } + + /* + * If name was exhausted and the next character in the + * alias is either the end-of-string or space + * character then we have a match. + */ + if (*host_ptr == '\0' && + (*aliases_ptr == '\0' || *aliases_ptr == ' ')) { + return (NSS_SUCCESS); + } + + /* Alias did not match, step over remainder of alias. */ + while (*aliases_ptr != ' ' && *aliases_ptr != '\0') + aliases_ptr++; + /* Step over separator character. */ + while (*aliases_ptr == ' ') aliases_ptr++; + } + return (NSS_ERROR); +} + +/* * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) * nss2 get hosts/ipnodes with ttl backend DNS search engine. * @@ -425,7 +478,12 @@ while (ancount-- > 0 && cp < eom && blen < bsize) { n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN); if (n > 0) { - if (strncasecmp(host, ans, hlen) != 0) { + /* + * Check that the expanded name is either the + * name we asked for or a learned alias. + */ + if (strncasecmp(host, ans, hlen) != 0 && (alen == 0 || + name_is_alias(aliases, ans) == NSS_ERROR)) { __res_ndestroy(statp); return (NSS_ERROR); /* spoof? */ } @@ -448,7 +506,16 @@ } eor = cp + n; if (type == T_CNAME) { - /* add an alias to the alias list */ + /* + * The name we looked up is really an alias + * and the canonical name should be in the + * RDATA. A canonical name may have several + * aliases but an alias should only have one + * canonical name. However multiple CNAMEs and + * CNAME chains do exist! So for caching + * purposes maintain the alias as the host + * name, and the CNAME as an alias. + */ n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN); if (n > 0) { len = strlen(aname);