2 * Copyright (c) 1997-2006 Erez Zadok
3 * Copyright (c) 1989 Jan-Simon Pendry
4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1989 The Regents of the University of California.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * File: am-utils/amd/info_ldap.c
46 * Get info from LDAP (Lightweight Directory Access Protocol)
47 * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
51 * WARNING: as of Linux Fedora Core 5 (which comes with openldap-2.3.9), the
52 * ldap.h headers deprecate several functions used in this file, such as
53 * ldap_unbind. You get compile errors about missing extern definitions.
54 * Those externs are still in <ldap.h>, but surrounded by an ifdef
55 * LDAP_DEPRECATED. I am turning on that ifdef here, under the assumption
56 * that the functions may be deprecated, but they still work for this
57 * (older?) version of the LDAP API. It gets am-utils to compile, but it is
58 * not clear if it will work perfectly.
60 #ifndef LDAP_DEPRECATED
61 # define LDAP_DEPRECATED 1
62 #endif /* not LDAP_DEPRECATED */
66 #endif /* HAVE_CONFIG_H */
74 #define AMD_LDAP_TYPE "ldap"
75 /* Time to live for an LDAP cached in an mnt_map */
76 #define AMD_LDAP_TTL 3600
77 #define AMD_LDAP_RETRIES 5
78 #define AMD_LDAP_HOST "ldap"
80 # define LDAP_PORT 389
81 #endif /* LDAP_PORT */
83 /* How timestamps are searched */
84 #define AMD_LDAP_TSFILTER "(&(objectClass=amdmapTimestamp)(amdmapName=%s))"
85 /* How maps are searched */
86 #define AMD_LDAP_FILTER "(&(objectClass=amdmap)(amdmapName=%s)(amdmapKey=%s))"
87 /* How timestamps are stored */
88 #define AMD_LDAP_TSATTR "amdmaptimestamp"
89 /* How maps are stored */
90 #define AMD_LDAP_ATTR "amdmapvalue"
95 typedef struct ald_ent ALD;
96 typedef struct cr_ent CR;
97 typedef struct he_ent HE_ENT;
122 * FORWARD DECLARATIONS:
124 static int amu_ldap_rebind(ALD *a);
125 static int get_ldap_timestamp(ALD *a, char *map, time_t *ts);
143 string2he(char *s_orig)
147 HE_ENT *new, *old = NULL;
149 if (NULL == s_orig || NULL == (s = strdup(s_orig)))
151 for (p = s; p; p = strchr(p, ',')) {
161 if (c) { /* Host and port */
163 old->host = strdup(p);
166 old->host = strdup(p);
184 * Special ldap_unbind function to handle SIGPIPE.
185 * We first ignore SIGPIPE, in case a remote LDAP server was
186 * restarted, then we reinstall the handler.
189 amu_ldap_unbind(LDAP *ld)
192 #ifdef HAVE_SIGACTION
194 #else /* not HAVE_SIGACTION */
195 void (*handler)(int);
196 #endif /* not HAVE_SIGACTION */
198 dlog("amu_ldap_unbind()\n");
200 #ifdef HAVE_SIGACTION
201 sa.sa_handler = SIG_IGN;
203 sigemptyset(&(sa.sa_mask));
204 sigaddset(&(sa.sa_mask), SIGPIPE);
205 sigaction(SIGPIPE, &sa, &sa); /* set IGNORE, and get old action */
206 #else /* not HAVE_SIGACTION */
207 handler = signal(SIGPIPE, SIG_IGN);
208 #endif /* not HAVE_SIGACTION */
212 #ifdef HAVE_SIGACTION
213 sigemptyset(&(sa.sa_mask));
214 sigaddset(&(sa.sa_mask), SIGPIPE);
215 sigaction(SIGPIPE, &sa, NULL);
216 #else /* not HAVE_SIGACTION */
217 (void) signal(SIGPIPE, handler);
218 #endif /* not HAVE_SIGACTION */
228 cr_free(a->credentials);
230 amu_ldap_unbind(a->ldap);
236 amu_ldap_init(mnt_map *m, char *map, time_t *ts)
241 dlog("-> amu_ldap_init: map <%s>\n", map);
244 * XXX: by checking that map_type must be defined, aren't we
245 * excluding the possibility of automatic searches through all
248 if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
249 dlog("amu_ldap_init called with map_type <%s>\n",
250 (gopt.map_type ? gopt.map_type : "null"));
252 dlog("Map %s is ldap\n", map);
258 aldh->hostent = string2he(gopt.ldap_hostports);
259 if (aldh->hostent == NULL) {
260 plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
261 gopt.ldap_hostports ? gopt.ldap_hostports : "(null)", map);
268 creds->method = LDAP_AUTH_SIMPLE;
269 aldh->credentials = creds;
272 dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
273 if (amu_ldap_rebind(aldh)) {
277 m->map_data = (void *) aldh;
278 dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
279 if (get_ldap_timestamp(aldh, map, ts))
281 dlog("Got timestamp for map %s: %ld\n", map, (u_long) *ts);
288 amu_ldap_rebind(ALD *a)
292 CR *c = a->credentials;
293 time_t now = clocktime(NULL);
296 dlog("-> amu_ldap_rebind\n");
298 if (a->ldap != NULL) {
299 if ((a->timestamp - now) > AMD_LDAP_TTL) {
300 dlog("Re-establishing ldap connection\n");
301 amu_ldap_unbind(a->ldap);
305 /* Assume all is OK. If it wasn't we'll be back! */
306 dlog("amu_ldap_rebind: timestamp OK\n");
311 for (try=0; try<10; try++) { /* XXX: try up to 10 times (makes sense?) */
312 for (h = a->hostent; h != NULL; h = h->next) {
313 if ((ld = ldap_open(h->host, h->port)) == NULL) {
314 plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
317 #if LDAP_VERSION_MAX > LDAP_VERSION2
318 /* handle LDAPv3 and heigher, if available and amd.conf-igured */
319 if (gopt.ldap_proto_version > LDAP_VERSION2) {
320 if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) {
321 dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n",
322 gopt.ldap_proto_version);
324 plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld\n",
325 gopt.ldap_proto_version);
329 #endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */
330 if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
331 plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
332 h->host, h->port, c->who);
335 if (gopt.ldap_cache_seconds > 0) {
336 #if defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE)
337 ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
338 #else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
339 plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds);
340 #endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
346 plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
349 plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
355 get_ldap_timestamp(ALD *a, char *map, time_t *ts)
359 char filter[MAXPATHLEN];
360 int i, err = 0, nentries = 0;
361 LDAPMessage *res = NULL, *entry;
363 dlog("-> get_ldap_timestamp: map <%s>\n", map);
367 xsnprintf(filter, sizeof(filter), AMD_LDAP_TSFILTER, map);
368 dlog("Getting timestamp for map %s\n", map);
369 dlog("Filter is: %s\n", filter);
370 dlog("Base is: %s\n", gopt.ldap_base);
371 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
372 err = ldap_search_st(a->ldap,
380 if (err == LDAP_SUCCESS)
386 plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n",
387 i + 1, ldap_err2string(err));
388 if (err != LDAP_TIMEOUT) {
389 dlog("get_ldap_timestamp: unbinding...\n");
390 amu_ldap_unbind(a->ldap);
392 if (amu_ldap_rebind(a))
395 dlog("Timestamp search failed, trying again...\n");
398 if (err != LDAP_SUCCESS) {
400 plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
401 ldap_err2string(err));
407 nentries = ldap_count_entries(a->ldap, res);
409 plog(XLOG_USER, "No timestamp entry for map %s\n", map);
415 entry = ldap_first_entry(a->ldap, res);
416 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR);
417 if (ldap_count_values(vals) == 0) {
418 plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
420 ldap_value_free(vals);
424 dlog("TS value is:%s:\n", vals[0]);
427 *ts = (time_t) strtol(vals[0], &end, 10);
428 if (end == vals[0]) {
429 plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
434 plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n",
439 plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
444 ldap_value_free(vals);
446 dlog("The timestamp for %s is %ld (err=%d)\n", map, (u_long) *ts, err);
452 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
454 char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN];
457 int i, err = 0, nvals = 0, nentries = 0;
458 LDAPMessage *entry, *res = NULL;
459 ALD *a = (ALD *) (m->map_data);
461 dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key);
466 plog(XLOG_USER, "LDAP panic: no map data\n");
469 if (amu_ldap_rebind(a)) /* Check that's the handle is still valid */
472 xsnprintf(filter, sizeof(filter), AMD_LDAP_FILTER, map, key);
473 /* "*" is special to ldap_search(); run through the filter escaping it. */
474 f1 = filter; f2 = filter2;
477 *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a';
484 dlog("Search with filter: <%s>\n", filter2);
485 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
486 err = ldap_search_st(a->ldap,
494 if (err == LDAP_SUCCESS)
500 plog(XLOG_USER, "LDAP search attempt %d failed: %s\n",
501 i + 1, ldap_err2string(err));
502 if (err != LDAP_TIMEOUT) {
503 dlog("amu_ldap_search: unbinding...\n");
504 amu_ldap_unbind(a->ldap);
506 if (amu_ldap_rebind(a))
514 case LDAP_NO_SUCH_OBJECT:
520 plog(XLOG_USER, "LDAP search failed: %s\n",
521 ldap_err2string(err));
527 nentries = ldap_count_entries(a->ldap, res);
528 dlog("Search found %d entries\n", nentries);
533 entry = ldap_first_entry(a->ldap, res);
534 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
535 nvals = ldap_count_values(vals);
537 plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
538 ldap_value_free(vals);
542 dlog("Map %s, %s => %s\n", map, key, vals[0]);
544 *pval = strdup(vals[0]);
547 plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
551 ldap_value_free(vals);
558 amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
560 ALD *aldh = (ALD *) (m->map_data);
563 dlog("LDAP panic: unable to find map data\n");
566 if (amu_ldap_rebind(aldh)) {
569 if (get_ldap_timestamp(aldh, map, ts)) {