]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/amd/amd/info_ldap.c
This commit was generated by cvs2svn to compensate for changes in r52744,
[FreeBSD/FreeBSD.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.5 1999/08/22 05:12:50 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
242   if (a->ldap != NULL) {
243     if ((a->timestamp - now) > AMD_LDAP_TTL) {
244 #ifdef DEBUG
245       dlog("Reestablishing ldap connection\n");
246 #endif /* DEBUG */
247       ldap_unbind(a->ldap);
248       a->timestamp = now;
249     } else
250       return (0);
251   }
252
253   while (TRUE) {
254     for (h = a->hostent; h != NULL; h = h->next) {
255       if ((ld = ldap_open(h->host, h->port)) == NULL) {
256         plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
257         break;
258       }
259       if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
260         plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
261              h->host, h->port, c->who);
262         break;
263       }
264       if (gopt.ldap_cache_seconds > 0) {
265         ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
266         a->ldap = ld;
267         a->timestamp = now;
268         return (0);
269       }
270     }
271     plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
272   }
273
274   plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
275   return (ENOENT);
276 }
277
278
279 static int
280 get_ldap_timestamp(LDAP * ld, char *map, time_t *ts)
281 {
282   struct timeval tv;
283   char **vals, *end;
284   char filter[MAXPATHLEN];
285   int i, err = 0, nentries = 0;
286   LDAPMessage *res, *entry;
287
288   tv.tv_sec = 3;
289   tv.tv_usec = 0;
290   sprintf(filter, AMD_LDAP_TSFILTER, map);
291 #ifdef DEBUG
292   dlog("Getting timestamp for map %s\n", map);
293   dlog("Filter is: %s\n", filter);
294   dlog("Base is: %s\n", gopt.ldap_base);
295 #endif /* DEBUG */
296   for (i = 0; i < AMD_LDAP_RETRIES; i++) {
297     err = ldap_search_st(ld,
298                          gopt.ldap_base,
299                          LDAP_SCOPE_SUBTREE,
300                          filter,
301                          0,
302                          0,
303                          &tv,
304                          &res);
305     if (err == LDAP_SUCCESS)
306       break;
307 #ifdef DEBUG
308     dlog("Timestamp search timed out, trying again...\n");
309 #endif /* DEBUG */
310   }
311
312   if (err != LDAP_SUCCESS) {
313     *ts = 0;
314     plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
315          ldap_err2string(ld->ld_errno));
316     return (ENOENT);
317   }
318
319   nentries = ldap_count_entries(ld, res);
320   if (nentries == 0) {
321     plog(XLOG_USER, "No timestamp entry for map %s\n", map);
322     *ts = 0;
323     ldap_msgfree(res);
324     return (ENOENT);
325   }
326
327   entry = ldap_first_entry(ld, res);
328   vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR);
329   if (ldap_count_values(vals) == 0) {
330     plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
331     *ts = 0;
332     ldap_value_free(vals);
333     ldap_msgfree(res);
334     ldap_msgfree(entry);
335     return (ENOENT);
336   }
337 #ifdef DEBUG
338   dlog("TS value is:%s:\n", vals[0]);
339 #endif /* DEBUG */
340
341   if (vals[0]) {
342     *ts = (time_t) strtol(vals[0], &end, 10);
343     if (end == vals[0]) {
344       plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
345            vals[0], map);
346       err = ENOENT;
347     }
348     if (!*ts > 0) {
349       plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n",
350            *ts, map);
351       err = ENOENT;
352     }
353   } else {
354     plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
355     *ts = 0;
356     err = ENOENT;
357   }
358
359   ldap_value_free(vals);
360   ldap_msgfree(res);
361   ldap_msgfree(entry);
362 #ifdef DEBUG
363   dlog("The timestamp for %s is %ld (err=%d)\n", map, *ts, err);
364 #endif /* DEBUG */
365   return (err);
366 }
367
368
369 int
370 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
371 {
372   char **vals, filter[MAXPATHLEN];
373   struct timeval tv;
374   int i, err = 0, nvals = 0, nentries = 0;
375   LDAPMessage *entry, *res;
376   ALD *a = (ALD *) (m->map_data);
377
378   tv.tv_sec = 2;
379   tv.tv_usec = 0;
380   if (a == NULL) {
381     plog(XLOG_USER, "LDAP panic: no map data\n");
382     return (EIO);
383   }
384   if (amu_ldap_rebind(a))       /* Check that's the handle is still valid */
385     return (ENOENT);
386
387   sprintf(filter, AMD_LDAP_FILTER, map, key);
388 #ifdef DEBUG
389   dlog("Search with filter: %s\n", filter);
390 #endif /* DEBUG */
391   for (i = 0; i < AMD_LDAP_RETRIES; i++) {
392     err = ldap_search_st(a->ldap,
393                          gopt.ldap_base,
394                          LDAP_SCOPE_SUBTREE,
395                          filter,
396                          0,
397                          0,
398                          &tv,
399                          &res);
400     if (err == LDAP_SUCCESS)
401       break;
402   }
403
404   switch (err) {
405   case LDAP_SUCCESS:
406     break;
407   case LDAP_NO_SUCH_OBJECT:
408 #ifdef DEBUG
409     dlog("No object\n");
410 #endif /* DEBUG */
411     ldap_msgfree(res);
412     return (ENOENT);
413   default:
414     plog(XLOG_USER, "LDAP search failed: %s\n",
415          ldap_err2string(a->ldap->ld_errno));
416     ldap_msgfree(res);
417     return (EIO);
418   }
419
420   nentries = ldap_count_entries(a->ldap, res);
421 #ifdef DEBUG
422   dlog("Search found %d entries\n", nentries);
423 #endif /* DEBUG */
424   if (nentries == 0) {
425     ldap_msgfree(res);
426     return (ENOENT);
427   }
428   entry = ldap_first_entry(a->ldap, res);
429   vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
430   nvals = ldap_count_values(vals);
431   if (nvals == 0) {
432     plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
433     ldap_value_free(vals);
434     ldap_msgfree(res);
435     ldap_msgfree(entry);
436     return (EIO);
437   }
438 #ifdef DEBUG
439   dlog("Map %s, %s => %s\n", map, key, vals[0]);
440 #endif /* DEBUG */
441   if (vals[0]) {
442     *pval = strdup(vals[0]);
443     err = 0;
444   } else {
445     plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
446     err = ENOENT;
447   }
448   ldap_msgfree(res);
449   ldap_msgfree(entry);
450   ldap_value_free(vals);
451
452   return (err);
453 }
454
455
456 int
457 amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
458 {
459   ALD *aldh = (ALD *) (m->map_data);
460
461   if (aldh == NULL) {
462 #ifdef DEBUG
463     dlog("LDAP panic: unable to find map data\n");
464 #endif /* DEBUG */
465     return (ENOENT);
466   }
467   if (amu_ldap_rebind(aldh)) {
468     return (ENOENT);
469   }
470   if (get_ldap_timestamp(aldh->ldap, map, ts)) {
471     return (ENOENT);
472   }
473   return (0);
474 }