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
40 #include <isc/print.h>
41 #include <isc/result.h>
43 #include <isc/thread.h>
47 #include <named/globals.h>
48 #include <named/log.h>
54 * A simple database driver for LDAP
57 /* enough for name with 8 labels of max length */
58 #define MAXNAMELEN 519
60 static dns_sdbimplementation_t
*ldapdb
= NULL
;
80 /* used by ldapdb_getconn */
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
))
99 static void ldapdb_insert(struct ldapdb_entry
**stack
,
100 struct ldapdb_entry
*item
) {
105 static void ldapdb_lock(int what
) {
106 static isc_mutex_t lock
;
110 isc_mutex_init(&lock
);
121 /* data == NULL means cleanup */
123 ldapdb_getconn(struct ldapdb_data
*data
)
125 static struct ldapdb_entry
*allthreadsdata
= NULL
;
126 struct ldapdb_entry
*threaddata
, *conndata
;
127 unsigned long threadid
;
131 /* lock out other threads */
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
;
144 allthreadsdata
= threaddata
->next
;
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
)
159 threaddata
->index
= malloc(sizeof(threadid
));
160 if (threaddata
->index
== NULL
) {
164 *(unsigned long *)threaddata
->index
= threadid
;
165 threaddata
->size
= sizeof(threadid
);
166 threaddata
->data
= NULL
;
168 /* need to lock out other threads here */
170 ldapdb_insert(&allthreadsdata
, threaddata
);
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
)
183 conndata
->index
= data
->hostport
;
184 conndata
->size
= strlen(data
->hostport
);
185 conndata
->data
= NULL
;
186 ldapdb_insert((struct ldapdb_entry
**)&threaddata
->data
,
190 return (LDAP
**)&conndata
->data
;
194 ldapdb_bind(struct ldapdb_data
*data
, LDAP
**ldp
)
196 #ifndef LDAPDB_RFC1823API
197 const int ver
= LDAPDB_LDAP_VERSION
;
202 *ldp
= ldap_open(data
->hostname
, data
->portno
);
206 #ifndef LDAPDB_RFC1823API
207 ldap_set_option(*ldp
, LDAP_OPT_PROTOCOL_VERSION
, &ver
);
212 ldap_start_tls_s(*ldp
, NULL
, NULL
);
216 if (ldap_simple_bind_s(*ldp
, data
->bindname
, data
->bindpw
) != LDAP_SUCCESS
) {
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
;
228 LDAPMessage
*res
, *e
;
229 char *fltr
, *a
, **vals
= NULL
, **names
= NULL
;
231 #ifdef LDAPDB_RFC1823API
236 int i
, j
, errno
, msgid
;
238 ldp
= ldapdb_getconn(data
);
240 return (ISC_R_FAILURE
);
242 ldapdb_bind(data
, ldp
);
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
);
251 fltr
= data
->filterall
;
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);
264 ldapdb_bind(data
, ldp
);
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
) {
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
);
285 return (ISC_R_FAILURE
);
288 /* only one entry per result message */
289 e
= ldap_first_entry(ld
, 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
);
298 names
= ldap_get_values(ld
, e
, "relativeDomainName");
303 vals
= ldap_get_values(ld
, e
, "dNSTTL");
306 ldap_value_free(vals
);
309 for (a
= ldap_first_attribute(ld
, e
, &ptr
); a
!= NULL
; a
= ldap_next_attribute(ld
, e
, ptr
)) {
314 s
= strstr(a
, "RECORD");
315 if ((s
== NULL
) || (s
== a
) || (s
- a
>= (signed int)sizeof(type
))) {
316 #ifndef LDAPDB_RFC1823API
322 strncpy(type
, a
, s
- a
);
324 vals
= ldap_get_values(ld
, e
, a
);
326 for (i
= 0; vals
[i
] != NULL
; i
++) {
328 result
= dns_sdb_putrr(retdata
, type
, ttl
, vals
[i
]);
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
)
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
346 ldap_value_free(names
);
348 return (ISC_R_FAILURE
);
351 ldap_value_free(vals
);
353 #ifndef LDAPDB_RFC1823API
357 #ifndef LDAPDB_RFC1823API
362 ldap_value_free(names
);
364 /* free this result */
368 /* free final result */
374 /* callback routines */
376 ldapdb_lookup(const char *zone
, const char *name
, void *dbdata
,
377 dns_sdblookup_t
*lookup
)
379 return ldapdb_search(zone
, name
, dbdata
, lookup
);
383 ldapdb_allnodes(const char *zone
, void *dbdata
,
384 dns_sdballnodes_t
*allnodes
)
386 return ldapdb_search(zone
, NULL
, dbdata
, allnodes
);
392 static const char hexdigits
[] = "0123456789abcdef";
396 while ((s
= strchr(s
, '%'))) {
399 if ((p
= strchr(hexdigits
, tolower(s
[1]))) == NULL
)
402 if ((p
= strchr(hexdigits
, tolower(s
[2]))) == NULL
)
406 memmove(s
, s
+ 2, strlen(s
) - 1);
411 /* returns 0 for ok, -1 for bad syntax, -2 for unknown critical extension */
413 parseextensions(char *extensions
, struct ldapdb_data
*data
)
415 char *s
, *next
, *name
, *value
;
418 while (extensions
!= NULL
) {
419 s
= strchr(extensions
, ',');
427 if (*extensions
!= '\0') {
428 s
= strchr(extensions
, '=');
431 value
= *s
!= '\0' ? s
: NULL
;
437 critical
= *name
== '!';
445 if (!strcasecmp(name
, "bindname")) {
446 data
->bindname
= value
;
447 } else if (!strcasecmp(name
, "x-bindpw")) {
448 data
->bindpw
= value
;
450 } else if (!strcasecmp(name
, "x-tls")) {
451 data
->tls
= value
== NULL
|| !strcasecmp(value
, "true");
453 } else if (critical
) {
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
));
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
;
487 /* we assume that only one thread will call create at a time */
488 /* want to do this only once for all instances */
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
));
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
) {
502 return (ISC_R_NOMEMORY
);
505 data
->defaultttl
= defaultttl
;
507 s
= strchr(data
->hostport
, '/');
511 /* attrs, scope, filter etc? */
515 /* ignore attributes */
534 if (*extensions
== '\0') {
538 if (*filter
== '\0') {
544 if (*data
->base
== '\0') {
549 /* parse extensions */
550 if (extensions
!= NULL
) {
553 err
= parseextensions(extensions
, data
);
555 /* err should be -1 or -2 */
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
)) {
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;
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
) {
590 return (ISC_R_NOMEMORY
);
592 data
->filterone
= isc_mem_get(ns_g_mctx
, data
->filteronelen
);
593 if (data
->filterone
== NULL
) {
595 return (ISC_R_NOMEMORY
);
598 if (filter
== NULL
) {
599 sprintf(data
->filterall
, "(zoneName=%s)", zone
);
600 sprintf(data
->filterone
, "(&(zoneName=%s)(relativeDomainName=", zone
);
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
) {
611 return (ISC_R_NOMEMORY
);
614 if (*data
->hostport
== '[' &&
615 (s
= strchr(data
->hostname
, ']')) != NULL
)
622 data
->portno
= atoi(s
);
624 data
->portno
= LDAP_PORT
;
627 return (ISC_R_SUCCESS
);
631 ldapdb_destroy(const char *zone
, void *driverdata
, void **dbdata
) {
632 struct ldapdb_data
*data
= *dbdata
;
640 static dns_sdbmethods_t ldapdb_methods
= {
642 NULL
, /* authority */
648 /* Wrapper around dns_sdb_register() */
652 DNS_SDBFLAG_RELATIVEOWNER
|
653 DNS_SDBFLAG_RELATIVERDATA
|
654 DNS_SDBFLAG_THREADSAFE
;
657 return (dns_sdb_register("ldap", &ldapdb_methods
, NULL
, flags
,
658 ns_g_mctx
, &ldapdb
));
661 /* Wrapper around dns_sdb_unregister() */
664 if (ldapdb
!= NULL
) {
665 /* clean up thread data */
666 ldapdb_getconn(NULL
);
667 dns_sdb_unregister(&ldapdb
);