No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / am-utils / dist / amd / info_ldap.c
blobf51d59f25c0b883c93b9e71529abd800a937b064
1 /* $NetBSD$ */
3 /*
4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1989 Jan-Simon Pendry
6 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1989 The Regents of the University of California.
8 * All rights reserved.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 * File: am-utils/amd/info_ldap.c
48 * Get info from LDAP (Lightweight Directory Access Protocol)
49 * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
53 * WARNING: as of Linux Fedora Core 5 (which comes with openldap-2.3.9), the
54 * ldap.h headers deprecate several functions used in this file, such as
55 * ldap_unbind. You get compile errors about missing extern definitions.
56 * Those externs are still in <ldap.h>, but surrounded by an ifdef
57 * LDAP_DEPRECATED. I am turning on that ifdef here, under the assumption
58 * that the functions may be deprecated, but they still work for this
59 * (older?) version of the LDAP API. It gets am-utils to compile, but it is
60 * not clear if it will work perfectly.
62 #ifndef LDAP_DEPRECATED
63 # define LDAP_DEPRECATED 1
64 #endif /* not LDAP_DEPRECATED */
66 #ifdef HAVE_CONFIG_H
67 # include <config.h>
68 #endif /* HAVE_CONFIG_H */
69 #include <am_defs.h>
70 #include <amd.h>
71 #include <sun_map.h>
75 * MACROS:
77 #define AMD_LDAP_TYPE "ldap"
78 /* Time to live for an LDAP cached in an mnt_map */
79 #define AMD_LDAP_TTL 3600
80 #define AMD_LDAP_RETRIES 5
81 #define AMD_LDAP_HOST "ldap"
82 #ifndef LDAP_PORT
83 # define LDAP_PORT 389
84 #endif /* LDAP_PORT */
86 /* How timestamps are searched */
87 #define AMD_LDAP_TSFILTER "(&(objectClass=amdmapTimestamp)(amdmapName=%s))"
88 /* How maps are searched */
89 #define AMD_LDAP_FILTER "(&(objectClass=amdmap)(amdmapName=%s)(amdmapKey=%s))"
90 /* How timestamps are stored */
91 #define AMD_LDAP_TSATTR "amdmaptimestamp"
92 /* How maps are stored */
93 #define AMD_LDAP_ATTR "amdmapvalue"
96 * TYPEDEFS:
98 typedef struct ald_ent ALD;
99 typedef struct cr_ent CR;
100 typedef struct he_ent HE_ENT;
103 * STRUCTURES:
105 struct ald_ent {
106 LDAP *ldap;
107 HE_ENT *hostent;
108 CR *credentials;
109 time_t timestamp;
112 struct cr_ent {
113 char *who;
114 char *pw;
115 int method;
118 struct he_ent {
119 char *host;
120 int port;
121 struct he_ent *next;
125 * FORWARD DECLARATIONS:
127 static int amu_ldap_rebind(ALD *a);
128 static int get_ldap_timestamp(ALD *a, char *map, time_t *ts);
130 int amu_ldap_init(mnt_map *m, char *map, time_t *tsu);
131 int amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts);
132 int amu_ldap_mtime(mnt_map *m, char *map, time_t *ts);
135 * FUNCTIONS:
138 static void
139 he_free(HE_ENT *h)
141 XFREE(h->host);
142 if (h->next != NULL)
143 he_free(h->next);
144 XFREE(h);
148 static HE_ENT *
149 string2he(char *s_orig)
151 char *c, *p;
152 char *s;
153 HE_ENT *first = NULL, *cur = NULL;
155 if (NULL == s_orig || NULL == (s = strdup(s_orig)))
156 return NULL;
157 for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
158 if (cur != NULL) {
159 cur->next = ALLOC(HE_ENT);
160 cur = cur->next;
161 } else
162 first = cur = ALLOC(HE_ENT);
164 cur->next = NULL;
165 c = strchr(p, ':');
166 if (c) { /* Host and port */
167 *c++ = '\0';
168 cur->host = strdup(p);
169 cur->port = atoi(c);
170 } else {
171 cur->host = strdup(p);
172 cur->port = LDAP_PORT;
174 plog(XLOG_USER, "Adding ldap server %s:%d",
175 cur->host, cur->port);
177 XFREE(s);
178 return first;
182 static void
183 cr_free(CR *c)
185 XFREE(c->who);
186 XFREE(c->pw);
187 XFREE(c);
192 * Special ldap_unbind function to handle SIGPIPE.
193 * We first ignore SIGPIPE, in case a remote LDAP server was
194 * restarted, then we reinstall the handler.
196 static int
197 amu_ldap_unbind(LDAP *ld)
199 int e;
200 #ifdef HAVE_SIGACTION
201 struct sigaction sa;
202 #else /* not HAVE_SIGACTION */
203 void (*handler)(int);
204 #endif /* not HAVE_SIGACTION */
206 dlog("amu_ldap_unbind()\n");
208 #ifdef HAVE_SIGACTION
209 sa.sa_handler = SIG_IGN;
210 sa.sa_flags = 0;
211 sigemptyset(&(sa.sa_mask));
212 sigaddset(&(sa.sa_mask), SIGPIPE);
213 sigaction(SIGPIPE, &sa, &sa); /* set IGNORE, and get old action */
214 #else /* not HAVE_SIGACTION */
215 handler = signal(SIGPIPE, SIG_IGN);
216 #endif /* not HAVE_SIGACTION */
218 e = ldap_unbind(ld);
220 #ifdef HAVE_SIGACTION
221 sigemptyset(&(sa.sa_mask));
222 sigaddset(&(sa.sa_mask), SIGPIPE);
223 sigaction(SIGPIPE, &sa, NULL);
224 #else /* not HAVE_SIGACTION */
225 (void) signal(SIGPIPE, handler);
226 #endif /* not HAVE_SIGACTION */
228 return e;
232 static void
233 ald_free(ALD *a)
235 he_free(a->hostent);
236 cr_free(a->credentials);
237 if (a->ldap != NULL)
238 amu_ldap_unbind(a->ldap);
239 XFREE(a);
244 amu_ldap_init(mnt_map *m, char *map, time_t *ts)
246 ALD *aldh;
247 CR *creds;
249 dlog("-> amu_ldap_init: map <%s>\n", map);
252 * XXX: by checking that map_type must be defined, aren't we
253 * excluding the possibility of automatic searches through all
254 * map types?
256 if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
257 dlog("amu_ldap_init called with map_type <%s>\n",
258 (gopt.map_type ? gopt.map_type : "null"));
259 } else {
260 dlog("Map %s is ldap\n", map);
263 aldh = ALLOC(ALD);
264 creds = ALLOC(CR);
265 aldh->ldap = NULL;
266 aldh->hostent = string2he(gopt.ldap_hostports);
267 if (aldh->hostent == NULL) {
268 plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
269 gopt.ldap_hostports ? gopt.ldap_hostports : "(null)", map);
270 XFREE(creds);
271 XFREE(aldh);
272 return (ENOENT);
274 creds->who = "";
275 creds->pw = "";
276 creds->method = LDAP_AUTH_SIMPLE;
277 aldh->credentials = creds;
278 aldh->timestamp = 0;
279 aldh->ldap = NULL;
280 dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
281 if (amu_ldap_rebind(aldh)) {
282 ald_free(aldh);
283 return (ENOENT);
285 m->map_data = (void *) aldh;
286 dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
287 if (get_ldap_timestamp(aldh, map, ts))
288 return (ENOENT);
289 dlog("Got timestamp for map %s: %ld\n", map, (u_long) *ts);
291 return (0);
295 static int
296 amu_ldap_rebind(ALD *a)
298 LDAP *ld;
299 HE_ENT *h;
300 CR *c = a->credentials;
301 time_t now = clocktime(NULL);
302 int try;
304 dlog("-> amu_ldap_rebind\n");
306 if (a->ldap != NULL) {
307 if ((a->timestamp - now) > AMD_LDAP_TTL) {
308 dlog("Re-establishing ldap connection\n");
309 amu_ldap_unbind(a->ldap);
310 a->timestamp = now;
311 a->ldap = NULL;
312 } else {
313 /* Assume all is OK. If it wasn't we'll be back! */
314 dlog("amu_ldap_rebind: timestamp OK\n");
315 return (0);
319 for (try=0; try<10; try++) { /* XXX: try up to 10 times (makes sense?) */
320 for (h = a->hostent; h != NULL; h = h->next) {
321 if ((ld = ldap_open(h->host, h->port)) == NULL) {
322 plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
323 continue;
325 #if LDAP_VERSION_MAX > LDAP_VERSION2
326 /* handle LDAPv3 and heigher, if available and amd.conf-igured */
327 if (gopt.ldap_proto_version > LDAP_VERSION2) {
328 if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) {
329 dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n",
330 gopt.ldap_proto_version);
331 } else {
332 plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld for "
333 "%s:%d\n", gopt.ldap_proto_version, h->host, h->port);
334 continue;
337 #endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */
338 if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
339 plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
340 h->host, h->port, c->who);
341 continue;
343 if (gopt.ldap_cache_seconds > 0) {
344 #if defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE)
345 ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
346 #else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
347 plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds);
348 #endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
350 a->ldap = ld;
351 a->timestamp = now;
352 return (0);
354 plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
357 plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
358 return (ENOENT);
362 static int
363 get_ldap_timestamp(ALD *a, char *map, time_t *ts)
365 struct timeval tv;
366 char **vals, *end;
367 char filter[MAXPATHLEN];
368 int i, err = 0, nentries = 0;
369 LDAPMessage *res = NULL, *entry;
371 dlog("-> get_ldap_timestamp: map <%s>\n", map);
373 tv.tv_sec = 3;
374 tv.tv_usec = 0;
375 xsnprintf(filter, sizeof(filter), AMD_LDAP_TSFILTER, map);
376 dlog("Getting timestamp for map %s\n", map);
377 dlog("Filter is: %s\n", filter);
378 dlog("Base is: %s\n", gopt.ldap_base);
379 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
380 err = ldap_search_st(a->ldap,
381 gopt.ldap_base,
382 LDAP_SCOPE_SUBTREE,
383 filter,
386 &tv,
387 &res);
388 if (err == LDAP_SUCCESS)
389 break;
390 if (res) {
391 ldap_msgfree(res);
392 res = NULL;
394 plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n",
395 i + 1, ldap_err2string(err));
396 if (err != LDAP_TIMEOUT) {
397 dlog("get_ldap_timestamp: unbinding...\n");
398 amu_ldap_unbind(a->ldap);
399 a->ldap = NULL;
400 if (amu_ldap_rebind(a))
401 return (ENOENT);
403 dlog("Timestamp search failed, trying again...\n");
406 if (err != LDAP_SUCCESS) {
407 *ts = 0;
408 plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
409 ldap_err2string(err));
410 if (res)
411 ldap_msgfree(res);
412 return (ENOENT);
415 nentries = ldap_count_entries(a->ldap, res);
416 if (nentries == 0) {
417 plog(XLOG_USER, "No timestamp entry for map %s\n", map);
418 *ts = 0;
419 ldap_msgfree(res);
420 return (ENOENT);
423 entry = ldap_first_entry(a->ldap, res);
424 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR);
425 if (ldap_count_values(vals) == 0) {
426 plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
427 *ts = 0;
428 ldap_value_free(vals);
429 ldap_msgfree(res);
430 return (ENOENT);
432 dlog("TS value is:%s:\n", vals[0]);
434 if (vals[0]) {
435 *ts = (time_t) strtol(vals[0], &end, 10);
436 if (end == vals[0]) {
437 plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
438 vals[0], map);
439 err = ENOENT;
441 if (!*ts > 0) {
442 plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n",
443 (u_long) *ts, map);
444 err = ENOENT;
446 } else {
447 plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
448 *ts = 0;
449 err = ENOENT;
452 ldap_value_free(vals);
453 ldap_msgfree(res);
454 dlog("The timestamp for %s is %ld (err=%d)\n", map, (u_long) *ts, err);
455 return (err);
460 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
462 char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN];
463 char *f1, *f2;
464 struct timeval tv;
465 int i, err = 0, nvals = 0, nentries = 0;
466 LDAPMessage *entry, *res = NULL;
467 ALD *a = (ALD *) (m->map_data);
469 dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key);
471 tv.tv_sec = 2;
472 tv.tv_usec = 0;
473 if (a == NULL) {
474 plog(XLOG_USER, "LDAP panic: no map data\n");
475 return (EIO);
477 if (amu_ldap_rebind(a)) /* Check that's the handle is still valid */
478 return (ENOENT);
480 xsnprintf(filter, sizeof(filter), AMD_LDAP_FILTER, map, key);
481 /* "*" is special to ldap_search(); run through the filter escaping it. */
482 f1 = filter; f2 = filter2;
483 while (*f1) {
484 if (*f1 == '*') {
485 *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a';
486 f1++;
487 } else {
488 *f2++ = *f1++;
491 *f2 = '\0';
492 dlog("Search with filter: <%s>\n", filter2);
493 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
494 err = ldap_search_st(a->ldap,
495 gopt.ldap_base,
496 LDAP_SCOPE_SUBTREE,
497 filter2,
500 &tv,
501 &res);
502 if (err == LDAP_SUCCESS)
503 break;
504 if (res) {
505 ldap_msgfree(res);
506 res = NULL;
508 plog(XLOG_USER, "LDAP search attempt %d failed: %s\n",
509 i + 1, ldap_err2string(err));
510 if (err != LDAP_TIMEOUT) {
511 dlog("amu_ldap_search: unbinding...\n");
512 amu_ldap_unbind(a->ldap);
513 a->ldap = NULL;
514 if (amu_ldap_rebind(a))
515 return (ENOENT);
519 switch (err) {
520 case LDAP_SUCCESS:
521 break;
522 case LDAP_NO_SUCH_OBJECT:
523 dlog("No object\n");
524 if (res)
525 ldap_msgfree(res);
526 return (ENOENT);
527 default:
528 plog(XLOG_USER, "LDAP search failed: %s\n",
529 ldap_err2string(err));
530 if (res)
531 ldap_msgfree(res);
532 return (EIO);
535 nentries = ldap_count_entries(a->ldap, res);
536 dlog("Search found %d entries\n", nentries);
537 if (nentries == 0) {
538 ldap_msgfree(res);
539 return (ENOENT);
541 entry = ldap_first_entry(a->ldap, res);
542 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
543 nvals = ldap_count_values(vals);
544 if (nvals == 0) {
545 plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
546 ldap_value_free(vals);
547 ldap_msgfree(res);
548 return (EIO);
550 dlog("Map %s, %s => %s\n", map, key, vals[0]);
551 if (vals[0]) {
552 if (m->cfm && (m->cfm->cfm_flags & CFM_SUN_MAP_SYNTAX))
553 *pval = sun_entry2amd(key, vals[0]);
554 else
555 *pval = strdup(vals[0]);
556 err = 0;
557 } else {
558 plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
559 err = ENOENT;
561 ldap_msgfree(res);
562 ldap_value_free(vals);
564 return (err);
569 amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
571 ALD *aldh = (ALD *) (m->map_data);
573 if (aldh == NULL) {
574 dlog("LDAP panic: unable to find map data\n");
575 return (ENOENT);
577 if (amu_ldap_rebind(aldh)) {
578 return (ENOENT);
580 if (get_ldap_timestamp(aldh, map, ts)) {
581 return (ENOENT);
583 return (0);