9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews * ldapdb.c version 1.0-beta
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews * Copyright (C) 2002, 2004 Stig Venaas
bf49a52178db60df60f2316a3977b2249f7c0edbBrian Wellington * Permission to use, copy, modify, and distribute this software for any
bf49a52178db60df60f2316a3977b2249f7c0edbBrian Wellington * purpose with or without fee is hereby granted, provided that the above
bf49a52178db60df60f2316a3977b2249f7c0edbBrian Wellington * copyright notice and this permission notice appear in all copies.
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews * Contributors: Jeremy C. McDermond
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews * If you want to use TLS, uncomment the define below
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews/* #define LDAPDB_TLS */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews * If you are using an old LDAP API uncomment the define below. Only do this
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews * if you know what you're doing or get compilation errors on ldap_memfree().
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews * This also forces LDAPv2.
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews/* #define LDAPDB_RFC1823API */
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews/* Using LDAPv3 by default, change this if you want v2 */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews * A simple database driver for LDAP
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews/* enough for name with 8 labels of max length */
bf49a52178db60df60f2316a3977b2249f7c0edbBrian Wellingtonstatic dns_sdbimplementation_t *ldapdb = NULL;
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington/* used by ldapdb_getconn */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellingtonstatic struct ldapdb_entry *ldapdb_find(struct ldapdb_entry *stack,
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington if (stack->size == size && !memcmp(stack->index, index, size))
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellingtonstatic void ldapdb_insert(struct ldapdb_entry **stack,
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington/* data == NULL means cleanup */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington static struct ldapdb_entry *allthreadsdata = NULL;
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington struct ldapdb_entry *threaddata, *conndata;
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* cleanup */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* lock out other threads */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* look for connection data for current thread */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington threaddata = ldapdb_find(allthreadsdata, &threadid, sizeof(threadid));
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* no data for this thread, create empty connection list */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington threaddata->index = malloc(sizeof(threadid));
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington *(unsigned long *)threaddata->index = threadid;
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* need to lock out other threads here */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington ldapdb_insert(&allthreadsdata, threaddata);
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* threaddata points at the connection list for current thread */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* look for existing connection to our server */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington conndata = ldapdb_find((struct ldapdb_entry *)threaddata->data,
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* no connection data structure for this server, create one */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington ldapdb_insert((struct ldapdb_entry **)&threaddata->data,
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellingtonldapdb_bind(struct ldapdb_data *data, LDAP **ldp)
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington *ldp = ldap_open(data->hostname, data->portno);
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews ldap_set_option(*ldp, LDAP_OPT_PROTOCOL_VERSION, &ver);
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews if (ldap_simple_bind_s(*ldp, data->bindname, data->bindpw) != LDAP_SUCCESS) {
793814f80703afdd69b59ade91e63efa81ae4178Evan Huntldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
793814f80703afdd69b59ade91e63efa81ae4178Evan Huntldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt#endif /* DNS_CLIENTINFO_VERSION */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews "LDAP sdb zone '%s': name %s too long", zone, name);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews "LDAP sdb zone '%s': search failed, filter %s", zone, fltr);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* Get the records one by one as they arrive and return them to bind */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews while ((errno = ldap_result(*ldp, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) {
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* not supporting continuation references at present */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews "LDAP sdb zone '%s': ldap_result returned %d", zone, errno);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* only one entry per result message */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews "LDAP sdb zone '%s': ldap_first_entry failed", zone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews names = ldap_get_values(ld, e, "relativeDomainName");
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; a = ldap_next_attribute(ld, e, ptr)) {
bf49a52178db60df60f2316a3977b2249f7c0edbBrian Wellington for (s = a; *s; s++)
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews if ((s == NULL) || (s == a) || (s - a >= (signed int)sizeof(type))) {
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews result = dns_sdb_putrr(retdata, type, ttl, vals[i]);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews result = dns_sdb_putnamedrr(retdata, names[j], type, ttl, vals[i]);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews "LDAP sdb zone '%s': dns_sdb_put... failed for %s", zone, vals[i]);
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews /* free this result */
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews /* free final result */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews/* callback routines */
793814f80703afdd69b59ade91e63efa81ae4178Evan Huntldapdb_lookup(const char *zone, const char *name, void *dbdata,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt return (ldapdb_search(zone, name, dbdata, lookup, NULL, NULL));
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrewsldapdb_lookup(const char *zone, const char *name, void *dbdata,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt return (ldapdb_search(zone, name, dbdata, lookup, methods,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt#endif /* DNS_CLIENTINFO_VERSION */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellingtonldapdb_allnodes(const char *zone, void *dbdata,
793814f80703afdd69b59ade91e63efa81ae4178Evan Hunt return (ldapdb_search(zone, NULL, dbdata, allnodes, NULL, NULL));
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrewsstatic char *
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews static const char hexdigits[] = "0123456789abcdef";
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews char *p, *s = in;
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews if ((p = strchr(hexdigits, tolower(s[1]))) == NULL)
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews if ((p = strchr(hexdigits, tolower(s[2]))) == NULL)
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews/* returns 0 for ok, -1 for bad syntax, -2 for unknown critical extension */
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrewsparseextensions(char *extensions, struct ldapdb_data *data)
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews *s++ = '\0';
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews *s++ = '\0';
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews data->tls = value == NULL || !strcasecmp(value, "true");
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews } else if (critical) {
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_mem_put(ns_g_mctx, data->filterall, data->filteralllen);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_mem_put(ns_g_mctx, data->filterone, data->filteronelen);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrewsldapdb_create(const char *zone, int argc, char **argv,
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* we assume that only one thread will call create at a time */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* want to do this only once for all instances */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data));
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* attrs, scope, filter etc? */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* ignore attributes */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* ignore scope */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* filter */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews /* extensions */
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews *s++ = '\0';
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews /* parse extensions */
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews /* err should be -1 or -2 */
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews "LDAP sdb zone '%s': URL: extension syntax error", zone);
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews "LDAP sdb zone '%s': URL: unknown critical extension", zone);
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews if ((data->base != NULL && unhex(data->base) == NULL) ||
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews (data->bindname != NULL && unhex(data->bindname) == NULL) ||
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews (data->bindpw != NULL && unhex(data->bindpw) == NULL)) {
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
9cc53f2a0819301c2de7ab93197eee11e8a365e7Mark Andrews "LDAP sdb zone '%s': URL: bad hex values", zone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* compute filterall and filterone once and for all */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filteralllen = strlen(zone) + strlen("(zoneName=)") + 1;
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filteronelen = strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filteralllen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=))") + 1;
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filteronelen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filterall = isc_mem_get(ns_g_mctx, data->filteralllen);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filterone = isc_mem_get(ns_g_mctx, data->filteronelen);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews sprintf(data->filterall, "(zoneName=%s)", zone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews sprintf(data->filterone, "(&(zoneName=%s)(relativeDomainName=", zone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews sprintf(data->filterall, "(&%s(zoneName=%s))", filter, zone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews sprintf(data->filterone, "(&%s(zoneName=%s)(relativeDomainName=", filter, zone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->filtername = data->filterone + strlen(data->filterone);
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews /* support URLs with literal IPv6 addresses */
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport + (*data->hostport == '[' ? 1 : 0));
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrews *s++ = '\0';
0ae34c3f6222ece01e0f710c7c0311f5cf9d9c0fMark Andrewsldapdb_destroy(const char *zone, void *driverdata, void **dbdata) {
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington/* Wrapper around dns_sdb_register() */
bf49a52178db60df60f2316a3977b2249f7c0edbBrian Wellington return (dns_sdb_register("ldap", &ldapdb_methods, NULL, flags,
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington/* Wrapper around dns_sdb_unregister() */
adf16bbdb65f8c0845d7c7a8ebc0836ec07bbfeeBrian Wellington /* clean up thread data */