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.
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
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
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 */
68 #endif /* HAVE_CONFIG_H */
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"
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"
98 typedef struct ald_ent ALD
;
99 typedef struct cr_ent CR
;
100 typedef struct he_ent HE_ENT
;
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
);
149 string2he(char *s_orig
)
153 HE_ENT
*first
= NULL
, *cur
= NULL
;
155 if (NULL
== s_orig
|| NULL
== (s
= strdup(s_orig
)))
157 for (p
= strtok(s
, ","); p
; p
= strtok(NULL
, ",")) {
159 cur
->next
= ALLOC(HE_ENT
);
162 first
= cur
= ALLOC(HE_ENT
);
166 if (c
) { /* Host and port */
168 cur
->host
= strdup(p
);
171 cur
->host
= strdup(p
);
172 cur
->port
= LDAP_PORT
;
174 plog(XLOG_USER
, "Adding ldap server %s:%d",
175 cur
->host
, cur
->port
);
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.
197 amu_ldap_unbind(LDAP
*ld
)
200 #ifdef HAVE_SIGACTION
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
;
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 */
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 */
236 cr_free(a
->credentials
);
238 amu_ldap_unbind(a
->ldap
);
244 amu_ldap_init(mnt_map
*m
, char *map
, time_t *ts
)
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
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"));
260 dlog("Map %s is ldap\n", map
);
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
);
276 creds
->method
= LDAP_AUTH_SIMPLE
;
277 aldh
->credentials
= creds
;
280 dlog("Trying for %s:%d\n", aldh
->hostent
->host
, aldh
->hostent
->port
);
281 if (amu_ldap_rebind(aldh
)) {
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
))
289 dlog("Got timestamp for map %s: %ld\n", map
, (u_long
) *ts
);
296 amu_ldap_rebind(ALD
*a
)
300 CR
*c
= a
->credentials
;
301 time_t now
= clocktime(NULL
);
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
);
313 /* Assume all is OK. If it wasn't we'll be back! */
314 dlog("amu_ldap_rebind: timestamp OK\n");
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
);
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
);
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
);
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
);
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) */
354 plog(XLOG_WARNING
, "Exhausted list of ldap servers, looping.\n");
357 plog(XLOG_USER
, "Unable to (re)bind to any ldap hosts\n");
363 get_ldap_timestamp(ALD
*a
, char *map
, time_t *ts
)
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
);
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
,
388 if (err
== LDAP_SUCCESS
)
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
);
400 if (amu_ldap_rebind(a
))
403 dlog("Timestamp search failed, trying again...\n");
406 if (err
!= LDAP_SUCCESS
) {
408 plog(XLOG_USER
, "LDAP timestamp search failed: %s\n",
409 ldap_err2string(err
));
415 nentries
= ldap_count_entries(a
->ldap
, res
);
417 plog(XLOG_USER
, "No timestamp entry for map %s\n", map
);
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
);
428 ldap_value_free(vals
);
432 dlog("TS value is:%s:\n", 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",
442 plog(XLOG_USER
, "Nonpositive timestamp %ld for map %s\n",
447 plog(XLOG_USER
, "Empty timestamp value for map %s\n", map
);
452 ldap_value_free(vals
);
454 dlog("The timestamp for %s is %ld (err=%d)\n", map
, (u_long
) *ts
, 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
];
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
);
474 plog(XLOG_USER
, "LDAP panic: no map data\n");
477 if (amu_ldap_rebind(a
)) /* Check that's the handle is still valid */
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
;
485 *f2
++ = '\\'; *f2
++ = '2'; *f2
++ = 'a';
492 dlog("Search with filter: <%s>\n", filter2
);
493 for (i
= 0; i
< AMD_LDAP_RETRIES
; i
++) {
494 err
= ldap_search_st(a
->ldap
,
502 if (err
== LDAP_SUCCESS
)
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
);
514 if (amu_ldap_rebind(a
))
522 case LDAP_NO_SUCH_OBJECT
:
528 plog(XLOG_USER
, "LDAP search failed: %s\n",
529 ldap_err2string(err
));
535 nentries
= ldap_count_entries(a
->ldap
, res
);
536 dlog("Search found %d entries\n", nentries
);
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
);
545 plog(XLOG_USER
, "Missing value for %s in map %s\n", key
, map
);
546 ldap_value_free(vals
);
550 dlog("Map %s, %s => %s\n", map
, key
, vals
[0]);
552 if (m
->cfm
&& (m
->cfm
->cfm_flags
& CFM_SUN_MAP_SYNTAX
))
553 *pval
= sun_entry2amd(key
, vals
[0]);
555 *pval
= strdup(vals
[0]);
558 plog(XLOG_USER
, "Empty value for %s in map %s\n", key
, map
);
562 ldap_value_free(vals
);
569 amu_ldap_mtime(mnt_map
*m
, char *map
, time_t *ts
)
571 ALD
*aldh
= (ALD
*) (m
->map_data
);
574 dlog("LDAP panic: unable to find map data\n");
577 if (amu_ldap_rebind(aldh
)) {
580 if (get_ldap_timestamp(aldh
, map
, ts
)) {