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