info_ldap.c revision 38494
1/*
2 * Copyright (c) 1997-1998 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 acknowledgement:
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 5.2.2.1 1992/02/09 15:08:29 jsp beta $
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 */
82typedef struct ald_ent ALD;
83typedef struct cr_ent CR;
84typedef struct he_ent HE;
85
86/*
87 * STRUCTURES:
88 */
89struct ald_ent {
90  LDAP *ldap;
91  HE *hostent;
92  CR *credentials;
93  time_t timestamp;
94};
95
96struct cr_ent {
97  char *who;
98  char *pw;
99  int method;
100};
101
102struct he_ent {
103  char *host;
104  int port;
105  struct he_ent *next;
106};
107
108/*
109 * FORWARD DECLARATIONS:
110 */
111static int amu_ldap_rebind(ALD *a);
112static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts);
113
114
115/*
116 * FUNCTIONS:
117 */
118
119static void
120he_free(HE *h)
121{
122  XFREE(h->host);
123  if (h->next != NULL)
124    he_free(h->next);
125  XFREE(h);
126}
127
128
129static HE *
130string2he(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
159static void
160cr_free(CR *c)
161{
162  XFREE(c->who);
163  XFREE(c->pw);
164  XFREE(c);
165}
166
167
168static void
169ald_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
179int
180amu_ldap_init(mnt_map *m, char *map, time_t *ts)
181{
182  ALD *aldh;
183  CR *creds;
184
185  if (!STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
186    return (ENOENT);
187  }
188#ifdef DEBUG
189  else {
190    dlog("Map %s is ldap\n", map);
191  }
192#endif /* DEBUG */
193
194  aldh = (ALD *) xmalloc(sizeof(ALD));
195  creds = (CR *) xmalloc(sizeof(CR));
196
197  aldh->hostent = string2he(gopt.ldap_hostports);
198  if (aldh->hostent == NULL) {
199    plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
200	 gopt.ldap_hostports, map);
201    return (ENOENT);
202  }
203  creds->who = "";
204  creds->pw = "";
205  creds->method = LDAP_AUTH_SIMPLE;
206  aldh->credentials = creds;
207  aldh->timestamp = 0;
208#ifdef DEBUG
209  dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
210#endif /* DEBUG */
211  if (amu_ldap_rebind(aldh)) {
212    ald_free(aldh);
213    return (ENOENT);
214  }
215  m->map_data = (void *) aldh;
216#ifdef DEBUG
217  dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
218#endif /* DEBUG */
219  if (get_ldap_timestamp(aldh->ldap, map, ts))
220    return (ENOENT);
221#ifdef DEBUG
222  dlog("Got timestamp for map %s: %d\n", map, *ts);
223#endif /* DEBUG */
224
225  return (0);
226}
227
228
229static int
230amu_ldap_rebind(ALD *a)
231{
232  LDAP *ld;
233  HE *h;
234  CR *c = a->credentials;
235  time_t now = clocktime();
236
237  if (a->ldap != NULL) {
238    if ((a->timestamp - now) > AMD_LDAP_TTL) {
239#ifdef DEBUG
240      dlog("Reestablishing ldap connection\n");
241#endif /* DEBUG */
242      ldap_unbind(a->ldap);
243      a->timestamp = now;
244    } else
245      return (0);
246  }
247
248  while (TRUE) {
249    for (h = a->hostent; h != NULL; h = h->next) {
250      if ((ld = ldap_open(h->host, h->port)) == NULL) {
251	plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
252	break;
253      }
254      if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
255	plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
256	     h->host, h->port, c->who);
257	break;
258      }
259      if (gopt.ldap_cache_seconds > 0) {
260	ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
261	a->ldap = ld;
262	a->timestamp = now;
263	return (0);
264      }
265    }
266    plog(XLOG_WARNING, "Exausted list of ldap servers, looping.\n");
267  }
268
269  plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
270  return (ENOENT);
271}
272
273
274static int
275get_ldap_timestamp(LDAP * ld, char *map, time_t *ts)
276{
277  struct timeval tv;
278  char **vals, *end;
279  char filter[MAXPATHLEN];
280  int i, err, nentries = 0;
281  LDAPMessage *res, *entry;
282
283  tv.tv_sec = 3;
284  tv.tv_usec = 0;
285  sprintf(filter, AMD_LDAP_TSFILTER, map);
286#ifdef DEBUG
287  dlog("Getting timestamp for map %s\n", map);
288  dlog("Filter is: %s\n", filter);
289  dlog("Base is: %s\n", gopt.ldap_base);
290#endif /* DEBUG */
291  for (i = 0; i < AMD_LDAP_RETRIES; i++) {
292    err = ldap_search_st(ld,
293			 gopt.ldap_base,
294			 LDAP_SCOPE_SUBTREE,
295			 filter,
296			 0,
297			 0,
298			 &tv,
299			 &res);
300    if (err == LDAP_SUCCESS)
301      break;
302    dlog("Timestamp search timed out, trying again...\n");
303  }
304
305  if (err != LDAP_SUCCESS) {
306    *ts = 0;
307    plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
308	 ldap_err2string(ld->ld_errno));
309    return (ENOENT);
310  }
311
312  nentries = ldap_count_entries(ld, res);
313  if (nentries == 0) {
314    plog(XLOG_USER, "No timestamp entry for map %s\n", map);
315    *ts = 0;
316    ldap_msgfree(res);
317    return (ENOENT);
318  }
319
320  entry = ldap_first_entry(ld, res);
321  vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR);
322  if (ldap_count_values(vals) == 0) {
323    plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
324    *ts = 0;
325    ldap_value_free(vals);
326    ldap_msgfree(res);
327    ldap_msgfree(entry);
328    return (ENOENT);
329  }
330#ifdef DEBUG
331  dlog("TS value is:%s:\n", vals[0]);
332#endif /* DEBUG */
333
334  if (vals[0]) {
335    *ts = (time_t) strtol(vals[0], &end, 10);
336    if (end == vals[0]) {
337      plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
338	   vals[0], map);
339      err = ENOENT;
340    }
341    if (!*ts > 0) {
342      plog(XLOG_USER, "Nonpositive timestamp %d for map %s\n",
343	   *ts, map);
344      err = ENOENT;
345    }
346  } else {
347    plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
348    *ts = 0;
349    err = ENOENT;
350  }
351
352  ldap_value_free(vals);
353  ldap_msgfree(res);
354  ldap_msgfree(entry);
355#ifdef DEBUG
356  dlog("The timestamp for %s is %d (err=%d)\n", map, *ts, err);
357#endif /* DEBUG */
358  return (err);
359}
360
361
362int
363amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
364{
365  char **vals, filter[MAXPATHLEN];
366  struct timeval tv;
367  int i, err, nvals = 0, nentries = 0;
368  LDAPMessage *entry, *res;
369  ALD *a = (ALD *) (m->map_data);
370
371  tv.tv_sec = 2;
372  tv.tv_usec = 0;
373  if (a == NULL) {
374    plog(XLOG_USER, "LDAP panic: no map data\n");
375    return (EIO);
376  }
377  if (amu_ldap_rebind(a))	/* Check that's the handle is still valid */
378    return (ENOENT);
379
380  sprintf(filter, AMD_LDAP_FILTER, map, key);
381#ifdef DEBUG
382  dlog("Search with filter: %s\n", filter);
383#endif /* DEBUG */
384  for (i = 0; i < AMD_LDAP_RETRIES; i++) {
385    err = ldap_search_st(a->ldap,
386			 gopt.ldap_base,
387			 LDAP_SCOPE_SUBTREE,
388			 filter,
389			 0,
390			 0,
391			 &tv,
392			 &res);
393    if (err == LDAP_SUCCESS)
394      break;
395  }
396
397  switch (err) {
398  case LDAP_SUCCESS:
399    break;
400  case LDAP_NO_SUCH_OBJECT:
401#ifdef DEBUG
402    dlog("No object\n");
403#endif /* DEBUG */
404    ldap_msgfree(res);
405    return (ENOENT);
406  default:
407    plog(XLOG_USER, "LDAP search failed: %s\n",
408	 ldap_err2string(a->ldap->ld_errno));
409    ldap_msgfree(res);
410    return (EIO);
411  }
412
413  nentries = ldap_count_entries(a->ldap, res);
414#ifdef DEBUG
415  dlog("Search found %d entries\n", nentries);
416#endif /* DEBUG */
417  if (nentries == 0) {
418    ldap_msgfree(res);
419    return (ENOENT);
420  }
421  entry = ldap_first_entry(a->ldap, res);
422  vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
423  nvals = ldap_count_values(vals);
424  if (nvals == 0) {
425    plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
426    ldap_value_free(vals);
427    ldap_msgfree(res);
428    ldap_msgfree(entry);
429    return (EIO);
430  }
431#ifdef DEBUG
432  dlog("Map %s, %s => %s\n", map, key, vals[0]);
433#endif /* DEBUG */
434  if (vals[0]) {
435    *pval = strdup(vals[0]);
436    err = 0;
437  } else {
438    plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
439    err = ENOENT;
440  }
441  ldap_msgfree(res);
442  ldap_msgfree(entry);
443  ldap_value_free(vals);
444
445  return (err);
446}
447
448
449int
450amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
451{
452  ALD *aldh = (ALD *) (m->map_data);
453
454  if (aldh == NULL) {
455    dlog("LDAP panic: unable to find map data\n");
456    return (ENOENT);
457  }
458  if (amu_ldap_rebind(aldh)) {
459    return (ENOENT);
460  }
461  if (get_ldap_timestamp(aldh->ldap, map, ts)) {
462    return (ENOENT);
463  }
464  return (0);
465}
466