No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / sdb / ldap / ldapdb.c
blob935c7673c0ef2cc86d96e6d3807eeb65f5f8b907
1 /* $NetBSD$ */
3 /*
4 * ldapdb.c version 1.0-beta
6 * Copyright (C) 2002, 2004 Stig Venaas
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * Contributors: Jeremy C. McDermond
16 * If you want to use TLS, uncomment the define below
18 /* #define LDAPDB_TLS */
21 * If you are using an old LDAP API uncomment the define below. Only do this
22 * if you know what you're doing or get compilation errors on ldap_memfree().
23 * This also forces LDAPv2.
25 /* #define LDAPDB_RFC1823API */
27 /* Using LDAPv3 by default, change this if you want v2 */
28 #ifndef LDAPDB_LDAP_VERSION
29 #define LDAPDB_LDAP_VERSION 3
30 #endif
32 #include <config.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <ctype.h>
39 #include <isc/mem.h>
40 #include <isc/print.h>
41 #include <isc/result.h>
42 #include <isc/util.h>
43 #include <isc/thread.h>
45 #include <dns/sdb.h>
47 #include <named/globals.h>
48 #include <named/log.h>
50 #include <ldap.h>
51 #include "ldapdb.h"
54 * A simple database driver for LDAP
55 */
57 /* enough for name with 8 labels of max length */
58 #define MAXNAMELEN 519
60 static dns_sdbimplementation_t *ldapdb = NULL;
62 struct ldapdb_data {
63 char *hostport;
64 char *hostname;
65 int portno;
66 char *base;
67 int defaultttl;
68 char *filterall;
69 int filteralllen;
70 char *filterone;
71 int filteronelen;
72 char *filtername;
73 char *bindname;
74 char *bindpw;
75 #ifdef LDAPDB_TLS
76 int tls;
77 #endif
80 /* used by ldapdb_getconn */
82 struct ldapdb_entry {
83 void *index;
84 size_t size;
85 void *data;
86 struct ldapdb_entry *next;
89 static struct ldapdb_entry *ldapdb_find(struct ldapdb_entry *stack,
90 const void *index, size_t size) {
91 while (stack != NULL) {
92 if (stack->size == size && !memcmp(stack->index, index, size))
93 return stack;
94 stack = stack->next;
96 return NULL;
99 static void ldapdb_insert(struct ldapdb_entry **stack,
100 struct ldapdb_entry *item) {
101 item->next = *stack;
102 *stack = item;
105 static void ldapdb_lock(int what) {
106 static isc_mutex_t lock;
108 switch (what) {
109 case 0:
110 isc_mutex_init(&lock);
111 break;
112 case 1:
113 LOCK(&lock);
114 break;
115 case -1:
116 UNLOCK(&lock);
117 break;
121 /* data == NULL means cleanup */
122 static LDAP **
123 ldapdb_getconn(struct ldapdb_data *data)
125 static struct ldapdb_entry *allthreadsdata = NULL;
126 struct ldapdb_entry *threaddata, *conndata;
127 unsigned long threadid;
129 if (data == NULL) {
130 /* cleanup */
131 /* lock out other threads */
132 ldapdb_lock(1);
133 while (allthreadsdata != NULL) {
134 threaddata = allthreadsdata;
135 free(threaddata->index);
136 while (threaddata->data != NULL) {
137 conndata = threaddata->data;
138 free(conndata->index);
139 if (conndata->data != NULL)
140 ldap_unbind((LDAP *)conndata->data);
141 threaddata->data = conndata->next;
142 free(conndata);
144 allthreadsdata = threaddata->next;
145 free(threaddata);
147 ldapdb_lock(-1);
148 return (NULL);
151 /* look for connection data for current thread */
152 threadid = isc_thread_self();
153 threaddata = ldapdb_find(allthreadsdata, &threadid, sizeof(threadid));
154 if (threaddata == NULL) {
155 /* no data for this thread, create empty connection list */
156 threaddata = malloc(sizeof(*threaddata));
157 if (threaddata == NULL)
158 return (NULL);
159 threaddata->index = malloc(sizeof(threadid));
160 if (threaddata->index == NULL) {
161 free(threaddata);
162 return (NULL);
164 *(unsigned long *)threaddata->index = threadid;
165 threaddata->size = sizeof(threadid);
166 threaddata->data = NULL;
168 /* need to lock out other threads here */
169 ldapdb_lock(1);
170 ldapdb_insert(&allthreadsdata, threaddata);
171 ldapdb_lock(-1);
174 /* threaddata points at the connection list for current thread */
175 /* look for existing connection to our server */
176 conndata = ldapdb_find((struct ldapdb_entry *)threaddata->data,
177 data->hostport, strlen(data->hostport));
178 if (conndata == NULL) {
179 /* no connection data structure for this server, create one */
180 conndata = malloc(sizeof(*conndata));
181 if (conndata == NULL)
182 return (NULL);
183 conndata->index = data->hostport;
184 conndata->size = strlen(data->hostport);
185 conndata->data = NULL;
186 ldapdb_insert((struct ldapdb_entry **)&threaddata->data,
187 conndata);
190 return (LDAP **)&conndata->data;
193 static void
194 ldapdb_bind(struct ldapdb_data *data, LDAP **ldp)
196 #ifndef LDAPDB_RFC1823API
197 const int ver = LDAPDB_LDAP_VERSION;
198 #endif
200 if (*ldp != NULL)
201 ldap_unbind(*ldp);
202 *ldp = ldap_open(data->hostname, data->portno);
203 if (*ldp == NULL)
204 return;
206 #ifndef LDAPDB_RFC1823API
207 ldap_set_option(*ldp, LDAP_OPT_PROTOCOL_VERSION, &ver);
208 #endif
210 #ifdef LDAPDB_TLS
211 if (data->tls) {
212 ldap_start_tls_s(*ldp, NULL, NULL);
214 #endif
216 if (ldap_simple_bind_s(*ldp, data->bindname, data->bindpw) != LDAP_SUCCESS) {
217 ldap_unbind(*ldp);
218 *ldp = NULL;
222 static isc_result_t
223 ldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata)
225 struct ldapdb_data *data = dbdata;
226 isc_result_t result = ISC_R_NOTFOUND;
227 LDAP **ldp;
228 LDAPMessage *res, *e;
229 char *fltr, *a, **vals = NULL, **names = NULL;
230 char type[64];
231 #ifdef LDAPDB_RFC1823API
232 void *ptr;
233 #else
234 BerElement *ptr;
235 #endif
236 int i, j, errno, msgid;
238 ldp = ldapdb_getconn(data);
239 if (ldp == NULL)
240 return (ISC_R_FAILURE);
241 if (*ldp == NULL) {
242 ldapdb_bind(data, ldp);
243 if (*ldp == NULL) {
244 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
245 "LDAP sdb zone '%s': bind failed", zone);
246 return (ISC_R_FAILURE);
250 if (name == NULL) {
251 fltr = data->filterall;
252 } else {
253 if (strlen(name) > MAXNAMELEN) {
254 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
255 "LDAP sdb zone '%s': name %s too long", zone, name);
256 return (ISC_R_FAILURE);
258 sprintf(data->filtername, "%s))", name);
259 fltr = data->filterone;
262 msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
263 if (msgid == -1) {
264 ldapdb_bind(data, ldp);
265 if (*ldp != NULL)
266 msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
269 if (*ldp == NULL || msgid == -1) {
270 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
271 "LDAP sdb zone '%s': search failed, filter %s", zone, fltr);
272 return (ISC_R_FAILURE);
275 /* Get the records one by one as they arrive and return them to bind */
276 while ((errno = ldap_result(*ldp, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) {
277 LDAP *ld = *ldp;
278 int ttl = data->defaultttl;
280 /* not supporting continuation references at present */
281 if (errno != LDAP_RES_SEARCH_ENTRY) {
282 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
283 "LDAP sdb zone '%s': ldap_result returned %d", zone, errno);
284 ldap_msgfree(res);
285 return (ISC_R_FAILURE);
288 /* only one entry per result message */
289 e = ldap_first_entry(ld, res);
290 if (e == NULL) {
291 ldap_msgfree(res);
292 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
293 "LDAP sdb zone '%s': ldap_first_entry failed", zone);
294 return (ISC_R_FAILURE);
297 if (name == NULL) {
298 names = ldap_get_values(ld, e, "relativeDomainName");
299 if (names == NULL)
300 continue;
303 vals = ldap_get_values(ld, e, "dNSTTL");
304 if (vals != NULL) {
305 ttl = atoi(vals[0]);
306 ldap_value_free(vals);
309 for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; a = ldap_next_attribute(ld, e, ptr)) {
310 char *s;
312 for (s = a; *s; s++)
313 *s = toupper(*s);
314 s = strstr(a, "RECORD");
315 if ((s == NULL) || (s == a) || (s - a >= (signed int)sizeof(type))) {
316 #ifndef LDAPDB_RFC1823API
317 ldap_memfree(a);
318 #endif
319 continue;
322 strncpy(type, a, s - a);
323 type[s - a] = '\0';
324 vals = ldap_get_values(ld, e, a);
325 if (vals != NULL) {
326 for (i = 0; vals[i] != NULL; i++) {
327 if (name != NULL) {
328 result = dns_sdb_putrr(retdata, type, ttl, vals[i]);
329 } else {
330 for (j = 0; names[j] != NULL; j++) {
331 result = dns_sdb_putnamedrr(retdata, names[j], type, ttl, vals[i]);
332 if (result != ISC_R_SUCCESS)
333 break;
336 ; if (result != ISC_R_SUCCESS) {
337 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
338 "LDAP sdb zone '%s': dns_sdb_put... failed for %s", zone, vals[i]);
339 ldap_value_free(vals);
340 #ifndef LDAPDB_RFC1823API
341 ldap_memfree(a);
342 if (ptr != NULL)
343 ber_free(ptr, 0);
344 #endif
345 if (name == NULL)
346 ldap_value_free(names);
347 ldap_msgfree(res);
348 return (ISC_R_FAILURE);
351 ldap_value_free(vals);
353 #ifndef LDAPDB_RFC1823API
354 ldap_memfree(a);
355 #endif
357 #ifndef LDAPDB_RFC1823API
358 if (ptr != NULL)
359 ber_free(ptr, 0);
360 #endif
361 if (name == NULL)
362 ldap_value_free(names);
364 /* free this result */
365 ldap_msgfree(res);
368 /* free final result */
369 ldap_msgfree(res);
370 return (result);
374 /* callback routines */
375 static isc_result_t
376 ldapdb_lookup(const char *zone, const char *name, void *dbdata,
377 dns_sdblookup_t *lookup)
379 return ldapdb_search(zone, name, dbdata, lookup);
382 static isc_result_t
383 ldapdb_allnodes(const char *zone, void *dbdata,
384 dns_sdballnodes_t *allnodes)
386 return ldapdb_search(zone, NULL, dbdata, allnodes);
389 static char *
390 unhex(char *in)
392 static const char hexdigits[] = "0123456789abcdef";
393 char *p, *s = in;
394 int d1, d2;
396 while ((s = strchr(s, '%'))) {
397 if (!(s[1] && s[2]))
398 return NULL;
399 if ((p = strchr(hexdigits, tolower(s[1]))) == NULL)
400 return NULL;
401 d1 = p - hexdigits;
402 if ((p = strchr(hexdigits, tolower(s[2]))) == NULL)
403 return NULL;
404 d2 = p - hexdigits;
405 *s++ = d1 << 4 | d2;
406 memmove(s, s + 2, strlen(s) - 1);
408 return in;
411 /* returns 0 for ok, -1 for bad syntax, -2 for unknown critical extension */
412 static int
413 parseextensions(char *extensions, struct ldapdb_data *data)
415 char *s, *next, *name, *value;
416 int critical;
418 while (extensions != NULL) {
419 s = strchr(extensions, ',');
420 if (s != NULL) {
421 *s++ = '\0';
422 next = s;
423 } else {
424 next = NULL;
427 if (*extensions != '\0') {
428 s = strchr(extensions, '=');
429 if (s != NULL) {
430 *s++ = '\0';
431 value = *s != '\0' ? s : NULL;
432 } else {
433 value = NULL;
435 name = extensions;
437 critical = *name == '!';
438 if (critical) {
439 name++;
441 if (*name == '\0') {
442 return -1;
445 if (!strcasecmp(name, "bindname")) {
446 data->bindname = value;
447 } else if (!strcasecmp(name, "x-bindpw")) {
448 data->bindpw = value;
449 #ifdef LDAPDB_TLS
450 } else if (!strcasecmp(name, "x-tls")) {
451 data->tls = value == NULL || !strcasecmp(value, "true");
452 #endif
453 } else if (critical) {
454 return -2;
457 extensions = next;
459 return 0;
462 static void
463 free_data(struct ldapdb_data *data)
465 if (data->hostport != NULL)
466 isc_mem_free(ns_g_mctx, data->hostport);
467 if (data->hostname != NULL)
468 isc_mem_free(ns_g_mctx, data->hostname);
469 if (data->filterall != NULL)
470 isc_mem_put(ns_g_mctx, data->filterall, data->filteralllen);
471 if (data->filterone != NULL)
472 isc_mem_put(ns_g_mctx, data->filterone, data->filteronelen);
473 isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
477 static isc_result_t
478 ldapdb_create(const char *zone, int argc, char **argv,
479 void *driverdata, void **dbdata)
481 struct ldapdb_data *data;
482 char *s, *filter = NULL, *extensions = NULL;
483 int defaultttl;
485 UNUSED(driverdata);
487 /* we assume that only one thread will call create at a time */
488 /* want to do this only once for all instances */
490 if ((argc < 2)
491 || (argv[0] != strstr( argv[0], "ldap://"))
492 || ((defaultttl = atoi(argv[1])) < 1))
493 return (ISC_R_FAILURE);
494 data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data));
495 if (data == NULL)
496 return (ISC_R_NOMEMORY);
498 memset(data, 0, sizeof(struct ldapdb_data));
499 data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
500 if (data->hostport == NULL) {
501 free_data(data);
502 return (ISC_R_NOMEMORY);
505 data->defaultttl = defaultttl;
507 s = strchr(data->hostport, '/');
508 if (s != NULL) {
509 *s++ = '\0';
510 data->base = s;
511 /* attrs, scope, filter etc? */
512 s = strchr(s, '?');
513 if (s != NULL) {
514 *s++ = '\0';
515 /* ignore attributes */
516 s = strchr(s, '?');
517 if (s != NULL) {
518 *s++ = '\0';
519 /* ignore scope */
520 s = strchr(s, '?');
521 if (s != NULL) {
522 *s++ = '\0';
523 /* filter */
524 filter = s;
525 s = strchr(s, '?');
526 if (s != NULL) {
527 *s++ = '\0';
528 /* extensions */
529 extensions = s;
530 s = strchr(s, '?');
531 if (s != NULL) {
532 *s++ = '\0';
534 if (*extensions == '\0') {
535 extensions = NULL;
538 if (*filter == '\0') {
539 filter = NULL;
544 if (*data->base == '\0') {
545 data->base = NULL;
549 /* parse extensions */
550 if (extensions != NULL) {
551 int err;
553 err = parseextensions(extensions, data);
554 if (err < 0) {
555 /* err should be -1 or -2 */
556 free_data(data);
557 if (err == -1) {
558 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
559 "LDAP sdb zone '%s': URL: extension syntax error", zone);
560 } else if (err == -2) {
561 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
562 "LDAP sdb zone '%s': URL: unknown critical extension", zone);
564 return (ISC_R_FAILURE);
568 if ((data->base != NULL && unhex(data->base) == NULL) ||
569 (filter != NULL && unhex(filter) == NULL) ||
570 (data->bindname != NULL && unhex(data->bindname) == NULL) ||
571 (data->bindpw != NULL && unhex(data->bindpw) == NULL)) {
572 free_data(data);
573 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
574 "LDAP sdb zone '%s': URL: bad hex values", zone);
575 return (ISC_R_FAILURE);
578 /* compute filterall and filterone once and for all */
579 if (filter == NULL) {
580 data->filteralllen = strlen(zone) + strlen("(zoneName=)") + 1;
581 data->filteronelen = strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
582 } else {
583 data->filteralllen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=))") + 1;
584 data->filteronelen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
587 data->filterall = isc_mem_get(ns_g_mctx, data->filteralllen);
588 if (data->filterall == NULL) {
589 free_data(data);
590 return (ISC_R_NOMEMORY);
592 data->filterone = isc_mem_get(ns_g_mctx, data->filteronelen);
593 if (data->filterone == NULL) {
594 free_data(data);
595 return (ISC_R_NOMEMORY);
598 if (filter == NULL) {
599 sprintf(data->filterall, "(zoneName=%s)", zone);
600 sprintf(data->filterone, "(&(zoneName=%s)(relativeDomainName=", zone);
601 } else {
602 sprintf(data->filterall, "(&%s(zoneName=%s))", filter, zone);
603 sprintf(data->filterone, "(&%s(zoneName=%s)(relativeDomainName=", filter, zone);
605 data->filtername = data->filterone + strlen(data->filterone);
607 /* support URLs with literal IPv6 addresses */
608 data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport + (*data->hostport == '[' ? 1 : 0));
609 if (data->hostname == NULL) {
610 free_data(data);
611 return (ISC_R_NOMEMORY);
614 if (*data->hostport == '[' &&
615 (s = strchr(data->hostname, ']')) != NULL )
616 *s++ = '\0';
617 else
618 s = data->hostname;
619 s = strchr(s, ':');
620 if (s != NULL) {
621 *s++ = '\0';
622 data->portno = atoi(s);
623 } else
624 data->portno = LDAP_PORT;
626 *dbdata = data;
627 return (ISC_R_SUCCESS);
630 static void
631 ldapdb_destroy(const char *zone, void *driverdata, void **dbdata) {
632 struct ldapdb_data *data = *dbdata;
634 UNUSED(zone);
635 UNUSED(driverdata);
637 free_data(data);
640 static dns_sdbmethods_t ldapdb_methods = {
641 ldapdb_lookup,
642 NULL, /* authority */
643 ldapdb_allnodes,
644 ldapdb_create,
645 ldapdb_destroy
648 /* Wrapper around dns_sdb_register() */
649 isc_result_t
650 ldapdb_init(void) {
651 unsigned int flags =
652 DNS_SDBFLAG_RELATIVEOWNER |
653 DNS_SDBFLAG_RELATIVERDATA |
654 DNS_SDBFLAG_THREADSAFE;
656 ldapdb_lock(0);
657 return (dns_sdb_register("ldap", &ldapdb_methods, NULL, flags,
658 ns_g_mctx, &ldapdb));
661 /* Wrapper around dns_sdb_unregister() */
662 void
663 ldapdb_clear(void) {
664 if (ldapdb != NULL) {
665 /* clean up thread data */
666 ldapdb_getconn(NULL);
667 dns_sdb_unregister(&ldapdb);