From e34a049628489ad4ace1bb2ba5600b179b409666 Mon Sep 17 00:00:00 2001 From: Erez Zadok Date: Tue, 15 Mar 2005 04:14:57 +0000 Subject: [PATCH] * amd/info_ldap.c (and others): backport of LDAP support from 6.1 to 6.0, suggestion and patch from Jim Zajkowski . --- AUTHORS | 3 +- ChangeLog | 4 + amd/amd.c | 4 +- amd/amd.h | 3 +- amd/conf.c | 42 ++++++++++- amd/info_ldap.c | 193 ++++++++++++++++++++++++++++++++++-------------- 6 files changed, 189 insertions(+), 60 deletions(-) diff --git a/AUTHORS b/AUTHORS index 708cc23..715b619 100644 --- a/AUTHORS +++ b/AUTHORS @@ -352,4 +352,5 @@ to "/tmp/mtab" to avoid security problem. Bug fixed to ensure that Amd terminates properly even mtab file doesn't exist. * Jim Zajkowski -March 14, 2005: small patch to amd2ldif. +March 14, 2005: small patch to amd2ldif, plus backport of LDAP support from +6.1 branch. diff --git a/ChangeLog b/ChangeLog index 0742a65..587ba60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2005-03-14 Erez Zadok + * amd/info_ldap.c (and others): backport of LDAP support from 6.1 + to 6.0, suggestion and patch from Jim Zajkowski + . + * scripts/amd2ldif.in: patch to add the amdMapName attribute to the amdMapTimestamp object when amd2ldif converts it. This is necessary if you are using type:=auto mounts and want those diff --git a/amd/amd.c b/amd/amd.c index 81a0528..6b41eb8 100644 --- a/amd/amd.c +++ b/amd/amd.c @@ -38,7 +38,7 @@ * * %W% (Berkeley) %G% * - * $Id: amd.c,v 1.8.2.7 2005/01/03 20:56:11 ezk Exp $ + * $Id: amd.c,v 1.8.2.8 2005/03/15 04:14:57 ezk Exp $ * */ @@ -293,6 +293,8 @@ init_global_options(void) /* LDAP cache */ gopt.ldap_cache_seconds = 0; gopt.ldap_cache_maxmem = 131072; + + gopt.ldap_proto_version = 2; #endif /* HAVE_MAP_LDAP */ #ifdef HAVE_MAP_NIS diff --git a/amd/amd.h b/amd/amd.h index 7e7e5ff..1a39c76 100644 --- a/amd/amd.h +++ b/amd/amd.h @@ -38,7 +38,7 @@ * * %W% (Berkeley) %G% * - * $Id: amd.h,v 1.8.2.11 2005/03/04 18:42:04 ezk Exp $ + * $Id: amd.h,v 1.8.2.12 2005/03/15 04:14:57 ezk Exp $ * */ @@ -140,6 +140,7 @@ struct amu_global_options { char *ldap_hostports; /* LDAP host ports */ long ldap_cache_seconds; /* LDAP internal cache - keep seconds */ long ldap_cache_maxmem; /* LDAP internal cache - max memory (bytes) */ + long ldap_proto_version; #endif /* HAVE_MAP_LDAP */ #ifdef HAVE_MAP_NIS char *nis_domain; /* YP domain name */ diff --git a/amd/conf.c b/amd/conf.c index 72b3b19..f0f1b94 100644 --- a/amd/conf.c +++ b/amd/conf.c @@ -38,7 +38,7 @@ * * %W% (Berkeley) %G% * - * $Id: conf.c,v 1.7.2.10 2005/03/04 18:42:04 ezk Exp $ + * $Id: conf.c,v 1.7.2.11 2005/03/15 04:14:57 ezk Exp $ * */ @@ -91,6 +91,7 @@ static int gopt_ldap_base(const char *val); static int gopt_ldap_cache_maxmem(const char *val); static int gopt_ldap_cache_seconds(const char *val); static int gopt_ldap_hostports(const char *val); +static int gopt_ldap_proto_version(const char *val); static int gopt_local_domain(const char *val); static int gopt_log_file(const char *val); static int gopt_log_options(const char *val); @@ -151,6 +152,7 @@ static struct _func_map glob_functable[] = { {"ldap_cache_maxmem", gopt_ldap_cache_maxmem}, {"ldap_cache_seconds", gopt_ldap_cache_seconds}, {"ldap_hostports", gopt_ldap_hostports}, + {"ldap_proto_version", gopt_ldap_proto_version}, {"local_domain", gopt_local_domain}, {"log_file", gopt_log_file}, {"log_options", gopt_log_options}, @@ -535,6 +537,44 @@ gopt_ldap_hostports(const char *val) } +static int +gopt_ldap_proto_version(const char *val) +{ +#ifdef HAVE_MAP_LDAP + char *end; + + gopt.ldap_proto_version = strtol((char *)val, &end, 10); + if (end == val) { + fprintf(stderr, "conf: bad ldap_proto_version option: %s\n",val); + return 1; + } + + if (gopt.ldap_proto_version < 0 || gopt.ldap_proto_version > LDAP_VERSION_MAX) { + fprintf(stderr, "conf: bad ldap_proto_version option value: %s\n",val); + return 1; + } + switch (gopt.ldap_proto_version) { + /* XXX: what about LDAP_VERSION1? */ + case LDAP_VERSION2: +#ifdef LDAP_VERSION3 + case LDAP_VERSION3: +#endif /* LDAP_VERSION3 */ +#ifdef LDAP_VERSION4 + case LDAP_VERSION4: +#endif /* LDAP_VERSION4 */ + break; + default: + fprintf(stderr, "conf: unsupported ldap_proto_version option value: %s\n",val); + return 1; + } + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_proto_version option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ +} + + static int gopt_log_file(const char *val) diff --git a/amd/info_ldap.c b/amd/info_ldap.c index 7cd800a..23095ea 100644 --- a/amd/info_ldap.c +++ b/amd/info_ldap.c @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_ldap.c,v 1.9.2.11 2005/01/16 23:56:32 ezk Exp $ + * $Id: info_ldap.c,v 1.9.2.12 2005/03/15 04:14:57 ezk Exp $ * */ @@ -109,7 +108,7 @@ struct he_ent { * FORWARD DECLARATIONS: */ static int amu_ldap_rebind(ALD *a); -static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts); +static int get_ldap_timestamp(ALD *a, char *map, time_t *ts); /* @@ -167,13 +166,54 @@ cr_free(CR *c) } +/* + * Special ldap_unbind function to handle SIGPIPE. + * We first ignore SIGPIPE, in case a remote LDAP server was + * restarted, then we reinstall the handler. + */ +static int +amu_ldap_unbind(LDAP *ld) +{ + int e; +#ifdef HAVE_SIGACTION + struct sigaction sa; +#else /* not HAVE_SIGACTION */ + void (*handler)(int); +#endif /* not HAVE_SIGACTION */ + + dlog("amu_ldap_unbind()\n"); + +#ifdef HAVE_SIGACTION + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGPIPE); + sigaction(SIGPIPE, &sa, &sa); /* set IGNORE, and get old action */ +#else /* not HAVE_SIGACTION */ + handler = signal(SIGPIPE, SIG_IGN); +#endif /* not HAVE_SIGACTION */ + + e = ldap_unbind(ld); + +#ifdef HAVE_SIGACTION + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGPIPE); + sigaction(SIGPIPE, &sa, NULL); +#else /* not HAVE_SIGACTION */ + (void) signal(SIGPIPE, handler); +#endif /* not HAVE_SIGACTION */ + + return e; +} + + static void ald_free(ALD *a) { he_free(a->hostent); cr_free(a->credentials); if (a->ldap != NULL) - ldap_unbind(a->ldap); + amu_ldap_unbind(a->ldap); XFREE(a); } @@ -184,23 +224,23 @@ amu_ldap_init(mnt_map *m, char *map, time_t *ts) ALD *aldh; CR *creds; + dlog("-> amu_ldap_init: map <%s>\n", map); + /* * XXX: by checking that map_type must be defined, aren't we * excluding the possibility of automatic searches through all * map types? */ if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) { - return (ENOENT); - } -#ifdef DEBUG - else { + plog(XLOG_WARNING, "amu_ldap_init called with map_type <%s>\n", + (gopt.map_type ? gopt.map_type : "null")); + } else { dlog("Map %s is ldap\n", map); } -#endif /* DEBUG */ aldh = ALLOC(ALD); creds = ALLOC(CR); - aldh->ldap = NULL ; + aldh->ldap = NULL; aldh->hostent = string2he(gopt.ldap_hostports); if (aldh->hostent == NULL) { plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s", @@ -212,22 +252,17 @@ amu_ldap_init(mnt_map *m, char *map, time_t *ts) creds->method = LDAP_AUTH_SIMPLE; aldh->credentials = creds; aldh->timestamp = 0; -#ifdef DEBUG + aldh->ldap = NULL; dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port); -#endif /* DEBUG */ if (amu_ldap_rebind(aldh)) { ald_free(aldh); return (ENOENT); } m->map_data = (void *) aldh; -#ifdef DEBUG dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port); -#endif /* DEBUG */ - if (get_ldap_timestamp(aldh->ldap, map, ts)) + if (get_ldap_timestamp(aldh, map, ts)) return (ENOENT); -#ifdef DEBUG dlog("Got timestamp for map %s: %ld\n", map, *ts); -#endif /* DEBUG */ return (0); } @@ -242,13 +277,18 @@ amu_ldap_rebind(ALD *a) time_t now = clocktime(); int try; + dlog("-> amu_ldap_rebind\n"); + if (a->ldap != NULL) { if ((a->timestamp - now) > AMD_LDAP_TTL) { -#ifdef DEBUG - dlog("Reestablishing ldap connection\n"); -#endif /* DEBUG */ - ldap_unbind(a->ldap); + dlog("Re-establishing ldap connection\n"); + amu_ldap_unbind(a->ldap); a->timestamp = now; + a->ldap = NULL; + } else { + /* Assume all is OK. If it wasn't we'll be back! */ + dlog("amu_ldap_rebind: timestamp OK\n"); + return (0); } } @@ -258,6 +298,19 @@ amu_ldap_rebind(ALD *a) plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port); break; } +#if LDAP_VERSION_MAX > LDAP_VERSION2 + /* handle LDAPv3 and heigher, if available and amd.conf-igured */ + if (gopt.ldap_proto_version > LDAP_VERSION2) { + if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) { + dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n", + gopt.ldap_proto_version); + } else { + plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld\n", + gopt.ldap_proto_version); + break; + } + } +#endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */ if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) { plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n", h->host, h->port, c->who); @@ -269,10 +322,10 @@ amu_ldap_rebind(ALD *a) #else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */ plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds); #endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */ - a->ldap = ld; - a->timestamp = now; - return (0); } + a->ldap = ld; + a->timestamp = now; + return (0); } plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n"); } @@ -283,24 +336,24 @@ amu_ldap_rebind(ALD *a) static int -get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) +get_ldap_timestamp(ALD *a, char *map, time_t *ts) { struct timeval tv; char **vals, *end; char filter[MAXPATHLEN]; int i, err = 0, nentries = 0; - LDAPMessage *res, *entry; + LDAPMessage *res = NULL, *entry; + + dlog("-> get_ldap_timestamp: map <%s>\n", map); tv.tv_sec = 3; tv.tv_usec = 0; sprintf(filter, AMD_LDAP_TSFILTER, map); -#ifdef DEBUG dlog("Getting timestamp for map %s\n", map); dlog("Filter is: %s\n", filter); dlog("Base is: %s\n", gopt.ldap_base); -#endif /* DEBUG */ for (i = 0; i < AMD_LDAP_RETRIES; i++) { - err = ldap_search_st(ld, + err = ldap_search_st(a->ldap, gopt.ldap_base, LDAP_SCOPE_SUBTREE, filter, @@ -310,19 +363,32 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) &res); if (err == LDAP_SUCCESS) break; -#ifdef DEBUG - dlog("Timestamp search timed out, trying again...\n"); -#endif /* DEBUG */ + if (res) { + ldap_msgfree(res); + res = NULL; + } + plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n", + i + 1, ldap_err2string(err)); + if (err != LDAP_TIMEOUT) { + dlog("get_ldap_timestamp: unbinding...\n"); + amu_ldap_unbind(a->ldap); + a->ldap = NULL; + if (amu_ldap_rebind(a)) + return (ENOENT); + } + dlog("Timestamp search failed, trying again...\n"); } if (err != LDAP_SUCCESS) { *ts = 0; plog(XLOG_USER, "LDAP timestamp search failed: %s\n", ldap_err2string(err)); + if (res) + ldap_msgfree(res); return (ENOENT); } - nentries = ldap_count_entries(ld, res); + nentries = ldap_count_entries(a->ldap, res); if (nentries == 0) { plog(XLOG_USER, "No timestamp entry for map %s\n", map); *ts = 0; @@ -330,8 +396,8 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) return (ENOENT); } - entry = ldap_first_entry(ld, res); - vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR); + entry = ldap_first_entry(a->ldap, res); + vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR); if (ldap_count_values(vals) == 0) { plog(XLOG_USER, "Missing timestamp value for map %s\n", map); *ts = 0; @@ -339,9 +405,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) ldap_msgfree(res); return (ENOENT); } -#ifdef DEBUG dlog("TS value is:%s:\n", vals[0]); -#endif /* DEBUG */ if (vals[0]) { *ts = (time_t) strtol(vals[0], &end, 10); @@ -363,9 +427,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) ldap_value_free(vals); ldap_msgfree(res); -#ifdef DEBUG dlog("The timestamp for %s is %ld (err=%d)\n", map, *ts, err); -#endif /* DEBUG */ return (err); } @@ -373,12 +435,15 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) int amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) { - char **vals, filter[MAXPATHLEN]; + char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN]; + char *f1, *f2; struct timeval tv; int i, err = 0, nvals = 0, nentries = 0; - LDAPMessage *entry, *res; + LDAPMessage *entry, *res = NULL; ALD *a = (ALD *) (m->map_data); + dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key); + tv.tv_sec = 2; tv.tv_usec = 0; if (a == NULL) { @@ -389,42 +454,62 @@ amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) return (ENOENT); sprintf(filter, AMD_LDAP_FILTER, map, key); -#ifdef DEBUG - dlog("Search with filter: %s\n", filter); -#endif /* DEBUG */ + /* "*" is special to ldap_search(); run through the filter escaping it. */ + f1 = filter; f2 = filter2; + while (*f1) { + if (*f1 == '*') { + *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a'; + f1++; + } else { + *f2++ = *f1++; + } + } + *f2 = '\0'; + dlog("Search with filter: <%s>\n", filter2); for (i = 0; i < AMD_LDAP_RETRIES; i++) { err = ldap_search_st(a->ldap, gopt.ldap_base, LDAP_SCOPE_SUBTREE, - filter, + filter2, 0, 0, &tv, &res); if (err == LDAP_SUCCESS) break; + if (res) { + ldap_msgfree(res); + res = NULL; + } + plog(XLOG_USER, "LDAP search attempt %d failed: %s\n", + i + 1, ldap_err2string(err)); + if (err != LDAP_TIMEOUT) { + dlog("amu_ldap_search: unbinding...\n"); + amu_ldap_unbind(a->ldap); + a->ldap = NULL; + if (amu_ldap_rebind(a)) + return (ENOENT); + } } switch (err) { case LDAP_SUCCESS: break; case LDAP_NO_SUCH_OBJECT: -#ifdef DEBUG dlog("No object\n"); -#endif /* DEBUG */ - ldap_msgfree(res); + if (res) + ldap_msgfree(res); return (ENOENT); default: plog(XLOG_USER, "LDAP search failed: %s\n", ldap_err2string(err)); - ldap_msgfree(res); + if (res) + ldap_msgfree(res); return (EIO); } nentries = ldap_count_entries(a->ldap, res); -#ifdef DEBUG dlog("Search found %d entries\n", nentries); -#endif /* DEBUG */ if (nentries == 0) { ldap_msgfree(res); return (ENOENT); @@ -438,9 +523,7 @@ amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) ldap_msgfree(res); return (EIO); } -#ifdef DEBUG dlog("Map %s, %s => %s\n", map, key, vals[0]); -#endif /* DEBUG */ if (vals[0]) { *pval = strdup(vals[0]); err = 0; @@ -461,15 +544,13 @@ amu_ldap_mtime(mnt_map *m, char *map, time_t *ts) ALD *aldh = (ALD *) (m->map_data); if (aldh == NULL) { -#ifdef DEBUG dlog("LDAP panic: unable to find map data\n"); -#endif /* DEBUG */ return (ENOENT); } if (amu_ldap_rebind(aldh)) { return (ENOENT); } - if (get_ldap_timestamp(aldh->ldap, map, ts)) { + if (get_ldap_timestamp(aldh, map, ts)) { return (ENOENT); } return (0); -- 2.43.0