Merge branch 'vendor/DIFFUTILS'
[dragonfly.git] / contrib / amd / amd / info_ldap.c
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.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
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.
26  *
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.
38  *
39  *      %W% (Berkeley) %G%
40  *
41  * $Id: info_ldap.c,v 1.6 1999/09/30 21:01:31 ezk Exp $
42  *
43  */
44
45
46 /*
47  * Get info from LDAP (Lightweight Directory Access Protocol)
48  * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
49  */
50
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif /* HAVE_CONFIG_H */
54 #include <am_defs.h>
55 #include <amd.h>
56
57
58 /*
59  * MACROS:
60  */
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 */
69
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"
78
79 /*
80  * TYPEDEFS:
81  */
82 typedef struct ald_ent ALD;
83 typedef struct cr_ent CR;
84 typedef struct he_ent HE;
85
86 /*
87  * STRUCTURES:
88  */
89 struct ald_ent {
90   LDAP *ldap;
91   HE *hostent;
92   CR *credentials;
93   time_t timestamp;
94 };
95
96 struct cr_ent {
97   char *who;
98   char *pw;
99   int method;
100 };
101
102 struct he_ent {
103   char *host;
104   int port;
105   struct he_ent *next;
106 };
107
108 /*
109  * FORWARD DECLARATIONS:
110  */
111 static int amu_ldap_rebind(ALD *a);
112 static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts);
113
114
115 /*
116  * FUNCTIONS:
117  */
118
119 static void
120 he_free(HE *h)
121 {
122   XFREE(h->host);
123   if (h->next != NULL)
124     he_free(h->next);
125   XFREE(h);
126 }
127
128
129 static HE *
130 string2he(char *s)
131 {
132   char *c, *p;
133   HE *new, *old = NULL;
134
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;
145     }
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);
153
154   }
155   return (old);
156 }
157
158
159 static void
160 cr_free(CR *c)
161 {
162   XFREE(c->who);
163   XFREE(c->pw);
164   XFREE(c);
165 }
166
167
168 static void
169 ald_free(ALD *a)
170 {
171   he_free(a->hostent);
172   cr_free(a->credentials);
173   if (a->ldap != NULL)
174     ldap_unbind(a->ldap);
175   XFREE(a);
176 }
177
178
179 int
180 amu_ldap_init(mnt_map *m, char *map, time_t *ts)
181 {
182   ALD *aldh;
183   CR *creds;
184
185   /*
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?
189    */
190   if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
191     return (ENOENT);
192   }
193 #ifdef DEBUG
194   else {
195     dlog("Map %s is ldap\n", map);
196   }
197 #endif /* DEBUG */
198
199   aldh = (ALD *) xmalloc(sizeof(ALD));
200   creds = (CR *) xmalloc(sizeof(CR));
201
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);
207   }
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);
219   }
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 */
229
230   return (0);
231 }
232
233
234 static int
235 amu_ldap_rebind(ALD *a)
236 {
237   LDAP *ld;
238   HE *h;
239   CR *c = a->credentials;
240   time_t now = clocktime();
241   int try;
242
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);
252   }
253
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;
259       }
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;
264       }
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);
270       }
271     }
272     plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
273   }
274
275   plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
276   return (ENOENT);
277 }
278
279
280 static int
281 get_ldap_timestamp(LDAP * ld, char *map, time_t *ts)
282 {
283   struct timeval tv;
284   char **vals, *end;
285   char filter[MAXPATHLEN];
286   int i, err = 0, nentries = 0;
287   LDAPMessage *res, *entry;
288
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,
302                          0,
303                          0,
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 */
311   }
312
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);
318   }
319
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);
326   }
327
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);
337   }
338 #ifdef DEBUG
339   dlog("TS value is:%s:\n", vals[0]);
340 #endif /* DEBUG */
341
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;
348     }
349     if (!*ts > 0) {
350       plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n",
351            *ts, map);
352       err = ENOENT;
353     }
354   } else {
355     plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
356     *ts = 0;
357     err = ENOENT;
358   }
359
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);
367 }
368
369
370 int
371 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
372 {
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);
378
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);
384   }
385   if (amu_ldap_rebind(a))       /* Check that's the handle is still valid */
386     return (ENOENT);
387
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,
397                          0,
398                          0,
399                          &tv,
400                          &res);
401     if (err == LDAP_SUCCESS)
402       break;
403   }
404
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);
419   }
420
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);
428   }
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);
438   }
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;
448   }
449   ldap_msgfree(res);
450   ldap_msgfree(entry);
451   ldap_value_free(vals);
452
453   return (err);
454 }
455
456
457 int
458 amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
459 {
460   ALD *aldh = (ALD *) (m->map_data);
461
462   if (aldh == NULL) {
463 #ifdef DEBUG
464     dlog("LDAP panic: unable to find map data\n");
465 #endif /* DEBUG */
466     return (ENOENT);
467   }
468   if (amu_ldap_rebind(aldh)) {
469     return (ENOENT);
470   }
471   if (get_ldap_timestamp(aldh->ldap, map, ts)) {
472     return (ENOENT);
473   }
474   return (0);
475 }