From 047210c74e3cf138dc5cbc3b8b319cfcbcfe50ba Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Mon, 5 Aug 2002 04:45:21 +0000 Subject: [PATCH] 1353. [contrib] sdb/ldap to version 0.9. --- CHANGES | 2 + contrib/sdb/ldap/INSTALL.ldap | 12 +- contrib/sdb/ldap/README.ldap | 44 ++- contrib/sdb/ldap/ldapdb.c | 532 ++++++++++++++++++++-------------- 4 files changed, 352 insertions(+), 238 deletions(-) diff --git a/CHANGES b/CHANGES index 3e9a8c1b00..389c1ee5d7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +1353. [contrib] sdb/ldap to version 0.9. + 1350. [bug] dns_name_fromtext() failed to handle too many labels gracefully. diff --git a/contrib/sdb/ldap/INSTALL.ldap b/contrib/sdb/ldap/INSTALL.ldap index e54b500157..e3801ed258 100644 --- a/contrib/sdb/ldap/INSTALL.ldap +++ b/contrib/sdb/ldap/INSTALL.ldap @@ -1,4 +1,4 @@ -This is the INSTALL file for 0.4. See +This is the INSTALL file for 0.9. See http://www.venaas.no/ldap/bind-sdb/ for updates or other information. BUILDING @@ -22,7 +22,10 @@ Finally you need to edit bin/named/main.c. Below where it says it says "xxdb_init();" add the line "ldapdb_init();", and finally below where it says "xxdb_clear();", add "ldapdb_clear();". -Now you should hopefully be able to build it. +Now you should hopefully be able to build it. If you get an error +message about ldap_memfree() not being defined, you're probably +using an LDAP library with the interface defined in RFC 1823. To +build, uncomment the #define RFC1823API line near the top of ldapdb.c. CONFIGURING @@ -50,6 +53,7 @@ zone "venaas.com" { When doing lookups BIND will do a sub-tree search below the base in the URL. The number 172800 is the TTL which will be used for all entries that -haven't got the dNSTTL attribute. +haven't got the dNSTTL attribute. It is also possible to add an filter to +the URL, say ldap://host/base???(o=internal) -Stig Venaas 2001-04-12 +Stig Venaas 2002-04-17 diff --git a/contrib/sdb/ldap/README.ldap b/contrib/sdb/ldap/README.ldap index 102d0ac969..10d6587255 100644 --- a/contrib/sdb/ldap/README.ldap +++ b/contrib/sdb/ldap/README.ldap @@ -1,18 +1,40 @@ This is an attempt at an LDAP back-end for BIND 9 using the new simplified -database interface "sdb". This is the fifth release (0.5) and is not ready -for production use yet. Note that this version (and 0.4) uses a new schema -and is not backwards compatible with versions before 0.4. The big changes in -0.5 are thread support and improved connection handling. Multiple threads -can now access the back-end simultaneously, and rather than having one -connection per zone, there is now one connection per thread per LDAP server. -This should help people with multiple CPUs and people with a huge number of -zones. One final change is support for literal IPv6 addresses in LDAP URLs. -At least OpenLDAP 2 has IPv6 support, so if you use OpenLDAP 2 libraries and -server, you got all you need. +database interface "sdb". This is the nineth release (0.9) and seems to +be pretty stable. Note that since version 0.4 a new schema is used. +It is not backwards compatible with versions before 0.4. + +In 0.9 the code has been cleaned up a bit and should be slightly faster +than previous versions. It also fixes an error with zone transfers (AXFR) +and entries with multiple relativeDomainName values. The problem was +that it would only use the first value in the result. There's no need +to upgrade unless you use such entries. + +0.8 uses asynchronous LDAP search which should give better performance. +Thanks to Ashley Burston for providing patch. Another new feature is +allowing filters in URLs. The syntax is as in RFC 2255. Few people will +need this, but if you have say an internal and external version of the +same zone, you could stick say o=internal and o=external into different +entries, and specify for instance ldap://host/base???(o=internal) +Some error logging has also been added. + +0.7 allows space and other characters to be used in URLs by use of %-quoting. +For instance space can be written as %20. It also fixes a problem with some +servers and/or APIs that do not preserve attribute casing. + +0.6 fixes some memory leaks present in older versions unless compiled with +the RFC 1823 API. + +The big changes in 0.5 are thread support and improved connection handling. +Multiple threads can now access the back-end simultaneously, and rather than +having one connection per zone, there is now one connection per thread per +LDAP server. This should help people with multiple CPUs and people with a +huge number of zones. One final change is support for literal IPv6 addresses +in LDAP URLs. At least OpenLDAP 2 has IPv6 support, so if you use OpenLDAP 2 +libraries and server, you got all you need. If you have bug reports, fixes, comments, questions or whatever, please contact me. See also http://www.venaas.no/ldap/bind-sdb/ for information. See INSTALL for how to build, install and use. -Stig Venaas 2001-05-06 +Stig Venaas 2001-12-29 diff --git a/contrib/sdb/ldap/ldapdb.c b/contrib/sdb/ldap/ldapdb.c index 0b6e88b48d..47612dce3e 100644 --- a/contrib/sdb/ldap/ldapdb.c +++ b/contrib/sdb/ldap/ldapdb.c @@ -1,11 +1,19 @@ /* - * Copyright (C) 2001 Stig Venaas + * ldapdb.c version 0.9 + * + * Copyright (C) 2002 Stig Venaas * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. */ +/* + * If you are using an old LDAP API uncomment the define below. Only do this + * if you know what you're doing or get compilation errors on ldap_memfree(). + */ +/* #define RFC1823API */ + #include #include @@ -22,14 +30,18 @@ #include #include +#include #include #include "ldapdb.h" /* - * A simple database driver for LDAP. Not production quality yet + * A simple database driver for LDAP */ +/* enough for name with 8 labels of max length */ +#define MAXNAMELEN 519 + static dns_sdbimplementation_t *ldapdb = NULL; struct ldapdb_data { @@ -38,6 +50,11 @@ struct ldapdb_data { int portno; char *base; int defaultttl; + char *filterall; + int filteralllen; + char *filterone; + int filteronelen; + char *filtername; }; /* used by ldapdb_getconn */ @@ -153,16 +170,232 @@ ldapdb_getconn(struct ldapdb_data *data) return (LDAP **)&conndata->data; } +static void +ldapdb_bind(struct ldapdb_data *data, LDAP **ldp) +{ + if (*ldp != NULL) + ldap_unbind(*ldp); + *ldp = ldap_open(data->hostname, data->portno); + if (*ldp == NULL) + return; + if (ldap_simple_bind_s(*ldp, NULL, NULL) != LDAP_SUCCESS) { + ldap_unbind(*ldp); + *ldp = NULL; + } +} + +static isc_result_t +ldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata) +{ + struct ldapdb_data *data = dbdata; + isc_result_t result = ISC_R_NOTFOUND; + LDAP **ldp; + LDAPMessage *res, *e; + char *fltr, *a, **vals, **names; + char type[64]; +#ifdef RFC1823API + void *ptr; +#else + BerElement *ptr; +#endif + int i, j, errno, msgid; + + ldp = ldapdb_getconn(data); + if (ldp == NULL) + return (ISC_R_FAILURE); + if (*ldp == NULL) { + ldapdb_bind(data, ldp); + if (*ldp == NULL) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': bind failed", zone); + return (ISC_R_FAILURE); + } + } + + if (name == NULL) { + fltr = data->filterall; + } else { + if (strlen(name) > MAXNAMELEN) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': name %s too long", zone, name); + return (ISC_R_FAILURE); + } + sprintf(data->filtername, "%s))", name); + fltr = data->filterone; + } + + msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0); + if (msgid == -1) { + ldapdb_bind(data, ldp); + if (*ldp != NULL) + msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0); + } + + if (*ldp == NULL || msgid == -1) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': search failed, filter %s", zone, fltr); + return (ISC_R_FAILURE); + } + + /* Get the records one by one as they arrive and return them to bind */ + while ((errno = ldap_result(*ldp, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) { + LDAP *ld = *ldp; + int ttl = data->defaultttl; + + /* not supporting continuation references at present */ + if (errno != LDAP_RES_SEARCH_ENTRY) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': ldap_result returned %d", zone, errno); + ldap_msgfree(res); + return (ISC_R_FAILURE); + } + + /* only one entry per result message */ + e = ldap_first_entry(ld, res); + if (e == NULL) { + ldap_msgfree(res); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': ldap_first_entry failed", zone); + return (ISC_R_FAILURE); + } + + if (name == NULL) { + names = ldap_get_values(ld, e, "relativeDomainName"); + if (names == NULL) + continue; + } + + vals = ldap_get_values(ld, e, "dNSTTL"); + if (vals != NULL) { + ttl = atoi(vals[0]); + ldap_value_free(vals); + } + + for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; a = ldap_next_attribute(ld, e, ptr)) { + char *s; + + for (s = a; *s; s++) + *s = toupper(*s); + s = strstr(a, "RECORD"); + if ((s == NULL) || (s == a) || (s - a >= (signed int)sizeof(type))) { +#ifndef RFC1823API + ldap_memfree(a); +#endif + continue; + } + + strncpy(type, a, s - a); + type[s - a] = '\0'; + vals = ldap_get_values(ld, e, a); + if (vals != NULL) { + for (i = 0; vals[i] != NULL; i++) { + if (name != NULL) { + result = dns_sdb_putrr(retdata, type, ttl, vals[i]); + } else { + for (j = 0; names[j] != NULL; j++) { + result = dns_sdb_putnamedrr(retdata, names[j], type, ttl, vals[i]); + if (result != ISC_R_SUCCESS) + break; + } + } +; if (result != ISC_R_SUCCESS) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': dns_sdb_put... failed for %s", zone, vals[i]); + ldap_value_free(vals); +#ifndef RFC1823API + ldap_memfree(a); + if (ptr != NULL) + ber_free(ptr, 0); +#endif + if (name == NULL) + ldap_value_free(names); + ldap_msgfree(res); + return (ISC_R_FAILURE); + } + } + ldap_value_free(vals); + } +#ifndef RFC1823API + ldap_memfree(a); +#endif + } +#ifndef RFC1823API + if (ptr != NULL) + ber_free(ptr, 0); +#endif + if (name == NULL) + ldap_value_free(names); + + /* cleanup this result */ + ldap_msgfree(res); + } + + return (result); +} + + /* callback routines */ +static isc_result_t +ldapdb_lookup(const char *zone, const char *name, void *dbdata, + dns_sdblookup_t *lookup) +{ + return ldapdb_search(zone, name, dbdata, lookup); +} + +static isc_result_t +ldapdb_allnodes(const char *zone, void *dbdata, + dns_sdballnodes_t *allnodes) +{ + return ldapdb_search(zone, NULL, dbdata, allnodes); +} + +static char * +unhex(char *in) +{ + static const char hexdigits[] = "0123456789abcdef"; + char *p, *s = in; + int d1, d2; + + while ((s = strchr(s, '%'))) { + if (!(s[1] && s[2])) + return NULL; + if ((p = strchr(hexdigits, tolower(s[1]))) == NULL) + return NULL; + d1 = p - hexdigits; + if ((p = strchr(hexdigits, tolower(s[2]))) == NULL) + return NULL; + d2 = p - hexdigits; + *s++ = d1 << 4 | d2; + memmove(s, s + 2, strlen(s) - 1); + } + return in; +} + + + +static void +free_data(struct ldapdb_data *data) +{ + if (data->hostport != NULL) + isc_mem_free(ns_g_mctx, data->hostport); + if (data->hostname != NULL) + isc_mem_free(ns_g_mctx, data->hostname); + if (data->filterall != NULL) + isc_mem_put(ns_g_mctx, data->filterall, data->filteralllen); + if (data->filterone != NULL) + isc_mem_put(ns_g_mctx, data->filterone, data->filteronelen); + isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data)); +} + + static isc_result_t ldapdb_create(const char *zone, int argc, char **argv, void *driverdata, void **dbdata) { struct ldapdb_data *data; - char *s; + char *s, *filter = NULL; int defaultttl; - UNUSED(zone); UNUSED(driverdata); /* we assume that only one thread will call create at a time */ @@ -175,24 +408,89 @@ ldapdb_create(const char *zone, int argc, char **argv, data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data)); if (data == NULL) return (ISC_R_NOMEMORY); + + memset(data, 0, sizeof(struct ldapdb_data)); data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://")); if (data->hostport == NULL) { - isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data)); + free_data(data); return (ISC_R_NOMEMORY); } + data->defaultttl = defaultttl; + s = strchr(data->hostport, '/'); if (s != NULL) { *s++ = '\0'; - data->base = *s != '\0' ? s : NULL; + data->base = s; + /* attrs, scope, filter etc? */ + s = strchr(s, '?'); + if (s != NULL) { + *s++ = '\0'; + /* ignore attributes */ + s = strchr(s, '?'); + if (s != NULL) { + *s++ = '\0'; + /* ignore scope */ + s = strchr(s, '?'); + if (s != NULL) { + *s++ = '\0'; + /* filter */ + filter = s; + s = strchr(s, '?'); + if (s != NULL) { + *s++ = '\0'; + } + if (*filter == '\0') { + filter = NULL; + } + } + } + } + if (*data->base == '\0') { + data->base = NULL; + } + + if ((data->base != NULL && unhex(data->base) == NULL) || (filter != NULL && unhex(filter) == NULL)) { + free_data(data); + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "LDAP sdb zone '%s': bad hex values", zone); + return (ISC_R_FAILURE); + } } + /* compute filterall and filterone once and for all */ + if (filter == NULL) { + data->filteralllen = strlen(zone) + strlen("(zoneName=)") + 1; + data->filteronelen = strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1; + } else { + data->filteralllen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=))") + 1; + data->filteronelen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1; + } + + data->filterall = isc_mem_get(ns_g_mctx, data->filteralllen); + if (data->filterall == NULL) { + free_data(data); + return (ISC_R_NOMEMORY); + } + data->filterone = isc_mem_get(ns_g_mctx, data->filteronelen); + if (data->filterone == NULL) { + free_data(data); + return (ISC_R_NOMEMORY); + } + + if (filter == NULL) { + sprintf(data->filterall, "(zoneName=%s)", zone); + sprintf(data->filterone, "(&(zoneName=%s)(relativeDomainName=", zone); + } else { + sprintf(data->filterall, "(&%s(zoneName=%s))", filter, zone); + sprintf(data->filterone, "(&%s(zoneName=%s)(relativeDomainName=", filter, zone); + } + data->filtername = data->filterone + strlen(data->filterone); + /* support URLs with literal IPv6 addresses */ - data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport + - (*data->hostport == '[' ? 1 : 0)); + data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport + (*data->hostport == '[' ? 1 : 0)); if (data->hostname == NULL) { - isc_mem_free(ns_g_mctx, data->hostport); - isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data)); + free_data(data); return (ISC_R_NOMEMORY); } @@ -219,219 +517,7 @@ ldapdb_destroy(const char *zone, void *driverdata, void **dbdata) { UNUSED(zone); UNUSED(driverdata); - if (data->hostport != NULL) - isc_mem_free(ns_g_mctx, data->hostport); - if (data->hostname != NULL) - isc_mem_free(ns_g_mctx, data->hostname); - isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data)); -} - -static void -ldapdb_bind(struct ldapdb_data *data, LDAP **ldp) -{ - if (*ldp != NULL) - ldap_unbind(*ldp); - *ldp = ldap_open(data->hostname, data->portno); - if (*ldp == NULL) - return; - if (ldap_simple_bind_s(*ldp, NULL, NULL) != LDAP_SUCCESS) { - ldap_unbind(*ldp); - *ldp = NULL; - } -} - -static isc_result_t -ldapdb_lookup(const char *zone, const char *name, void *dbdata, - dns_sdblookup_t *lookup) -{ - isc_result_t result = ISC_R_NOTFOUND; - struct ldapdb_data *data = dbdata; - LDAP **ldp; - LDAPMessage *res, *e; - char *fltr, *a, **vals; - char type[64]; - BerElement *ptr; - int i; - - ldp = ldapdb_getconn(data); - if (ldp == NULL) - return (ISC_R_FAILURE); - if (*ldp == NULL) { - ldapdb_bind(data, ldp); - if (*ldp == NULL) - return (ISC_R_FAILURE); - } - fltr = isc_mem_get(ns_g_mctx, strlen(zone) + strlen(name) + - strlen("(&(zoneName=)(relativeDomainName=))") + 1); - if (fltr == NULL) - return (ISC_R_NOMEMORY); - - strcpy(fltr, "(&(zoneName="); - strcat(fltr, zone); - strcat(fltr, ")(relativeDomainName="); - strcat(fltr, name); - strcat(fltr, "))"); - - if (ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0, - &res) != LDAP_SUCCESS) { - ldapdb_bind(data, ldp); - if (*ldp != NULL) - ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, - fltr, NULL, 0, &res); - } - - isc_mem_put(ns_g_mctx, fltr, strlen(fltr) + 1); - - if (*ldp == NULL) - goto exit; - - for (e = ldap_first_entry(*ldp, res); e != NULL; - e = ldap_next_entry(*ldp, e)) { - LDAP *ld = *ldp; - int ttl = data->defaultttl; - - for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; - a = ldap_next_attribute(ld, e, ptr)) { - if (!strcmp(a, "dNSTTL")) { - vals = ldap_get_values(ld, e, a); - ttl = atoi(vals[0]); - ldap_value_free(vals); - ldap_memfree(a); - break; - } - ldap_memfree(a); - } - for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; - a = ldap_next_attribute(ld, e, ptr)) { - char *s; - - for (s = a; *s; s++) - *s = toupper(*s); - s = strstr(a, "RECORD"); - if ((s == NULL) || (s == a) - || (s - a >= (signed int)sizeof(type))) { - ldap_memfree(a); - continue; - } - strncpy(type, a, s - a); - type[s - a] = '\0'; - vals = ldap_get_values(ld, e, a); - for (i=0; vals[i] != NULL; i++) { - result = dns_sdb_putrr(lookup, type, ttl, - vals[i]); - if (result != ISC_R_SUCCESS) { - ldap_value_free(vals); - ldap_memfree(a); - result = ISC_R_FAILURE; - goto exit; - } - } - ldap_value_free(vals); - ldap_memfree(a); - } - } - exit: - ldap_msgfree(res); - return (result); -} - -static isc_result_t -ldapdb_allnodes(const char *zone, void *dbdata, - dns_sdballnodes_t *allnodes) { - isc_result_t result = ISC_R_NOTFOUND; - struct ldapdb_data *data = dbdata; - LDAP **ldp; - LDAPMessage *res, *e; - char type[64]; - char *fltr, *a, **vals; - BerElement *ptr; - int i; - - ldp = ldapdb_getconn(data); - if (ldp == NULL) - return (ISC_R_FAILURE); - if (*ldp == NULL) { - ldapdb_bind(data, ldp); - if (*ldp == NULL) - return (ISC_R_FAILURE); - } - - fltr = isc_mem_get(ns_g_mctx, strlen(zone) + strlen("(zoneName=)") + 1); - if (fltr == NULL) - return (ISC_R_NOMEMORY); - - strcpy(fltr, "(zoneName="); - strcat(fltr, zone); - strcat(fltr, ")"); - - if (ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0, - &res) != LDAP_SUCCESS) { - ldapdb_bind(data, ldp); - if (*ldp != NULL) - ldap_search_s(*ldp, data->base, LDAP_SCOPE_SUBTREE, - fltr, NULL, 0, &res); - } - - isc_mem_put(ns_g_mctx, fltr, strlen(fltr) + 1); - - for (e = ldap_first_entry(*ldp, res); e != NULL; - e = ldap_next_entry(*ldp, e)) { - LDAP *ld = *ldp; - char *name = NULL; - int ttl = data->defaultttl; - - for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; - a = ldap_next_attribute(ld, e, ptr)) { - if (!strcmp(a, "dNSTTL")) { - vals = ldap_get_values(ld, e, a); - ttl = atoi(vals[0]); - ldap_value_free(vals); - } else if (!strcmp(a, "relativeDomainName")) { - vals = ldap_get_values(ld, e, a); - name = isc_mem_strdup(ns_g_mctx, vals[0]); - ldap_value_free(vals); - } - ldap_memfree(a); - } - - if (name == NULL) - continue; - - for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; - a = ldap_next_attribute(ld, e, ptr)) { - char *s; - - for (s = a; *s; s++) - *s = toupper(*s); - s = strstr(a, "RECORD"); - if ((s == NULL) || (s == a) - || (s - a >= (signed int)sizeof(type))) { - ldap_memfree(a); - continue; - } - strncpy(type, a, s - a); - type[s - a] = '\0'; - vals = ldap_get_values(ld, e, a); - for (i=0; vals[i] != NULL; i++) { - result = dns_sdb_putnamedrr(allnodes, name, - type, ttl, vals[i]); - if (result != ISC_R_SUCCESS) { - ldap_value_free(vals); - ldap_memfree(a); - isc_mem_free(ns_g_mctx, name); - result = ISC_R_FAILURE; - goto exit; - } - } - ldap_value_free(vals); - ldap_memfree(a); - } - isc_mem_free(ns_g_mctx, name); - } - - exit: - ldap_msgfree(res); - return (result); + free_data(data); } static dns_sdbmethods_t ldapdb_methods = {