/*
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
*
* 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.
*
* 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 ROB BUTLER
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* ROB BUTLER 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.
*/
/*
* Copyright (C) 1999-2001, 2016 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 http://mozilla.org/MPL/2.0/.
*/
#ifdef DLZ_LDAP
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <isc/platform.h>
#include <dlz/sdlz_helper.h>
#include <dlz/dlz_ldap_driver.h>
/*
* Need older API functions from ldap.h.
*/
#include <ldap.h>
/*%
* Structure to hold everthing needed by this "instance" of the LDAP
* driver remember, the driver code is only loaded once, but may have
* many separate instances.
*/
typedef struct {
#ifdef ISC_PLATFORM_USETHREADS
#else
#endif
/* forward references */
static isc_result_t
static void
/*
* Private methods
*/
/*% checks that the LDAP URL parameters make sense */
static isc_result_t
int ldap_result;
if (!ldap_is_ldap_url(URL)) {
"%s query is not a valid LDAP URL", msg);
goto cleanup;
}
"parsing %s query failed", msg);
goto cleanup;
}
"%s query must specify at least "
"%d attributes to return",
goto cleanup;
}
"%s query must not specify a host", msg);
goto cleanup;
}
"%s query must not specify a port", msg);
goto cleanup;
}
"%s query must specify a search base", msg);
goto cleanup;
}
"%s uses extensions. "
"The driver does not support LDAP extensions.",
msg);
goto cleanup;
}
return (result);
}
/*% Connects / reconnects to LDAP server */
static isc_result_t
int ldap_result;
/* if we have a connection, get ride of it. */
}
/* now connect / reconnect. */
/* initialize. */
return (ISC_R_NOMEMORY);
/* set protocol version. */
if (ldap_result != LDAP_SUCCESS) {
goto cleanup;
}
/* "bind" to server. i.e. send username / pass */
if (ldap_result != LDAP_SUCCESS) {
goto cleanup;
}
return (ISC_R_SUCCESS);
/* cleanup if failure. */
}
return (result);
}
#ifdef ISC_PLATFORM_USETHREADS
/*%
* Properly cleans up a list of database instances.
* This function is only used when the driver is compiled for
* multithreaded operation.
*/
static void
/* get the first DBI in the list */
/* loop through the list */
/* get the next DBI in the list */
/* release DB connection */
/* release all memory that comprised a DBI */
}
/* release memory for the list structure */
}
/*%
* 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 *
int count = 0;
/* get top of list */
/* loop through list */
while (count < dbc_search_limit) {
/* try to lock on the mutex */
return (dbi); /* success, return the DBI for use. */
/* not successful, keep trying */
/* check to see if we have gone to the top of the list. */
count++;
}
}
"LDAP driver unable to find available connection "
"after searching %d times",
count);
return (NULL);
}
#endif /* ISC_PLATFORM_USETHREADS */
static isc_result_t
{
int i = 0;
int j;
int len;
int ttl;
/* make sure there are at least some attributes to process. */
/* get the first entry to process */
"LDAP no entries to process.");
return (ISC_R_FAILURE);
}
/* loop through all entries returned */
/* reset for this loop */
ttl = 0;
len = 0;
i = 0;
/* determine how much space we need for data string */
/* get the list of values for this attribute. */
/* skip empty attributes. */
continue;
/*
* we only use the first value. this driver
* does not support multi-valued attributes.
*/
/* free vals for next loop */
} /* end for (j = 0; attrs[j] != NULL, j++) loop */
/* allocate memory for data string */
"LDAP driver unable to allocate memory "
"while processing results");
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. */
/* get the list of values for this attribute. */
/* skip empty attributes. */
/* increment attibute pointer */
/* 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
*/
"LDAP driver ttl must "
"be a postive number");
goto cleanup;
}
break;
case 1:
j++;
break;
case 2:
j++;
if (allnodes)
vals[0]);
else
break;
case 3:
j++;
if (allnodes)
else {
}
break;
default:
break;
} /* end switch(j) */
/* free values */
/* increment attibute pointer */
} /* end while (attribute != NULL) */
"LDAP driver unable "
"to retrieve DNS type");
goto cleanup;
}
"LDAP driver unable "
"to retrieve DNS data");
goto cleanup;
}
(dns_sdlzallnodes_t *) ptr,
else
(dns_sdlzallnodes_t *) ptr,
if (result != ISC_R_SUCCESS)
"dlz-ldap: putnamedrr failed "
"for \"%s %s %u %s\", %s",
} else {
if (result != ISC_R_SUCCESS)
"dlz-ldap: putrr failed "
"for \"%s %u %s\", %s",
}
if (result != ISC_R_SUCCESS) {
"LDAP driver failed "
"while sending data to BIND.");
goto cleanup;
}
/* free memory for type, data and host for next loop */
/* get the next entry to process */
} /* end while (entry != NULL) */
/* de-allocate memory */
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
{
int ldap_result = 0;
int i;
int entries;
/* get db instance / connection */
#ifdef ISC_PLATFORM_USETHREADS
/* find an available DBI from the list */
#else /* ISC_PLATFORM_USETHREADS */
/*
* only 1 DBI - no need to lock instance lock either
* only 1 thread in the whole process, no possible contention.
*/
#endif /* ISC_PLATFORM_USETHREADS */
/* if DBI is null, can't do anything else */
return (ISC_R_FAILURE);
/* set fields */
goto cleanup;
}
} else {
}
goto cleanup;
}
} else {
}
goto cleanup;
}
} else {
}
/* 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!!!
*/
goto cleanup;
} else {
dbi->allnodes_q);
}
break;
case ALLOWXFR:
/* same as comments as ALLNODES */
goto cleanup;
} else {
dbi->allowxfr_q);
}
break;
case AUTHORITY:
/* same as comments as ALLNODES */
goto cleanup;
} else {
dbi->authority_q);
}
break;
case FINDZONE:
/* this is required. It's the whole point of DLZ! */
"No query specified for findzone. "
"Findzone requires a query");
goto cleanup;
} else {
dbi->findzone_q);
}
break;
case LOOKUP:
/* this is required. It's also a major point of DLZ! */
"No query specified for lookup. "
"Lookup requires a query");
goto cleanup;
} else {
}
break;
default:
/*
* this should never happen. If it does, the code is
* screwed up!
*/
"Incorrect query flag passed to "
"ldap_get_results");
goto cleanup;
}
/* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
if (querystring == NULL) {
goto cleanup;
}
/*
* output the full query string during debug so we can see
* what lame error the query has.
*/
"\nQuery String: %s\n", querystring);
/* break URL down into it's component parts, if error cleanup */
goto cleanup;
}
for (i = 0; i < 3; i++) {
/*
* dbi->dbconn may be null if trying to reconnect on a
* previous query failed.
*/
"LDAP driver attempting to re-connect");
dbi);
if (result != ISC_R_SUCCESS) {
continue;
}
}
/* perform ldap search syncronously */
/*
* check return code. No such object is ok, just
* didn't find what we wanted
*/
switch(ldap_result) {
case LDAP_NO_SUCH_OBJECT:
"No object found matching "
"query requirements");
goto cleanup;
break;
case LDAP_SUCCESS: /* on success do nothing */
i = 3;
break;
case LDAP_SERVER_DOWN:
"LDAP driver attempting to re-connect");
dbi);
if (result != ISC_R_SUCCESS)
break;
default:
/*
* other errors not ok. Log error message and
* get out
*/
"LDAP error: %s",
goto cleanup;
break;
} /* close switch(ldap_result) */
} /* end for (int i = 0 i < 3; i++) */
if (result != ISC_R_SUCCESS)
goto cleanup;
switch(query) {
case ALLNODES:
break;
case AUTHORITY:
case LOOKUP:
break;
case ALLOWXFR:
if (entries == 0)
else if (entries > 0)
else
break;
case FINDZONE:
if (entries == 0)
else if (entries > 0)
else
break;
default:
/*
* this should never happen. If it does, the code is
* screwed up!
*/
"Incorrect query flag passed to "
"ldap_get_results");
}
/* it's always good to cleanup after yourself */
/* if we retrieved results, free them */
/* cleanup */
#ifdef ISC_PLATFORM_USETHREADS
/* release the lock so another thread can use this dbi */
#endif /* ISC_PLATFORM_USETHREADS */
/* release query string */
if (querystring != NULL)
/* return result */
return (result);
}
/*
* DLZ methods
*/
static isc_result_t
const char *client)
{
/* check to see if we are authoritative for the zone first */
if (result != ISC_R_SUCCESS) {
return (result);
}
/* get all the zone data */
return (result);
}
static isc_result_t
{
}
static isc_result_t
{
}
static isc_result_t
{
}
static isc_result_t
{
else
return (result);
}
static isc_result_t
{
int protocol;
int method;
#ifdef ISC_PLATFORM_USETHREADS
/* if multi-threaded, we need a few extra variables. */
int dbcount;
char *endp;
/* db_list_t *dblist = NULL; */
int i;
#endif /* ISC_PLATFORM_USETHREADS */
#ifdef ISC_PLATFORM_USETHREADS
/* if debugging, let user know we are multithreaded. */
"LDAP driver running multithreaded");
#else /* ISC_PLATFORM_USETHREADS */
/* if debugging, let user know we are single threaded. */
"LDAP driver running single threaded");
#endif /* ISC_PLATFORM_USETHREADS */
if (argc < 9) {
"LDAP driver requires at least "
"8 command line args.");
return (ISC_R_FAILURE);
}
/* no more than 13 arg's should be passed to the driver */
if (argc > 12) {
"LDAP driver cannot accept more than "
"11 command line args.");
return (ISC_R_FAILURE);
}
/* determine protocol version. */
protocol = 2;
protocol = 3;
} else {
"LDAP driver protocol must be either %s or %s",
return (ISC_R_FAILURE);
}
/* determine connection method. */
} else {
"LDAP driver authentication method must be "
"one of %s, %s or %s",
return (ISC_R_FAILURE);
}
/* multithreaded build can have multiple DB connections */
#ifdef ISC_PLATFORM_USETHREADS
/* check how many db connections we should create */
"LDAP driver database connection count "
"must be positive.");
return (ISC_R_FAILURE);
}
#endif
/* check that LDAP URL parameters make sense */
switch(argc) {
case 12:
if (result != ISC_R_SUCCESS)
return (result);
case 11:
if (result != ISC_R_SUCCESS)
return (result);
case 10:
if (result != ISC_R_SUCCESS)
return (result);
}
case 9:
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
break;
default:
/* not really needed, should shut up compiler. */
}
/* allocate memory for LDAP instance */
return (ISC_R_NOMEMORY);
/* store info needed to automatically re-connect. */
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
#ifdef ISC_PLATFORM_USETHREADS
/* allocate memory for database connection list */
goto cleanup;
}
/* initialize DB connection list */
/*
* create the appropriate number of database instances (DBI)
* append each new DBI to the end of the list
*/
for (i = 0; i < dbcount; i++) {
#endif /* ISC_PLATFORM_USETHREADS */
/* how many queries were passed in from config file? */
switch(argc) {
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
default:
/* not really needed, should shut up compiler. */
}
if (result == ISC_R_SUCCESS) {
"LDAP driver created "
"database instance object.");
} else { /* unsuccessful?, log err msg and cleanup. */
"LDAP driver could not create "
"database instance object.");
goto cleanup;
}
#ifdef ISC_PLATFORM_USETHREADS
/* when multithreaded, build a list of DBI's */
#else
/*
* when single threaded, hold onto the one connection
* instance.
*/
#endif
/* attempt to connect */
/*
* 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:
#ifdef ISC_PLATFORM_USETHREADS
"LDAP driver could not allocate memory "
"for connection number %u",
i+1);
#else
"LDAP driver could not allocate memory "
"for connection");
#endif
goto cleanup;
break;
/*
* no perm means ldap_set_option could not set
* protocol version
*/
case ISC_R_NOPERM:
"LDAP driver could not "
"set protocol version.");
goto cleanup;
break;
/* failure means couldn't connect to ldap server */
case ISC_R_FAILURE:
#ifdef ISC_PLATFORM_USETHREADS
"LDAP driver could not "
"bind connection number %u to server.",
i+1);
#else
"LDAP driver could not "
"bind connection to server.");
#endif
goto cleanup;
break;
/*
* default should never happen. If it does,
* major errors.
*/
default:
"dlz_ldap_create() failed: %s",
goto cleanup;
break;
} /* end switch(result) */
#ifdef ISC_PLATFORM_USETHREADS
/* set DBI = null for next loop through. */
} /* end for loop */
#endif /* ISC_PLATFORM_USETHREADS */
/* set dbdata to the ldap_instance we created. */
/* hey, we got through all of that ok, return success. */
return(ISC_R_SUCCESS);
return(ISC_R_FAILURE);
}
void
#ifdef ISC_PLATFORM_USETHREADS
/* cleanup the list of DBI's */
#else /* ISC_PLATFORM_USETHREADS */
ldap_unbind_s((LDAP *)
/* destroy single DB instance */
#endif /* ISC_PLATFORM_USETHREADS */
}
}
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
/*%
* Wrapper around dns_sdlzregister().
*/
dlz_ldap_init(void) {
/*
* Write debugging message to log
*/
"Registering DLZ ldap driver.");
if (result != ISC_R_SUCCESS) {
"dns_sdlzregister() failed: %s",
}
return (result);
}
/*%
* Wrapper around dns_sdlzunregister().
*/
void
dlz_ldap_clear(void) {
/*
* Write debugging message to log
*/
"Unregistering DLZ ldap driver.");
}
#endif