1353. [contrib] sdb/ldap to version 0.9.

This commit is contained in:
Mark Andrews 2002-08-05 04:45:21 +00:00
parent 4016d47cf3
commit 047210c74e
4 changed files with 352 additions and 238 deletions

View file

@ -1,3 +1,5 @@
1353. [contrib] sdb/ldap to version 0.9.
1350. [bug] dns_name_fromtext() failed to handle too many labels
gracefully.

View file

@ -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 <venaas@uninett.no> 2001-04-12
Stig Venaas <venaas@uninett.no> 2002-04-17

View file

@ -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 <venaas@uninett.no> 2001-05-06
Stig Venaas <venaas@uninett.no> 2001-12-29

View file

@ -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 <config.h>
#include <string.h>
@ -22,14 +30,18 @@
#include <dns/sdb.h>
#include <named/globals.h>
#include <named/log.h>
#include <ldap.h>
#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 = {