diff options
Diffstat (limited to 'contrib/dlz/modules/ldap')
-rw-r--r-- | contrib/dlz/modules/ldap/Makefile | 46 | ||||
-rw-r--r-- | contrib/dlz/modules/ldap/dlz_ldap_dynamic.c | 1190 | ||||
-rw-r--r-- | contrib/dlz/modules/ldap/testing/README | 10 | ||||
-rw-r--r-- | contrib/dlz/modules/ldap/testing/dlz.schema | 192 | ||||
-rw-r--r-- | contrib/dlz/modules/ldap/testing/example.ldif | 192 | ||||
-rw-r--r-- | contrib/dlz/modules/ldap/testing/named.conf | 43 | ||||
-rw-r--r-- | contrib/dlz/modules/ldap/testing/slapd.conf | 44 |
7 files changed, 1717 insertions, 0 deletions
diff --git a/contrib/dlz/modules/ldap/Makefile b/contrib/dlz/modules/ldap/Makefile new file mode 100644 index 0000000..d9b6ff5 --- /dev/null +++ b/contrib/dlz/modules/ldap/Makefile @@ -0,0 +1,46 @@ +# Copyright Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. + +# Copyright (C) Stichting NLnet, Netherlands, stichting@nlnet.nl. +# +# The development of Dynamically Loadable Zones (DLZ) for Bind 9 was +# conceived and contributed by Rob Butler. +# +# SPDX-License-Identifier: ISC and MPL-2.0 +# +# 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. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL STICHTING NLNET BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +prefix = /usr +libdir = $(prefix)/lib/bind9 + +CFLAGS += -fPIC -g -I../include +LDAP_LIBS=-lldap + +all: dlz_ldap_dynamic.so + +dlz_dbi.o: ../common/dlz_dbi.c + $(CC) $(CFLAGS) -c ../common/dlz_dbi.c + +dlz_ldap_dynamic.so: dlz_ldap_dynamic.c dlz_dbi.o + $(CC) $(CFLAGS) -shared -o dlz_ldap_dynamic.so \ + dlz_ldap_dynamic.c dlz_dbi.o $(LDAP_LIBS) + +clean: + rm -f dlz_ldap_dynamic.so *.o + +install: dlz_ldap_dynamic.so + mkdir -p $(DESTDIR)$(libdir) + install dlz_ldap_dynamic.so $(DESTDIR)$(libdir) diff --git a/contrib/dlz/modules/ldap/dlz_ldap_dynamic.c b/contrib/dlz/modules/ldap/dlz_ldap_dynamic.c new file mode 100644 index 0000000..ce1c50c --- /dev/null +++ b/contrib/dlz/modules/ldap/dlz_ldap_dynamic.c @@ -0,0 +1,1190 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 and ISC + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + */ + +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This provides the externally loadable ldap DLZ module, without + * update support + */ + +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <dlz_dbi.h> +#include <dlz_list.h> +#include <dlz_minimal.h> +#include <dlz_pthread.h> + +/* + * Need older API functions from ldap.h. + */ +#define LDAP_DEPRECATED 1 + +#include <ldap.h> + +#define SIMPLE "simple" +#define KRB41 "krb41" +#define KRB42 "krb42" +#define V2 "v2" +#define V3 "v3" + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +/*% + * Structure to hold everything needed by this "instance" of the LDAP + * driver remember, the driver code is only loaded once, but may have + * many separate instances. + */ +typedef struct { + db_list_t *db; /*%< handle to a list of DB */ + int method; /*%< security authentication + * method */ + char *user; /*%< who is authenticating */ + char *cred; /*%< password for simple + * authentication method */ + int protocol; /*%< LDAP communication + * protocol version */ + char *hosts; /*%< LDAP server hosts */ + + /* Helper functions from the dlz_dlopen driver */ + log_t *log; + dns_sdlz_putrr_t *putrr; + dns_sdlz_putnamedrr_t *putnamedrr; + dns_dlz_writeablezone_t *writeable_zone; +} ldap_instance_t; + +/* forward references */ + +#if DLZ_DLOPEN_VERSION < 3 +isc_result_t +dlz_findzonedb(void *dbdata, const char *name); +#else /* if DLZ_DLOPEN_VERSION < 3 */ +isc_result_t +dlz_findzonedb(void *dbdata, const char *name, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo); +#endif /* if DLZ_DLOPEN_VERSION < 3 */ + +void +dlz_destroy(void *dbdata); + +static void +b9_add_helper(ldap_instance_t *db, const char *helper_name, void *ptr); + +/* + * Private methods + */ + +/*% checks that the LDAP URL parameters make sense */ +static isc_result_t +dlz_ldap_checkURL(ldap_instance_t *db, char *URL, int attrCnt, + const char *msg) { + isc_result_t result = ISC_R_SUCCESS; + int ldap_result; + LDAPURLDesc *ldap_url = NULL; + + if (!ldap_is_ldap_url(URL)) { + db->log(ISC_LOG_ERROR, "%s query is not a valid LDAP URL", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + ldap_result = ldap_url_parse(URL, &ldap_url); + if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) { + db->log(ISC_LOG_ERROR, "parsing %s query failed", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) { + db->log(ISC_LOG_ERROR, + "%s query must specify at least " + "%d attributes to return", + msg, attrCnt); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_host != NULL) { + db->log(ISC_LOG_ERROR, "%s query must not specify a host", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_port != 389) { + db->log(ISC_LOG_ERROR, "%s query must not specify a port", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_dn == NULL || strlen(ldap_url->lud_dn) < 1) { + db->log(ISC_LOG_ERROR, "%s query must specify a search base", + msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) { + db->log(ISC_LOG_ERROR, + "%s uses extensions. " + "The driver does not support LDAP extensions.", + msg); + result = ISC_R_FAILURE; + goto cleanup; + } + +cleanup: + if (ldap_url != NULL) { + ldap_free_urldesc(ldap_url); + } + + return (result); +} + +/*% Connects / reconnects to LDAP server */ +static isc_result_t +dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) { + isc_result_t result; + int ldap_result; + + /* if we have a connection, get ride of it. */ + if (dbc->dbconn != NULL) { + ldap_unbind_s((LDAP *)dbc->dbconn); + dbc->dbconn = NULL; + } + + /* now connect / reconnect. */ + + /* initialize. */ + dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT); + if (dbc->dbconn == NULL) { + return (ISC_R_NOMEMORY); + } + + /* set protocol version. */ + ldap_result = ldap_set_option((LDAP *)dbc->dbconn, + LDAP_OPT_PROTOCOL_VERSION, + &(dbi->protocol)); + if (ldap_result != LDAP_SUCCESS) { + result = ISC_R_NOPERM; + goto cleanup; + } + + /* "bind" to server. i.e. send username / pass */ + ldap_result = ldap_bind_s((LDAP *)dbc->dbconn, dbi->user, dbi->cred, + dbi->method); + if (ldap_result != LDAP_SUCCESS) { + result = ISC_R_FAILURE; + goto cleanup; + } + + return (ISC_R_SUCCESS); + +cleanup: + + /* cleanup if failure. */ + if (dbc->dbconn != NULL) { + ldap_unbind_s((LDAP *)dbc->dbconn); + dbc->dbconn = NULL; + } + + return (result); +} + +/*% + * Properly cleans up a list of database instances. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ +static void +dlz_ldap_destroy_dblist(db_list_t *dblist) { + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + /* get the first DBI in the list */ + ndbi = DLZ_LIST_HEAD(*dblist); + + /* loop through the list */ + while (ndbi != NULL) { + dbi = ndbi; + /* get the next DBI in the list */ + ndbi = DLZ_LIST_NEXT(dbi, link); + /* release DB connection */ + if (dbi->dbconn != NULL) { + ldap_unbind_s((LDAP *)dbi->dbconn); + } + /* release all memory that comprised a DBI */ + destroy_dbinstance(dbi); + } + /* release memory for the list structure */ + free(dblist); +} + +/*% + * Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ +static dbinstance_t * +dlz_ldap_find_avail_conn(ldap_instance_t *ldap) { + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + /* get top of list */ + head = dbi = DLZ_LIST_HEAD(*ldap->db); + + /* loop through list */ + while (count < dbc_search_limit) { + /* try to lock on the mutex */ + if (dlz_mutex_trylock(&dbi->lock) == 0) { + return (dbi); /* success, return the DBI for use. */ + } + /* not successful, keep trying */ + dbi = DLZ_LIST_NEXT(dbi, link); + + /* check to see if we have gone to the top of the list. */ + if (dbi == NULL) { + count++; + dbi = head; + } + } + + ldap->log(ISC_LOG_INFO, + "LDAP driver unable to find available connection " + "after searching %d times", + count); + return (NULL); +} + +static isc_result_t +dlz_ldap_process_results(ldap_instance_t *db, LDAP *dbc, LDAPMessage *msg, + char **attrs, void *ptr, bool allnodes) { + isc_result_t result = ISC_R_SUCCESS; + int i = 0; + int j; + int len; + char *attribute = NULL; + LDAPMessage *entry; + char *endp = NULL; + char *host = NULL; + char *type = NULL; + char *data = NULL; + char **vals = NULL; + int ttl; + + /* get the first entry to process */ + entry = ldap_first_entry(dbc, msg); + if (entry == NULL) { + db->log(ISC_LOG_INFO, "LDAP no entries to process."); + return (ISC_R_FAILURE); + } + + /* loop through all entries returned */ + while (entry != NULL) { + /* reset for this loop */ + ttl = 0; + len = 0; + i = 0; + attribute = attrs[i]; + + /* determine how much space we need for data string */ + for (j = 0; attrs[j] != NULL; j++) { + /* get the list of values for this attribute. */ + vals = ldap_get_values(dbc, entry, attrs[j]); + /* skip empty attributes. */ + if (vals == NULL || ldap_count_values(vals) < 1) { + continue; + } + /* + * we only use the first value. this driver + * does not support multi-valued attributes. + */ + len = len + strlen(vals[0]) + 1; + /* free vals for next loop */ + ldap_value_free(vals); + } + + /* allocate memory for data string */ + data = malloc(len + 1); + if (data == NULL) { + db->log(ISC_LOG_ERROR, "LDAP driver unable to allocate " + "memory " + "while processing results"); + result = ISC_R_FAILURE; + goto cleanup; + } + + /* + * Make sure data is null termed at the beginning so + * we can check if any data was stored to it later. + */ + data[0] = '\0'; + + /* reset j to re-use below */ + j = 0; + + /* loop through the attributes in the order specified. */ + while (attribute != NULL) { + /* get the list of values for this attribute. */ + vals = ldap_get_values(dbc, entry, attribute); + + /* skip empty attributes. */ + if (vals == NULL || vals[0] == NULL) { + /* increment attribute pointer */ + attribute = attrs[++i]; + /* start loop over */ + continue; + } + + /* + * j initially = 0. Increment j each time we + * set a field that way next loop will set + * next field. + */ + switch (j) { + case 0: + j++; + /* + * convert text to int, make sure it + * worked right + */ + ttl = strtol(vals[0], &endp, 10); + if (*endp != '\0' || ttl < 0) { + db->log(ISC_LOG_ERROR, "LDAP driver " + "ttl must " + "be a positive " + "number"); + goto cleanup; + } + break; + case 1: + j++; + type = strdup(vals[0]); + break; + case 2: + j++; + if (allnodes) { + host = strdup(vals[0]); + } else { + strcpy(data, vals[0]); + } + break; + case 3: + j++; + if (allnodes) { + strcpy(data, vals[0]); + } else { + strcat(data, " "); + strcat(data, vals[0]); + } + break; + default: + strcat(data, " "); + strcat(data, vals[0]); + break; + } + + /* free values */ + ldap_value_free(vals); + vals = NULL; + + /* increment attribute pointer */ + attribute = attrs[++i]; + } + + if (type == NULL) { + db->log(ISC_LOG_ERROR, "LDAP driver unable to retrieve " + "DNS type"); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (strlen(data) < 1) { + db->log(ISC_LOG_ERROR, "LDAP driver unable to retrieve " + "DNS data"); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (allnodes && host != NULL) { + dns_sdlzallnodes_t *an = (dns_sdlzallnodes_t *)ptr; + if (strcasecmp(host, "~") == 0) { + result = db->putnamedrr(an, "*", type, ttl, + data); + } else { + result = db->putnamedrr(an, host, type, ttl, + data); + } + if (result != ISC_R_SUCCESS) { + db->log(ISC_LOG_ERROR, + "ldap_dynamic: putnamedrr failed " + "for \"%s %s %u %s\" (%d)", + host, type, ttl, data, result); + } + } else { + dns_sdlzlookup_t *lookup = (dns_sdlzlookup_t *)ptr; + result = db->putrr(lookup, type, ttl, data); + if (result != ISC_R_SUCCESS) { + db->log(ISC_LOG_ERROR, + "ldap_dynamic: putrr failed " + "for \"%s %u %s\" (%s)", + type, ttl, data, result); + } + } + + if (result != ISC_R_SUCCESS) { + db->log(ISC_LOG_ERROR, "LDAP driver failed " + "while sending data to BIND."); + goto cleanup; + } + + /* free memory for type, data and host for next loop */ + free(type); + type = NULL; + + free(data); + data = NULL; + + if (host != NULL) { + free(host); + host = NULL; + } + + /* get the next entry to process */ + entry = ldap_next_entry(dbc, entry); + } + +cleanup: + /* de-allocate memory */ + if (vals != NULL) { + ldap_value_free(vals); + } + if (host != NULL) { + free(host); + } + if (type != NULL) { + free(type); + } + if (data != NULL) { + free(data); + } + + return (result); +} + +/*% + * This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. + */ +static isc_result_t +dlz_ldap_get_results(const char *zone, const char *record, const char *client, + unsigned int query, void *dbdata, void *ptr) { + isc_result_t result; + ldap_instance_t *db = (ldap_instance_t *)dbdata; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + LDAPURLDesc *ldap_url = NULL; + int ldap_result = 0; + LDAPMessage *ldap_msg = NULL; + int i; + int entries; + + /* get db instance / connection */ + /* find an available DBI from the list */ + dbi = dlz_ldap_find_avail_conn(db); + + /* if DBI is null, can't do anything else */ + if (dbi == NULL) { + return (ISC_R_FAILURE); + } + + /* set fields */ + if (zone != NULL) { + dbi->zone = strdup(zone); + if (dbi->zone == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->zone = NULL; + } + + if (record != NULL) { + dbi->record = strdup(record); + if (dbi->record == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->record = NULL; + } + + if (client != NULL) { + dbi->client = strdup(client); + if (dbi->client == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->client = NULL; + } + + /* what type of query are we going to run? */ + switch (query) { + case ALLNODES: + /* + * if the query was not passed in from the config file + * then we can't run it. return not_implemented, so + * it's like the code for that operation was never + * built into the driver.... AHHH flexibility!!! + */ + if (dbi->allnodes_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(dbi->allnodes_q); + } + break; + case ALLOWXFR: + /* same as comments as ALLNODES */ + if (dbi->allowxfr_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(dbi->allowxfr_q); + } + break; + case AUTHORITY: + /* same as comments as ALLNODES */ + if (dbi->authority_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(dbi->authority_q); + } + break; + case FINDZONE: + /* this is required. It's the whole point of DLZ! */ + if (dbi->findzone_q == NULL) { + db->log(ISC_LOG_DEBUG(2), "No query specified for " + "findzone. " + "Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + querystring = build_querystring(dbi->findzone_q); + } + break; + case LOOKUP: + /* this is required. It's also a major point of DLZ! */ + if (dbi->lookup_q == NULL) { + db->log(ISC_LOG_DEBUG(2), "No query specified for " + "lookup. " + "Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + querystring = build_querystring(dbi->lookup_q); + } + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + db->log(ISC_LOG_ERROR, "Incorrect query flag passed to " + "dlz_ldap_get_results"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ + if (querystring == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* + * output the full query string during debug so we can see + * what lame error the query has. + */ + db->log(ISC_LOG_DEBUG(1), "Query String: %s", querystring); + + /* break URL down into it's component parts, if error cleanup */ + ldap_result = ldap_url_parse(querystring, &ldap_url); + if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + for (i = 0; i < 3; i++) { + /* + * dbi->dbconn may be null if trying to reconnect on a + * previous query failed. + */ + if (dbi->dbconn == NULL) { + db->log(ISC_LOG_INFO, "LDAP driver attempting to " + "re-connect"); + + result = dlz_ldap_connect((ldap_instance_t *)dbdata, + dbi); + if (result != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + continue; + } + } + + /* perform ldap search synchronously */ + ldap_result = + ldap_search_s((LDAP *)dbi->dbconn, ldap_url->lud_dn, + ldap_url->lud_scope, ldap_url->lud_filter, + ldap_url->lud_attrs, 0, &ldap_msg); + + /* + * check return code. No such object is ok, just + * didn't find what we wanted + */ + switch (ldap_result) { + case LDAP_NO_SUCH_OBJECT: + db->log(ISC_LOG_DEBUG(1), "No object found matching " + "query requirements"); + result = ISC_R_NOTFOUND; + goto cleanup; + break; + case LDAP_SUCCESS: /* on success do nothing */ + result = ISC_R_SUCCESS; + i = 3; + break; + case LDAP_SERVER_DOWN: + db->log(ISC_LOG_INFO, "LDAP driver attempting to " + "re-connect"); + result = dlz_ldap_connect((ldap_instance_t *)dbdata, + dbi); + if (result != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + break; + default: + /* + * other errors not ok. Log error message and + * get out + */ + db->log(ISC_LOG_ERROR, "LDAP error: %s", + ldap_err2string(ldap_result)); + result = ISC_R_FAILURE; + goto cleanup; + break; + } + } + + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + switch (query) { + case ALLNODES: + result = dlz_ldap_process_results(db, (LDAP *)dbi->dbconn, + ldap_msg, ldap_url->lud_attrs, + ptr, true); + break; + case AUTHORITY: + case LOOKUP: + result = dlz_ldap_process_results(db, (LDAP *)dbi->dbconn, + ldap_msg, ldap_url->lud_attrs, + ptr, false); + break; + case ALLOWXFR: + entries = ldap_count_entries((LDAP *)dbi->dbconn, ldap_msg); + if (entries == 0) { + result = ISC_R_NOPERM; + } else if (entries > 0) { + result = ISC_R_SUCCESS; + } else { + result = ISC_R_FAILURE; + } + break; + case FINDZONE: + entries = ldap_count_entries((LDAP *)dbi->dbconn, ldap_msg); + if (entries == 0) { + result = ISC_R_NOTFOUND; + } else if (entries > 0) { + result = ISC_R_SUCCESS; + } else { + result = ISC_R_FAILURE; + } + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + db->log(ISC_LOG_ERROR, "Incorrect query flag passed to " + "dlz_ldap_get_results"); + result = ISC_R_UNEXPECTED; + } + +cleanup: + /* it's always good to cleanup after yourself */ + + /* if we retrieved results, free them */ + if (ldap_msg != NULL) { + ldap_msgfree(ldap_msg); + } + + if (ldap_url != NULL) { + ldap_free_urldesc(ldap_url); + } + + /* cleanup */ + if (dbi->zone != NULL) { + free(dbi->zone); + } + if (dbi->record != NULL) { + free(dbi->record); + } + if (dbi->client != NULL) { + free(dbi->client); + } + dbi->zone = dbi->record = dbi->client = NULL; + + /* release the lock so another thread can use this dbi */ + (void)dlz_mutex_unlock(&dbi->lock); + + /* release query string */ + if (querystring != NULL) { + free(querystring); + } + + /* return result */ + return (result); +} + +/* + * DLZ methods + */ +isc_result_t +dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { + isc_result_t result; + + /* check to see if we are authoritative for the zone first */ +#if DLZ_DLOPEN_VERSION < 3 + result = dlz_findzonedb(dbdata, name); +#else /* if DLZ_DLOPEN_VERSION < 3 */ + result = dlz_findzonedb(dbdata, name, NULL, NULL); +#endif /* if DLZ_DLOPEN_VERSION < 3 */ + if (result != ISC_R_SUCCESS) { + return (result); + } + + /* get all the zone data */ + result = dlz_ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, + NULL); + return (result); +} + +isc_result_t +dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) { + return (dlz_ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, + allnodes)); +} + +isc_result_t +dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) { + return (dlz_ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, + lookup)); +} + +#if DLZ_DLOPEN_VERSION < 3 +isc_result_t +dlz_findzonedb(void *dbdata, const char *name) +#else /* if DLZ_DLOPEN_VERSION < 3 */ +isc_result_t +dlz_findzonedb(void *dbdata, const char *name, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) +#endif /* if DLZ_DLOPEN_VERSION < 3 */ +{ +#if DLZ_DLOPEN_VERSION >= 3 + UNUSED(methods); + UNUSED(clientinfo); +#endif /* if DLZ_DLOPEN_VERSION >= 3 */ + return (dlz_ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL)); +} + +#if DLZ_DLOPEN_VERSION == 1 +isc_result_t +dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup) +#else /* if DLZ_DLOPEN_VERSION == 1 */ +isc_result_t +dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) +#endif /* if DLZ_DLOPEN_VERSION == 1 */ +{ + isc_result_t result; + +#if DLZ_DLOPEN_VERSION >= 2 + UNUSED(methods); + UNUSED(clientinfo); +#endif /* if DLZ_DLOPEN_VERSION >= 2 */ + + if (strcmp(name, "*") == 0) { + result = dlz_ldap_get_results(zone, "~", NULL, LOOKUP, dbdata, + lookup); + } else { + result = dlz_ldap_get_results(zone, name, NULL, LOOKUP, dbdata, + lookup); + } + return (result); +} + +isc_result_t +dlz_create(const char *dlzname, unsigned int argc, char *argv[], void **dbdata, + ...) { + isc_result_t result = ISC_R_FAILURE; + ldap_instance_t *ldap = NULL; + dbinstance_t *dbi = NULL; + const char *helper_name = NULL; + int protocol, method, dbcount, i; + char *endp = NULL; + va_list ap; + + UNUSED(dlzname); + + /* allocate memory for LDAP instance */ + ldap = calloc(1, sizeof(ldap_instance_t)); + if (ldap == NULL) { + return (ISC_R_NOMEMORY); + } + memset(ldap, 0, sizeof(ldap_instance_t)); + + /* Fill in the helper functions */ + va_start(ap, dbdata); + while ((helper_name = va_arg(ap, const char *)) != NULL) { + b9_add_helper(ldap, helper_name, va_arg(ap, void *)); + } + va_end(ap); + + /* if debugging, let user know we are multithreaded. */ + ldap->log(ISC_LOG_DEBUG(1), "LDAP driver running multithreaded"); + + if (argc < 9) { + ldap->log(ISC_LOG_ERROR, "LDAP driver requires at least " + "8 command line args."); + goto cleanup; + } + + /* no more than 13 arg's should be passed to the driver */ + if (argc > 12) { + ldap->log(ISC_LOG_ERROR, "LDAP driver cannot accept more than " + "11 command line args."); + goto cleanup; + } + + /* determine protocol version. */ + if (strncasecmp(argv[2], V2, strlen(V2)) == 0) { + protocol = 2; + } else if (strncasecmp(argv[2], V3, strlen(V3)) == 0) { + protocol = 3; + } else { + ldap->log(ISC_LOG_ERROR, + "LDAP driver protocol must be either %s or %s", V2, + V3); + goto cleanup; + } + + /* determine connection method. */ + if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0) { + method = LDAP_AUTH_SIMPLE; + } else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0) { + method = LDAP_AUTH_KRBV41; + } else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0) { + method = LDAP_AUTH_KRBV42; + } else { + ldap->log(ISC_LOG_ERROR, + "LDAP driver authentication method must be " + "one of %s, %s or %s", + SIMPLE, KRB41, KRB42); + goto cleanup; + } + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0) { + ldap->log(ISC_LOG_ERROR, "LDAP driver database connection " + "count " + "must be positive."); + goto cleanup; + } + + /* check that LDAP URL parameters make sense */ + switch (argc) { + case 12: + result = dlz_ldap_checkURL(ldap, argv[11], 0, + "allow zone transfer"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + FALLTHROUGH; + case 11: + result = dlz_ldap_checkURL(ldap, argv[10], 3, "all nodes"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + FALLTHROUGH; + case 10: + if (strlen(argv[9]) > 0) { + result = dlz_ldap_checkURL(ldap, argv[9], 3, + "authority"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + FALLTHROUGH; + case 9: + result = dlz_ldap_checkURL(ldap, argv[8], 3, "lookup"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + result = dlz_ldap_checkURL(ldap, argv[7], 0, "find zone"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + /* store info needed to automatically re-connect. */ + ldap->protocol = protocol; + ldap->method = method; + ldap->hosts = strdup(argv[6]); + if (ldap->hosts == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + ldap->user = strdup(argv[4]); + if (ldap->user == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + ldap->cred = strdup(argv[5]); + if (ldap->cred == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* allocate memory for database connection list */ + ldap->db = calloc(1, sizeof(db_list_t)); + if (ldap->db == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* initialize DB connection list */ + DLZ_LIST_INIT(*(ldap->db)); + + /* + * create the appropriate number of database instances (DBI) + * append each new DBI to the end of the list + */ + for (i = 0; i < dbcount; i++) { + /* how many queries were passed in from config file? */ + switch (argc) { + case 9: + result = build_dbinstance(NULL, NULL, NULL, argv[7], + argv[8], NULL, &dbi, + ldap->log); + break; + case 10: + result = build_dbinstance(NULL, NULL, argv[9], argv[7], + argv[8], NULL, &dbi, + ldap->log); + break; + case 11: + result = build_dbinstance(argv[10], NULL, argv[9], + argv[7], argv[8], NULL, &dbi, + ldap->log); + break; + case 12: + result = build_dbinstance(argv[10], argv[11], argv[9], + argv[7], argv[8], NULL, &dbi, + ldap->log); + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + if (result == ISC_R_SUCCESS) { + ldap->log(ISC_LOG_DEBUG(2), "LDAP driver created " + "database instance " + "object."); + } else { /* unsuccessful?, log err msg and cleanup. */ + ldap->log(ISC_LOG_ERROR, "LDAP driver could not create " + "database instance object."); + goto cleanup; + } + + /* when multithreaded, build a list of DBI's */ + DLZ_LINK_INIT(dbi, link); + DLZ_LIST_APPEND(*(ldap->db), dbi, link); + /* attempt to connect */ + result = dlz_ldap_connect(ldap, dbi); + + /* + * if db connection cannot be created, log err msg and + * cleanup. + */ + switch (result) { + /* success, do nothing */ + case ISC_R_SUCCESS: + break; + /* + * no memory means ldap_init could not + * allocate memory + */ + case ISC_R_NOMEMORY: + ldap->log(ISC_LOG_ERROR, + "LDAP driver could not allocate memory " + "for connection number %u", + i + 1); + goto cleanup; + /* + * no perm means ldap_set_option could not set + * protocol version + */ + case ISC_R_NOPERM: + ldap->log(ISC_LOG_ERROR, "LDAP driver could not " + "set protocol version."); + result = ISC_R_FAILURE; + goto cleanup; + /* failure means couldn't connect to ldap server */ + case ISC_R_FAILURE: + ldap->log(ISC_LOG_ERROR, + "LDAP driver could not bind " + "connection number %u to server.", + i + 1); + goto cleanup; + /* + * default should never happen. If it does, + * major errors. + */ + default: + ldap->log(ISC_LOG_ERROR, "dlz_create() failed (%d)", + result); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* set DBI = null for next loop through. */ + dbi = NULL; + } + + /* set dbdata to the ldap_instance we created. */ + *dbdata = ldap; + + return (ISC_R_SUCCESS); + +cleanup: + dlz_destroy(ldap); + + return (result); +} + +void +dlz_destroy(void *dbdata) { + if (dbdata != NULL) { + ldap_instance_t *db = (ldap_instance_t *)dbdata; + /* cleanup the list of DBI's */ + if (db->db != NULL) { + dlz_ldap_destroy_dblist((db_list_t *)(db->db)); + } + + if (db->hosts != NULL) { + free(db->hosts); + } + if (db->user != NULL) { + free(db->user); + } + if (db->cred != NULL) { + free(db->cred); + } + free(dbdata); + } +} + +/* + * Return the version of the API + */ +int +dlz_version(unsigned int *flags) { + *flags |= DNS_SDLZFLAG_RELATIVERDATA | DNS_SDLZFLAG_THREADSAFE; + return (DLZ_DLOPEN_VERSION); +} + +/* + * Register a helper function from the bind9 dlz_dlopen driver + */ +static void +b9_add_helper(ldap_instance_t *db, const char *helper_name, void *ptr) { + if (strcmp(helper_name, "log") == 0) { + db->log = (log_t *)ptr; + } + if (strcmp(helper_name, "putrr") == 0) { + db->putrr = (dns_sdlz_putrr_t *)ptr; + } + if (strcmp(helper_name, "putnamedrr") == 0) { + db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr; + } + if (strcmp(helper_name, "writeable_zone") == 0) { + db->writeable_zone = (dns_dlz_writeablezone_t *)ptr; + } +} diff --git a/contrib/dlz/modules/ldap/testing/README b/contrib/dlz/modules/ldap/testing/README new file mode 100644 index 0000000..69b1381 --- /dev/null +++ b/contrib/dlz/modules/ldap/testing/README @@ -0,0 +1,10 @@ +These files were used for testing on Ubuntu Linux using OpenLDAP. + +- Move aside /etc/ldap/slapd.d +- Move slapd.conf to /etc/ldap +- Move dlz.schema to /etc/ldap/schema/dlz.schema +- Run "/etc/init.d/slapd restart" +- Run "ldapadd -x -f example.ldif -D 'cn=Manager,o=bind-dlz' -w secret" + +LDAP server is now loaded with example.com data from the file example.ldif + diff --git a/contrib/dlz/modules/ldap/testing/dlz.schema b/contrib/dlz/modules/ldap/testing/dlz.schema new file mode 100644 index 0000000..d0f0086 --- /dev/null +++ b/contrib/dlz/modules/ldap/testing/dlz.schema @@ -0,0 +1,192 @@ +# +# +# 1.3.6.1.4.1.18420.1.1.X is reserved for attribute types declared by the DLZ project. +# 1.3.6.1.4.1.18420.1.2.X is reserved for object classes declared by the DLZ project. +# 1.3.6.1.4.1.18420.1.3.X is reserved for PRIVATE extensions to the DLZ attribute +# types and object classes that may be needed by end users +# to add security, etc. Attributes and object classes using +# this OID MUST NOT be published outside of an organization +# except to offer them for consideration to become part of the +# standard attributes and object classes published by the DLZ project. + +attributetype ( 1.3.6.1.4.1.18420.1.1.10 + NAME 'dlzZoneName' + DESC 'DNS zone name - domain name not including host name' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.20 + NAME 'dlzHostName' + DESC 'Host portion of a domain name' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.30 + NAME 'dlzData' + DESC 'Data for the resource record' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.40 + NAME 'dlzType' + DESC 'DNS record type - A, SOA, NS, MX, etc...' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.50 + NAME 'dlzSerial' + DESC 'SOA record serial number' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.60 + NAME 'dlzRefresh' + DESC 'SOA record refresh time in seconds' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.70 + NAME 'dlzRetry' + DESC 'SOA retry time in seconds' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.80 + NAME 'dlzExpire' + DESC 'SOA expire time in seconds' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.90 + NAME 'dlzMinimum' + DESC 'SOA minimum time in seconds' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.100 + NAME 'dlzAdminEmail' + DESC 'E-mail address of person responsible for this zone - @ should be replaced with . (period)' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.110 + NAME 'dlzPrimaryNS' + DESC 'Primary name server for this zone - should be host name not IP address' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.120 + NAME 'dlzIPAddr' + DESC 'IP address - IPV4 should be in dot notation xxx.xxx.xxx.xxx IPV6 should be in colon notation xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{40} + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.130 + NAME 'dlzCName' + DESC 'DNS cname' + SUP name + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.140 + NAME 'dlzPreference' + DESC 'DNS MX record preference. Lower numbers have higher preference' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.150 + NAME 'dlzTTL' + DESC 'DNS time to live - how long this record can be cached by caching DNS servers' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.18420.1.1.160 + NAME 'dlzRecordID' + DESC 'Unique ID for each DLZ resource record' + SUP name + SINGLE-VALUE ) + +#------------------------------------------------------------------------------ +# Object class definitions +#------------------------------------------------------------------------------ + +objectclass ( 1.3.6.1.4.1.18420.1.2.10 + NAME 'dlzZone' + DESC 'Zone name portion of a domain name' + SUP top STRUCTURAL + MUST ( objectclass $ dlzZoneName ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.20 + NAME 'dlzHost' + DESC 'Host name portion of a domain name' + SUP top STRUCTURAL + MUST ( objectclass $ dlzHostName ) MAY ( description ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.30 + NAME 'dlzAbstractRecord' + DESC 'Data common to all DNS record types' + SUP top ABSTRACT + MUST ( objectclass $ dlzRecordID $ dlzHostName $ dlzType $ dlzTTL ) MAY ( description ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.40 + NAME 'dlzGenericRecord' + DESC 'Generic DNS record - useful when a specific object class has not been defined for a DNS record' + SUP dlzAbstractRecord STRUCTURAL + MUST ( dlzData ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.50 + NAME 'dlzARecord' + DESC 'DNS A record' + SUP dlzAbstractrecord STRUCTURAL + MUST ( dlzIPAddr ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.60 + NAME 'dlzNSRecord' + DESC 'DNS NS record' + SUP dlzGenericRecord STRUCTURAL ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.70 + NAME 'dlzMXRecord' + DESC 'DNS MX record' + SUP dlzGenericRecord STRUCTURAL + MUST ( dlzPreference ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.80 + NAME 'dlzSOARecord' + DESC 'DNS SOA record' + SUP dlzAbstractRecord STRUCTURAL + MUST ( dlzSerial $ dlzRefresh $ dlzRetry + $ dlzExpire $ dlzMinimum $ dlzAdminEmail $ dlzPrimaryNS ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.90 + NAME 'dlzTextRecord' + DESC 'Text data with spaces should be wrapped in double quotes' + SUP dlzGenericRecord STRUCTURAL ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.100 + NAME 'dlzPTRRecord' + DESC 'DNS PTR record' + SUP dlzGenericRecord STRUCTURAL ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.110 + NAME 'dlzCNameRecord' + DESC 'DNS CName record' + SUP dlzGenericRecord STRUCTURAL ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.120 + NAME 'dlzXFR' + DESC 'Host allowed to perform zone transfer' + SUP top STRUCTURAL + MUST ( objectclass $ dlzRecordID $ dlzIPAddr ) ) + +objectclass ( 1.3.6.1.4.1.18420.1.2.130 + NAME 'dlzDNameRecord' + DESC 'DNS DName record' + SUP dlzGenericRecord STRUCTURAL ) diff --git a/contrib/dlz/modules/ldap/testing/example.ldif b/contrib/dlz/modules/ldap/testing/example.ldif new file mode 100644 index 0000000..fff1793 --- /dev/null +++ b/contrib/dlz/modules/ldap/testing/example.ldif @@ -0,0 +1,192 @@ +# server suffix - o=bind-dlz + +dn: o=bind-dlz +objectclass: organization +o: bind-dlz + +dn: ou=dns,o=bind-dlz +objectclass: organizationalUnit +ou: dns + +dn: dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzZone +dlzZoneName: example.com + +dn: dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: @ + +dn: dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: www + +dn: dlzHostName=mail,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: mail + +dn: dlzHostName=backup,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: backup + +dn: dlzHostName=ns1,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: ns1 + +dn: dlzHostName=ns2,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: ns2 + +dn: dlzHostName=~,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: ~ + +dn: dlzHostName=cname,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: cname + +dn: dlzHostName=dname,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzHost +dlzHostName: dname + +dn: dlzRecordID=1,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzGenericRecord +dlzRecordID: 1 +dlzHostName: @ +dlzType: txt +dlzData: "this is a text record" +dlzTTL: 10 + +dn: dlzRecordID=2,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzARecord +dlzRecordID: 2 +dlzHostName: www +dlzType: a +dlzIPAddr: 192.168.0.1 +dlzTTL: 10 + +dn: dlzRecordID=3,dlzHostName=mail,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzARecord +dlzRecordID: 3 +dlzHostName: mail +dlzType: a +dlzIPAddr: 192.168.0.2 +dlzTTL: 10 + +dn: dlzRecordID=4,dlzHostName=backup,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzARecord +dlzRecordID: 4 +dlzHostName: backup +dlzType: a +dlzIPAddr: 192.168.0.3 +dlzTTL: 10 + +dn: dlzRecordID=5,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzMXRecord +dlzRecordID: 5 +dlzHostName: @ +dlzType: mx +dlzData: mail +dlzPreference: 20 +dlzTTL: 10 + +dn: dlzRecordID=6,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzMXRecord +dlzRecordID: 6 +dlzHostName: @ +dlzType: mx +dlzData: backup +dlzPreference: 40 +dlzTTL: 10 + +dn: dlzRecordID=7,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzMXRecord +dlzRecordID: 7 +dlzHostName: www +dlzType: mx +dlzData: backup +dlzPreference: 40 +dlzTTL: 10 + +dn: dlzRecordID=8,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzMXRecord +dlzRecordID: 8 +dlzHostName: www +dlzType: mx +dlzData: mail +dlzPreference: 20 +dlzTTL: 10 + +dn: dlzRecordID=9,dlzHostName=ns1,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzARecord +dlzRecordID: 9 +dlzHostName: ns1 +dlzType: a +dlzIPAddr: 192.168.0.4 +dlzTTL: 10 + +dn: dlzRecordID=10,dlzHostName=ns2,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzARecord +dlzRecordID: 10 +dlzHostName: ns2 +dlzType: a +dlzIPAddr: 192.168.0.5 +dlzTTL: 10 + +dn: dlzRecordID=11,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzSOARecord +dlzRecordID: 11 +dlzHostName: @ +dlzType: soa +dlzSerial: 2 +dlzRefresh: 2800 +dlzRetry: 7200 +dlzExpire: 604800 +dlzMinimum: 86400 +dlzAdminEmail: root.example.com. +dlzPrimaryns: ns1.example.com. +dlzTTL: 10 + +dn: dlzRecordID=12,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzNSRecord +dlzRecordID: 12 +dlzHostName: @ +dlzType: ns +dlzData: ns1.example.com. +dlzTTL: 10 + +dn: dlzRecordID=13,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzNSRecord +dlzRecordID: 13 +dlzHostName: @ +dlzType: ns +dlzData: ns2 +dlzTTL: 10 + +dn: dlzRecordID=14,dlzHostName=~,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzARecord +dlzRecordID: 14 +dlzHostName: ~ +dlzType: a +dlzIPAddr: 192.168.0.250 +dlzTTL: 10 + +dn: dlzRecordID=15,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzXFR +dlzRecordID: 15 +dlzIPAddr: 127.0.0.1 + +dn: dlzRecordID=16,dlzHostName=cname,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzCNameRecord +dlzRecordID: 16 +dlzHostName: cname +dlzType: cname +dlzData: www +dlzTTL: 10 + +dn: dlzRecordID=17,dlzHostName=dname,dlzZoneName=example.com,ou=dns,o=bind-dlz +objectclass: dlzDNameRecord +dlzRecordID: 17 +dlzHostName: dname +dlzType: dname +dlzData: example.net. +dlzTTL: 10 diff --git a/contrib/dlz/modules/ldap/testing/named.conf b/contrib/dlz/modules/ldap/testing/named.conf new file mode 100644 index 0000000..3f8378b --- /dev/null +++ b/contrib/dlz/modules/ldap/testing/named.conf @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +controls { }; + +options { + directory "."; + port 5300; + pid-file "named.pid"; + session-keyfile "session.key"; + listen-on { any; }; + listen-on-v6 { none; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-md5; +}; + +controls { + inet 127.0.0.1 port 9953 allow { any; } keys { rndc_key; }; +}; + +dlz "test" { + database "dlopen ../dlz_ldap_dynamic.so 2 + v3 simple {cn=Manager,o=bind-dlz} {secret} {127.0.0.1} + ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz???objectclass=dlzZone + ldap:///dlzHostName=$record$,dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzPreference,dlzData,dlzIPAddr?sub?(&(objectclass=dlzAbstractRecord)(!(dlzType=soa))) + ldap:///dlzHostName=@,dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzData,dlzPrimaryNS,dlzAdminEmail,dlzSerial,dlzRefresh,dlzRetry,dlzExpire,dlzMinimum?sub?(&(objectclass=dlzAbstractRecord)(dlzType=soa)) + ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzHostName,dlzPreference,dlzData,dlzIPAddr,dlzPrimaryNS,dlzAdminEmail,dlzSerial,dlzRefresh,dlzRetry,dlzExpire,dlzMinimum?sub?(&(objectclass=dlzAbstractRecord)(!(dlzType=soa))) + ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz??sub?(&(objectclass=dlzXFR)(dlzIPAddr=$client$))"; +}; diff --git a/contrib/dlz/modules/ldap/testing/slapd.conf b/contrib/dlz/modules/ldap/testing/slapd.conf new file mode 100644 index 0000000..d4a6287 --- /dev/null +++ b/contrib/dlz/modules/ldap/testing/slapd.conf @@ -0,0 +1,44 @@ +# this is the full path to the core.schema +include /etc/ldap/schema/core.schema + +# this is the full path to the dlz.schema +include /etc/ldap/schema/dlz.schema + +# these files hold the slapd process ID and program args when +# slapd is started. +pidfile /var/run/slapd/slapd.pid +argsfile /var/run/slapd/slapd.args + +modulepath /usr/lib/ldap +moduleload back_hdb + +# this allows ldap version 2 connections. You should comment +# it out if you don't need ldap version 2. +allow bind_v2 + +# this sets up the Berkeley DB database backend for LDAP to use. +database hdb + +# This is the root of the LDAP server. You still need to add +# an entry to this location via a LDIF file, or you won't be +# able to add anything else into the LDAP server. +suffix "o=bind-dlz" + +# this is the "username" you have to use when connecting to the +# ldap server to make updates. Type the whole thing exactly +# as you see it as a parameter to ldapadd. +rootdn "cn=Manager,o=bind-dlz" + +# this is the "password" you have to use when connecting to the +# ldap server to make updates. +rootpw secret + +# this is the directory that the LDAP server will create the +# Berkeley DB backend in. +directory /var/lib/ldap + +# this just adds some indexing to the LDAP server. +# probably should have more to better optimize DLZ LDAP searches. +index cn,sn,uid pres,eq +index objectClass eq + |