2 * Copyright (c) 1997-2014 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * File: am-utils/amd/info_ldap.c
42 * Get info from LDAP (Lightweight Directory Access Protocol)
43 * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
47 * WARNING: as of Linux Fedora Core 5 (which comes with openldap-2.3.9), the
48 * ldap.h headers deprecate several functions used in this file, such as
49 * ldap_unbind. You get compile errors about missing extern definitions.
50 * Those externs are still in <ldap.h>, but surrounded by an ifdef
51 * LDAP_DEPRECATED. I am turning on that ifdef here, under the assumption
52 * that the functions may be deprecated, but they still work for this
53 * (older?) version of the LDAP API. It gets am-utils to compile, but it is
54 * not clear if it will work perfectly.
56 #ifndef LDAP_DEPRECATED
57 # define LDAP_DEPRECATED 1
58 #endif /* not LDAP_DEPRECATED */
62 #endif /* HAVE_CONFIG_H */
71 #define AMD_LDAP_TYPE "ldap"
72 /* Time to live for an LDAP cached in an mnt_map */
73 #define AMD_LDAP_TTL 3600
74 #define AMD_LDAP_RETRIES 5
75 #define AMD_LDAP_HOST "ldap"
77 # define LDAP_PORT 389
78 #endif /* LDAP_PORT */
80 /* How timestamps are searched */
81 #define AMD_LDAP_TSFILTER "(&(objectClass=amdmapTimestamp)(amdmapName=%s))"
82 /* How maps are searched */
83 #define AMD_LDAP_FILTER "(&(objectClass=amdmap)(amdmapName=%s)(amdmapKey=%s))"
84 /* How timestamps are stored */
85 #define AMD_LDAP_TSATTR "amdmaptimestamp"
86 /* How maps are stored */
87 #define AMD_LDAP_ATTR "amdmapvalue"
92 typedef struct ald_ent ALD;
93 typedef struct cr_ent CR;
94 typedef struct he_ent HE_ENT;
118 static ALD *ldap_connection;
121 * FORWARD DECLARATIONS:
123 static int amu_ldap_rebind(ALD *a);
124 static int get_ldap_timestamp(ALD *a, char *map, time_t *ts);
126 int amu_ldap_init(mnt_map *m, char *map, time_t *tsu);
127 int amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts);
128 int amu_ldap_mtime(mnt_map *m, char *map, time_t *ts);
145 string2he(char *s_orig)
149 HE_ENT *first = NULL, *cur = NULL;
154 for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
156 cur->next = ALLOC(HE_ENT);
159 first = cur = ALLOC(HE_ENT);
163 if (c) { /* Host and port */
165 cur->host = xstrdup(p);
168 cur->host = xstrdup(p);
169 cur->port = LDAP_PORT;
171 plog(XLOG_USER, "Adding ldap server %s:%d",
172 cur->host, cur->port);
189 * Special ldap_unbind function to handle SIGPIPE.
190 * We first ignore SIGPIPE, in case a remote LDAP server was
191 * restarted, then we reinstall the handler.
194 amu_ldap_unbind(LDAP *ld)
197 #ifdef HAVE_SIGACTION
199 #else /* not HAVE_SIGACTION */
200 void (*handler)(int);
201 #endif /* not HAVE_SIGACTION */
203 dlog("amu_ldap_unbind()\n");
205 #ifdef HAVE_SIGACTION
206 sa.sa_handler = SIG_IGN;
208 sigemptyset(&(sa.sa_mask));
209 sigaddset(&(sa.sa_mask), SIGPIPE);
210 sigaction(SIGPIPE, &sa, &sa); /* set IGNORE, and get old action */
211 #else /* not HAVE_SIGACTION */
212 handler = signal(SIGPIPE, SIG_IGN);
213 #endif /* not HAVE_SIGACTION */
217 #ifdef HAVE_SIGACTION
218 sigemptyset(&(sa.sa_mask));
219 sigaddset(&(sa.sa_mask), SIGPIPE);
220 sigaction(SIGPIPE, &sa, NULL);
221 #else /* not HAVE_SIGACTION */
222 (void) signal(SIGPIPE, handler);
223 #endif /* not HAVE_SIGACTION */
233 cr_free(a->credentials);
235 amu_ldap_unbind(a->ldap);
241 amu_ldap_init(mnt_map *m, char *map, time_t *ts)
246 dlog("-> amu_ldap_init: map <%s>\n", map);
249 * XXX: by checking that map_type must be defined, aren't we
250 * excluding the possibility of automatic searches through all
253 if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
254 dlog("amu_ldap_init called with map_type <%s>\n",
255 (gopt.map_type ? gopt.map_type : "null"));
258 dlog("Map %s is ldap\n", map);
261 #ifndef LDAP_CONNECTION_PER_MAP
262 if (ldap_connection != NULL) {
263 m->map_data = (void *) ldap_connection;
271 aldh->hostent = string2he(gopt.ldap_hostports);
272 if (aldh->hostent == NULL) {
273 plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
274 gopt.ldap_hostports ? gopt.ldap_hostports : "(null)", map);
281 creds->method = LDAP_AUTH_SIMPLE;
282 aldh->credentials = creds;
285 dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
286 if (amu_ldap_rebind(aldh)) {
290 dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
291 if (get_ldap_timestamp(aldh, map, ts)) {
295 dlog("Got timestamp for map %s: %ld\n", map, (u_long) *ts);
296 ldap_connection = aldh;
297 m->map_data = (void *) ldap_connection;
304 amu_ldap_rebind(ALD *a)
308 CR *c = a->credentials;
309 time_t now = clocktime(NULL);
312 dlog("-> amu_ldap_rebind\n");
314 if (a->ldap != NULL) {
315 if ((a->timestamp - now) > AMD_LDAP_TTL) {
316 dlog("Re-establishing ldap connection\n");
317 amu_ldap_unbind(a->ldap);
321 /* Assume all is OK. If it wasn't we'll be back! */
322 dlog("amu_ldap_rebind: timestamp OK\n");
327 for (try=0; try<10; try++) { /* XXX: try up to 10 times (makes sense?) */
328 for (h = a->hostent; h != NULL; h = h->next) {
329 if ((ld = ldap_open(h->host, h->port)) == NULL) {
330 plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
333 #if LDAP_VERSION_MAX > LDAP_VERSION2
334 /* handle LDAPv3 and heigher, if available and amd.conf-igured */
335 if (gopt.ldap_proto_version > LDAP_VERSION2) {
336 if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) {
337 dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n",
338 gopt.ldap_proto_version);
340 plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld for "
341 "%s:%d\n", gopt.ldap_proto_version, h->host, h->port);
345 #endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */
346 if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
347 plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
348 h->host, h->port, c->who);
351 if (gopt.ldap_cache_seconds > 0) {
352 #if defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE)
353 ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
354 #else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
355 plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds);
356 #endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
362 plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
365 plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
371 get_ldap_timestamp(ALD *a, char *map, time_t *ts)
375 char filter[MAXPATHLEN];
376 int i, err = 0, nentries = 0;
377 LDAPMessage *res = NULL, *entry;
379 dlog("-> get_ldap_timestamp: map <%s>\n", map);
383 xsnprintf(filter, sizeof(filter), AMD_LDAP_TSFILTER, map);
384 dlog("Getting timestamp for map %s\n", map);
385 dlog("Filter is: %s\n", filter);
386 dlog("Base is: %s\n", gopt.ldap_base);
387 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
388 err = ldap_search_st(a->ldap,
396 if (err == LDAP_SUCCESS)
402 plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n",
403 i + 1, ldap_err2string(err));
404 if (err != LDAP_TIMEOUT) {
405 dlog("get_ldap_timestamp: unbinding...\n");
406 amu_ldap_unbind(a->ldap);
408 if (amu_ldap_rebind(a))
411 dlog("Timestamp search failed, trying again...\n");
414 if (err != LDAP_SUCCESS) {
416 plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
417 ldap_err2string(err));
423 nentries = ldap_count_entries(a->ldap, res);
425 plog(XLOG_USER, "No timestamp entry for map %s\n", map);
431 entry = ldap_first_entry(a->ldap, res);
432 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR);
433 if (ldap_count_values(vals) == 0) {
434 plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
436 ldap_value_free(vals);
440 dlog("TS value is:%s:\n", vals[0]);
443 *ts = (time_t) strtol(vals[0], &end, 10);
444 if (end == vals[0]) {
445 plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
450 plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n",
455 plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
460 ldap_value_free(vals);
462 dlog("The timestamp for %s is %ld (err=%d)\n", map, (u_long) *ts, err);
468 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
470 char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN];
473 int i, err = 0, nvals = 0, nentries = 0;
474 LDAPMessage *entry, *res = NULL;
475 ALD *a = (ALD *) (m->map_data);
477 dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key);
482 plog(XLOG_USER, "LDAP panic: no map data\n");
485 if (amu_ldap_rebind(a)) /* Check that's the handle is still valid */
488 xsnprintf(filter, sizeof(filter), AMD_LDAP_FILTER, map, key);
489 /* "*" is special to ldap_search(); run through the filter escaping it. */
490 f1 = filter; f2 = filter2;
493 *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a';
500 dlog("Search with filter: <%s>\n", filter2);
501 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
502 err = ldap_search_st(a->ldap,
510 if (err == LDAP_SUCCESS)
516 plog(XLOG_USER, "LDAP search attempt %d failed: %s\n",
517 i + 1, ldap_err2string(err));
518 if (err != LDAP_TIMEOUT) {
519 dlog("amu_ldap_search: unbinding...\n");
520 amu_ldap_unbind(a->ldap);
522 if (amu_ldap_rebind(a))
530 case LDAP_NO_SUCH_OBJECT:
536 plog(XLOG_USER, "LDAP search failed: %s\n",
537 ldap_err2string(err));
543 nentries = ldap_count_entries(a->ldap, res);
544 dlog("Search found %d entries\n", nentries);
549 entry = ldap_first_entry(a->ldap, res);
550 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
551 nvals = ldap_count_values(vals);
553 plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
554 ldap_value_free(vals);
558 dlog("Map %s, %s => %s\n", map, key, vals[0]);
560 if (m->cfm && (m->cfm->cfm_flags & CFM_SUN_MAP_SYNTAX))
561 *pval = sun_entry2amd(key, vals[0]);
563 *pval = xstrdup(vals[0]);
566 plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
570 ldap_value_free(vals);
577 amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
579 ALD *aldh = (ALD *) (m->map_data);
582 dlog("LDAP panic: unable to find map data\n");
585 if (amu_ldap_rebind(aldh)) {
588 if (get_ldap_timestamp(aldh, map, ts)) {