MFC r1.27:
[dragonfly.git] / contrib / amd / amd / info_ldap.c
bloba2bd9baeaf6e0d262455a591261d7073bb80ccd9
1 /*
2 * Copyright (c) 1997-1999 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.
6 * All rights reserved.
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
13 * are met:
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
37 * SUCH DAMAGE.
39 * %W% (Berkeley) %G%
41 * $Id: info_ldap.c,v 1.6 1999/09/30 21:01:31 ezk Exp $
47 * Get info from LDAP (Lightweight Directory Access Protocol)
48 * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif /* HAVE_CONFIG_H */
54 #include <am_defs.h>
55 #include <amd.h>
59 * MACROS:
61 #define AMD_LDAP_TYPE "ldap"
62 /* Time to live for an LDAP cached in an mnt_map */
63 #define AMD_LDAP_TTL 3600
64 #define AMD_LDAP_RETRIES 5
65 #define AMD_LDAP_HOST "ldap"
66 #ifndef LDAP_PORT
67 # define LDAP_PORT 389
68 #endif /* LDAP_PORT */
70 /* How timestamps are searched */
71 #define AMD_LDAP_TSFILTER "(&(objectClass=amdmapTimestamp)(amdmapName=%s))"
72 /* How maps are searched */
73 #define AMD_LDAP_FILTER "(&(objectClass=amdmap)(amdmapName=%s)(amdmapKey=%s))"
74 /* How timestamps are stored */
75 #define AMD_LDAP_TSATTR "amdmaptimestamp"
76 /* How maps are stored */
77 #define AMD_LDAP_ATTR "amdmapvalue"
80 * TYPEDEFS:
82 typedef struct ald_ent ALD;
83 typedef struct cr_ent CR;
84 typedef struct he_ent HE;
87 * STRUCTURES:
89 struct ald_ent {
90 LDAP *ldap;
91 HE *hostent;
92 CR *credentials;
93 time_t timestamp;
96 struct cr_ent {
97 char *who;
98 char *pw;
99 int method;
102 struct he_ent {
103 char *host;
104 int port;
105 struct he_ent *next;
109 * FORWARD DECLARATIONS:
111 static int amu_ldap_rebind(ALD *a);
112 static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts);
116 * FUNCTIONS:
119 static void
120 he_free(HE *h)
122 XFREE(h->host);
123 if (h->next != NULL)
124 he_free(h->next);
125 XFREE(h);
129 static HE *
130 string2he(char *s)
132 char *c, *p;
133 HE *new, *old = NULL;
135 if (s == NULL)
136 return (NULL);
137 for (p = s; p; p = strchr(p, ',')) {
138 if (old != NULL) {
139 new = (HE *) xmalloc(sizeof(HE));
140 old->next = new;
141 old = new;
142 } else {
143 old = (HE *) xmalloc(sizeof(HE));
144 old->next = NULL;
146 c = strchr(p, ':');
147 if (c) { /* Host and port */
148 *c++ = '\0';
149 old->host = strdup(p);
150 old->port = atoi(c);
151 } else
152 old->host = strdup(p);
155 return (old);
159 static void
160 cr_free(CR *c)
162 XFREE(c->who);
163 XFREE(c->pw);
164 XFREE(c);
168 static void
169 ald_free(ALD *a)
171 he_free(a->hostent);
172 cr_free(a->credentials);
173 if (a->ldap != NULL)
174 ldap_unbind(a->ldap);
175 XFREE(a);
180 amu_ldap_init(mnt_map *m, char *map, time_t *ts)
182 ALD *aldh;
183 CR *creds;
186 * XXX: by checking that map_type must be defined, aren't we
187 * excluding the possibility of automatic searches through all
188 * map types?
190 if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
191 return (ENOENT);
193 #ifdef DEBUG
194 else {
195 dlog("Map %s is ldap\n", map);
197 #endif /* DEBUG */
199 aldh = (ALD *) xmalloc(sizeof(ALD));
200 creds = (CR *) xmalloc(sizeof(CR));
202 aldh->hostent = string2he(gopt.ldap_hostports);
203 if (aldh->hostent == NULL) {
204 plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
205 gopt.ldap_hostports, map);
206 return (ENOENT);
208 creds->who = "";
209 creds->pw = "";
210 creds->method = LDAP_AUTH_SIMPLE;
211 aldh->credentials = creds;
212 aldh->timestamp = 0;
213 #ifdef DEBUG
214 dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
215 #endif /* DEBUG */
216 if (amu_ldap_rebind(aldh)) {
217 ald_free(aldh);
218 return (ENOENT);
220 m->map_data = (void *) aldh;
221 #ifdef DEBUG
222 dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
223 #endif /* DEBUG */
224 if (get_ldap_timestamp(aldh->ldap, map, ts))
225 return (ENOENT);
226 #ifdef DEBUG
227 dlog("Got timestamp for map %s: %ld\n", map, *ts);
228 #endif /* DEBUG */
230 return (0);
234 static int
235 amu_ldap_rebind(ALD *a)
237 LDAP *ld;
238 HE *h;
239 CR *c = a->credentials;
240 time_t now = clocktime();
241 int try;
243 if (a->ldap != NULL) {
244 if ((a->timestamp - now) > AMD_LDAP_TTL) {
245 #ifdef DEBUG
246 dlog("Reestablishing ldap connection\n");
247 #endif /* DEBUG */
248 ldap_unbind(a->ldap);
249 a->timestamp = now;
250 } else
251 return (0);
254 for (try=0; try<10; try++) { /* XXX: try up to 10 times (makes sense?) */
255 for (h = a->hostent; h != NULL; h = h->next) {
256 if ((ld = ldap_open(h->host, h->port)) == NULL) {
257 plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
258 break;
260 if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
261 plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
262 h->host, h->port, c->who);
263 break;
265 if (gopt.ldap_cache_seconds > 0) {
266 ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
267 a->ldap = ld;
268 a->timestamp = now;
269 return (0);
272 plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
275 plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
276 return (ENOENT);
280 static int
281 get_ldap_timestamp(LDAP * ld, char *map, time_t *ts)
283 struct timeval tv;
284 char **vals, *end;
285 char filter[MAXPATHLEN];
286 int i, err = 0, nentries = 0;
287 LDAPMessage *res, *entry;
289 tv.tv_sec = 3;
290 tv.tv_usec = 0;
291 sprintf(filter, AMD_LDAP_TSFILTER, map);
292 #ifdef DEBUG
293 dlog("Getting timestamp for map %s\n", map);
294 dlog("Filter is: %s\n", filter);
295 dlog("Base is: %s\n", gopt.ldap_base);
296 #endif /* DEBUG */
297 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
298 err = ldap_search_st(ld,
299 gopt.ldap_base,
300 LDAP_SCOPE_SUBTREE,
301 filter,
304 &tv,
305 &res);
306 if (err == LDAP_SUCCESS)
307 break;
308 #ifdef DEBUG
309 dlog("Timestamp search timed out, trying again...\n");
310 #endif /* DEBUG */
313 if (err != LDAP_SUCCESS) {
314 *ts = 0;
315 plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
316 ldap_err2string(ld->ld_errno));
317 return (ENOENT);
320 nentries = ldap_count_entries(ld, res);
321 if (nentries == 0) {
322 plog(XLOG_USER, "No timestamp entry for map %s\n", map);
323 *ts = 0;
324 ldap_msgfree(res);
325 return (ENOENT);
328 entry = ldap_first_entry(ld, res);
329 vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR);
330 if (ldap_count_values(vals) == 0) {
331 plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
332 *ts = 0;
333 ldap_value_free(vals);
334 ldap_msgfree(res);
335 ldap_msgfree(entry);
336 return (ENOENT);
338 #ifdef DEBUG
339 dlog("TS value is:%s:\n", vals[0]);
340 #endif /* DEBUG */
342 if (vals[0]) {
343 *ts = (time_t) strtol(vals[0], &end, 10);
344 if (end == vals[0]) {
345 plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
346 vals[0], map);
347 err = ENOENT;
349 if (!*ts > 0) {
350 plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n",
351 *ts, map);
352 err = ENOENT;
354 } else {
355 plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
356 *ts = 0;
357 err = ENOENT;
360 ldap_value_free(vals);
361 ldap_msgfree(res);
362 ldap_msgfree(entry);
363 #ifdef DEBUG
364 dlog("The timestamp for %s is %ld (err=%d)\n", map, *ts, err);
365 #endif /* DEBUG */
366 return (err);
371 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
373 char **vals, filter[MAXPATHLEN];
374 struct timeval tv;
375 int i, err = 0, nvals = 0, nentries = 0;
376 LDAPMessage *entry, *res;
377 ALD *a = (ALD *) (m->map_data);
379 tv.tv_sec = 2;
380 tv.tv_usec = 0;
381 if (a == NULL) {
382 plog(XLOG_USER, "LDAP panic: no map data\n");
383 return (EIO);
385 if (amu_ldap_rebind(a)) /* Check that's the handle is still valid */
386 return (ENOENT);
388 sprintf(filter, AMD_LDAP_FILTER, map, key);
389 #ifdef DEBUG
390 dlog("Search with filter: %s\n", filter);
391 #endif /* DEBUG */
392 for (i = 0; i < AMD_LDAP_RETRIES; i++) {
393 err = ldap_search_st(a->ldap,
394 gopt.ldap_base,
395 LDAP_SCOPE_SUBTREE,
396 filter,
399 &tv,
400 &res);
401 if (err == LDAP_SUCCESS)
402 break;
405 switch (err) {
406 case LDAP_SUCCESS:
407 break;
408 case LDAP_NO_SUCH_OBJECT:
409 #ifdef DEBUG
410 dlog("No object\n");
411 #endif /* DEBUG */
412 ldap_msgfree(res);
413 return (ENOENT);
414 default:
415 plog(XLOG_USER, "LDAP search failed: %s\n",
416 ldap_err2string(a->ldap->ld_errno));
417 ldap_msgfree(res);
418 return (EIO);
421 nentries = ldap_count_entries(a->ldap, res);
422 #ifdef DEBUG
423 dlog("Search found %d entries\n", nentries);
424 #endif /* DEBUG */
425 if (nentries == 0) {
426 ldap_msgfree(res);
427 return (ENOENT);
429 entry = ldap_first_entry(a->ldap, res);
430 vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
431 nvals = ldap_count_values(vals);
432 if (nvals == 0) {
433 plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
434 ldap_value_free(vals);
435 ldap_msgfree(res);
436 ldap_msgfree(entry);
437 return (EIO);
439 #ifdef DEBUG
440 dlog("Map %s, %s => %s\n", map, key, vals[0]);
441 #endif /* DEBUG */
442 if (vals[0]) {
443 *pval = strdup(vals[0]);
444 err = 0;
445 } else {
446 plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
447 err = ENOENT;
449 ldap_msgfree(res);
450 ldap_msgfree(entry);
451 ldap_value_free(vals);
453 return (err);
458 amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
460 ALD *aldh = (ALD *) (m->map_data);
462 if (aldh == NULL) {
463 #ifdef DEBUG
464 dlog("LDAP panic: unable to find map data\n");
465 #endif /* DEBUG */
466 return (ENOENT);
468 if (amu_ldap_rebind(aldh)) {
469 return (ENOENT);
471 if (get_ldap_timestamp(aldh->ldap, map, ts)) {
472 return (ENOENT);
474 return (0);