Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / contrib / dlz / drivers / dlz_ldap_driver.c
blob1f65d1c4cb80fb0bb3182c4b6181e9df604f2d32
1 /* $NetBSD: dlz_ldap_driver.c,v 1.7 2014/12/10 04:37:55 christos Exp $ */
3 /*
4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the
8 * above copyright notice and this permission notice appear in all
9 * copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
12 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
17 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
18 * USE OR PERFORMANCE OF THIS SOFTWARE.
20 * The development of Dynamically Loadable Zones (DLZ) for BIND 9 was
21 * conceived and contributed by Rob Butler.
23 * Permission to use, copy, modify, and distribute this software for any
24 * purpose with or without fee is hereby granted, provided that the
25 * above copyright notice and this permission notice appear in all
26 * copies.
28 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
29 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
31 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
34 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
35 * USE OR PERFORMANCE OF THIS SOFTWARE.
39 * Copyright (C) 1999-2001 Internet Software Consortium.
41 * Permission to use, copy, modify, and distribute this software for any
42 * purpose with or without fee is hereby granted, provided that the above
43 * copyright notice and this permission notice appear in all copies.
45 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
46 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
48 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
49 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
50 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
51 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
52 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
55 #ifdef DLZ_LDAP
57 #include <config.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <stdlib.h>
62 #include <dns/log.h>
63 #include <dns/sdlz.h>
64 #include <dns/result.h>
66 #include <isc/mem.h>
67 #include <isc/platform.h>
68 #include <isc/print.h>
69 #include <isc/result.h>
70 #include <isc/string.h>
71 #include <isc/util.h>
73 #include <named/globals.h>
75 #include <dlz/sdlz_helper.h>
76 #include <dlz/dlz_ldap_driver.h>
79 * Need older API functions from ldap.h.
81 #define LDAP_DEPRECATED 1
83 #include <ldap.h>
85 #define SIMPLE "simple"
86 #define KRB41 "krb41"
87 #define KRB42 "krb42"
88 #define V2 "v2"
89 #define V3 "v3"
91 static dns_sdlzimplementation_t *dlz_ldap = NULL;
93 #define dbc_search_limit 30
94 #define ALLNODES 1
95 #define ALLOWXFR 2
96 #define AUTHORITY 3
97 #define FINDZONE 4
98 #define LOOKUP 5
101 * Structure to hold everthing needed by this "instance" of the LDAP
102 * driver remember, the driver code is only loaded once, but may have
103 * many separate instances.
106 typedef struct {
107 #ifdef ISC_PLATFORM_USETHREADS
108 db_list_t *db; /*%< handle to a list of DB */
109 #else
110 dbinstance_t *db; /*%< handle to db */
111 #endif
112 int method; /*%< security authentication method */
113 char *user; /*%< who is authenticating */
114 char *cred; /*%< password for simple authentication method */
115 int protocol; /*%< LDAP communication protocol version */
116 char *hosts; /*%< LDAP server hosts */
117 } ldap_instance_t;
119 /* forward references */
121 static isc_result_t
122 dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name,
123 dns_clientinfomethods_t *methods,
124 dns_clientinfo_t *clientinfo);
126 static void
127 dlz_ldap_destroy(void *driverarg, void *dbdata);
130 * Private methods
133 /*% checks that the LDAP URL parameters make sense */
134 static isc_result_t
135 dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg) {
136 isc_result_t result = ISC_R_SUCCESS;
137 int ldap_result;
138 LDAPURLDesc *ldap_url = NULL;
140 if (!ldap_is_ldap_url(URL)) {
141 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
142 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
143 "%s query is not a valid LDAP URL", msg);
144 result = ISC_R_FAILURE;
145 goto cleanup;
148 ldap_result = ldap_url_parse(URL, &ldap_url);
149 if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
150 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
151 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
152 "parsing %s query failed", msg);
153 result = ISC_R_FAILURE;
154 goto cleanup;
157 if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) {
158 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
159 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
160 "%s query must specify at least "
161 "%d attributes to return",
162 msg, attrCnt);
163 result = ISC_R_FAILURE;
164 goto cleanup;
167 if (ldap_url->lud_host != NULL) {
168 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
169 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
170 "%s query must not specify a host", msg);
171 result = ISC_R_FAILURE;
172 goto cleanup;
175 if (ldap_url->lud_port != 389) {
176 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
177 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
178 "%s query must not specify a port", msg);
179 result = ISC_R_FAILURE;
180 goto cleanup;
183 if (ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1) {
184 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
185 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
186 "%s query must specify a search base", msg);
187 result = ISC_R_FAILURE;
188 goto cleanup;
191 if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) {
192 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
193 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
194 "%s uses extensions. "
195 "The driver does not support LDAP extensions.",
196 msg);
197 result = ISC_R_FAILURE;
198 goto cleanup;
201 cleanup:
202 if (ldap_url != NULL)
203 ldap_free_urldesc(ldap_url);
205 return (result);
208 /*% Connects / reconnects to LDAP server */
209 static isc_result_t
210 dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) {
211 isc_result_t result;
212 int ldap_result;
214 /* if we have a connection, get ride of it. */
215 if (dbc->dbconn != NULL) {
216 ldap_unbind_s((LDAP *) dbc->dbconn);
217 dbc->dbconn = NULL;
220 /* now connect / reconnect. */
222 /* initialize. */
223 dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT);
224 if (dbc->dbconn == NULL)
225 return (ISC_R_NOMEMORY);
227 /* set protocol version. */
228 ldap_result = ldap_set_option((LDAP *) dbc->dbconn,
229 LDAP_OPT_PROTOCOL_VERSION,
230 &(dbi->protocol));
231 if (ldap_result != LDAP_SUCCESS) {
232 result = ISC_R_NOPERM;
233 goto cleanup;
236 /* "bind" to server. i.e. send username / pass */
237 ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user,
238 dbi->cred, dbi->method);
239 if (ldap_result != LDAP_SUCCESS) {
240 result = ISC_R_FAILURE;
241 goto cleanup;
244 return (ISC_R_SUCCESS);
246 cleanup:
248 /* cleanup if failure. */
249 if (dbc->dbconn != NULL) {
250 ldap_unbind_s((LDAP *) dbc->dbconn);
251 dbc->dbconn = NULL;
254 return (result);
257 #ifdef ISC_PLATFORM_USETHREADS
261 * Properly cleans up a list of database instances.
262 * This function is only used when the driver is compiled for
263 * multithreaded operation.
265 static void
266 ldap_destroy_dblist(db_list_t *dblist) {
267 dbinstance_t *ndbi = NULL;
268 dbinstance_t *dbi = NULL;
270 /* get the first DBI in the list */
271 ndbi = ISC_LIST_HEAD(*dblist);
273 /* loop through the list */
274 while (ndbi != NULL) {
275 dbi = ndbi;
276 /* get the next DBI in the list */
277 ndbi = ISC_LIST_NEXT(dbi, link);
278 /* release DB connection */
279 if (dbi->dbconn != NULL)
280 ldap_unbind_s((LDAP *) dbi->dbconn);
281 /* release all memory that comprised a DBI */
282 destroy_sqldbinstance(dbi);
284 /* release memory for the list structure */
285 isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
289 * Loops through the list of DB instances, attempting to lock
290 * on the mutex. If successful, the DBI is reserved for use
291 * and the thread can perform queries against the database.
292 * If the lock fails, the next one in the list is tried.
293 * looping continues until a lock is obtained, or until
294 * the list has been searched dbc_search_limit times.
295 * This function is only used when the driver is compiled for
296 * multithreaded operation.
298 static dbinstance_t *
299 ldap_find_avail_conn(db_list_t *dblist) {
300 dbinstance_t *dbi = NULL;
301 dbinstance_t *head;
302 int count = 0;
304 /* get top of list */
305 head = dbi = ISC_LIST_HEAD(*dblist);
307 /* loop through list */
308 while (count < dbc_search_limit) {
309 /* try to lock on the mutex */
310 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
311 return (dbi); /* success, return the DBI for use. */
313 /* not successful, keep trying */
314 dbi = ISC_LIST_NEXT(dbi, link);
316 /* check to see if we have gone to the top of the list. */
317 if (dbi == NULL) {
318 count++;
319 dbi = head;
322 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
323 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
324 "LDAP driver unable to find available connection "
325 "after searching %d times",
326 count);
327 return (NULL);
329 #endif /* ISC_PLATFORM_USETHREADS */
331 static isc_result_t
332 ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs,
333 void *ptr, isc_boolean_t allnodes)
335 isc_result_t result = ISC_R_SUCCESS;
336 int i = 0;
337 int j;
338 int len;
339 char *attribute = NULL;
340 LDAPMessage *entry;
341 char *endp = NULL;
342 char *host = NULL;
343 char *type = NULL;
344 char *data = NULL;
345 char **vals = NULL;
346 int ttl;
348 /* make sure there are at least some attributes to process. */
349 REQUIRE(attrs != NULL || attrs[0] != NULL);
351 /* get the first entry to process */
352 entry = ldap_first_entry(dbc, msg);
353 if (entry == NULL) {
354 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
355 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
356 "LDAP no entries to process.");
357 return (ISC_R_FAILURE);
360 /* loop through all entries returned */
361 while (entry != NULL) {
362 /* reset for this loop */
363 ttl = 0;
364 len = 0;
365 i = 0;
366 attribute = attrs[i];
368 /* determine how much space we need for data string */
369 for (j = 0; attrs[j] != NULL; j++) {
370 /* get the list of values for this attribute. */
371 vals = ldap_get_values(dbc, entry, attrs[j]);
372 /* skip empty attributes. */
373 if (vals == NULL || ldap_count_values(vals) < 1)
374 continue;
376 * we only use the first value. this driver
377 * does not support multi-valued attributes.
379 len = len + strlen(vals[0]) + 1;
380 /* free vals for next loop */
381 ldap_value_free(vals);
382 } /* end for (j = 0; attrs[j] != NULL, j++) loop */
384 /* allocate memory for data string */
385 data = isc_mem_allocate(ns_g_mctx, len + 1);
386 if (data == NULL) {
387 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
388 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
389 "LDAP driver unable to allocate memory "
390 "while processing results");
391 result = ISC_R_FAILURE;
392 goto cleanup;
396 * Make sure data is null termed at the beginning so
397 * we can check if any data was stored to it later.
399 data[0] = '\0';
401 /* reset j to re-use below */
402 j = 0;
404 /* loop through the attributes in the order specified. */
405 while (attribute != NULL) {
406 /* get the list of values for this attribute. */
407 vals = ldap_get_values(dbc, entry, attribute);
409 /* skip empty attributes. */
410 if (vals == NULL || vals[0] == NULL) {
411 /* increment attibute pointer */
412 attribute = attrs[++i];
413 /* start loop over */
414 continue;
418 * j initially = 0. Increment j each time we
419 * set a field that way next loop will set
420 * next field.
422 switch(j) {
423 case 0:
424 j++;
426 * convert text to int, make sure it
427 * worked right
429 ttl = strtol(vals[0], &endp, 10);
430 if (*endp != '\0' || ttl < 0) {
431 isc_log_write(dns_lctx,
432 DNS_LOGCATEGORY_DATABASE,
433 DNS_LOGMODULE_DLZ,
434 ISC_LOG_ERROR,
435 "LDAP driver ttl must "
436 "be a postive number");
437 goto cleanup;
439 break;
440 case 1:
441 j++;
442 type = isc_mem_strdup(ns_g_mctx, vals[0]);
443 break;
444 case 2:
445 j++;
446 if (allnodes)
447 host = isc_mem_strdup(ns_g_mctx,
448 vals[0]);
449 else
450 strcpy(data, vals[0]);
451 break;
452 case 3:
453 j++;
454 if (allnodes)
455 strcpy(data, vals[0]);
456 else {
457 strcat(data, " ");
458 strcat(data, vals[0]);
460 break;
461 default:
462 strcat(data, " ");
463 strcat(data, vals[0]);
464 break;
465 } /* end switch(j) */
467 /* free values */
468 ldap_value_free(vals);
469 vals = NULL;
471 /* increment attibute pointer */
472 attribute = attrs[++i];
473 } /* end while (attribute != NULL) */
475 if (type == NULL) {
476 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
477 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
478 "LDAP driver unable "
479 "to retrieve DNS type");
480 result = ISC_R_FAILURE;
481 goto cleanup;
484 if (strlen(data) < 1) {
485 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
486 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
487 "LDAP driver unable "
488 "to retrieve DNS data");
489 result = ISC_R_FAILURE;
490 goto cleanup;
493 if (allnodes && host != NULL) {
494 if (strcasecmp(host, "~") == 0)
495 result = dns_sdlz_putnamedrr(
496 (dns_sdlzallnodes_t *) ptr,
497 "*", type, ttl, data);
498 else
499 result = dns_sdlz_putnamedrr(
500 (dns_sdlzallnodes_t *) ptr,
501 host, type, ttl, data);
502 if (result != ISC_R_SUCCESS)
503 isc_log_write(dns_lctx,
504 DNS_LOGCATEGORY_DATABASE,
505 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
506 "dlz-ldap: putnamedrr failed "
507 "for \"%s %s %u %s\", %s",
508 host, type, ttl, data,
509 isc_result_totext(result));
510 } else {
511 result = dns_sdlz_putrr((dns_sdlzlookup_t *) ptr,
512 type, ttl, data);
513 if (result != ISC_R_SUCCESS)
514 isc_log_write(dns_lctx,
515 DNS_LOGCATEGORY_DATABASE,
516 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
517 "dlz-ldap: putrr failed "
518 "for \"%s %u %s\", %s",
519 type, ttl, data,
520 isc_result_totext(result));
523 if (result != ISC_R_SUCCESS) {
524 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
525 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
526 "LDAP driver failed "
527 "while sending data to BIND.");
528 goto cleanup;
531 /* free memory for type, data and host for next loop */
532 isc_mem_free(ns_g_mctx, type);
533 isc_mem_free(ns_g_mctx, data);
534 if (host != NULL)
535 isc_mem_free(ns_g_mctx, host);
537 /* get the next entry to process */
538 entry = ldap_next_entry(dbc, entry);
539 } /* end while (entry != NULL) */
541 cleanup:
542 /* de-allocate memory */
543 if (vals != NULL)
544 ldap_value_free(vals);
545 if (host != NULL)
546 isc_mem_free(ns_g_mctx, host);
547 if (type != NULL)
548 isc_mem_free(ns_g_mctx, type);
549 if (data != NULL)
550 isc_mem_free(ns_g_mctx, data);
552 return (result);
556 * This function is the real core of the driver. Zone, record
557 * and client strings are passed in (or NULL is passed if the
558 * string is not available). The type of query we want to run
559 * is indicated by the query flag, and the dbdata object is passed
560 * passed in to. dbdata really holds either:
561 * 1) a list of database instances (in multithreaded mode) OR
562 * 2) a single database instance (in single threaded mode)
563 * The function will construct the query and obtain an available
564 * database instance (DBI). It will then run the query and hopefully
565 * obtain a result set.
567 static isc_result_t
568 ldap_get_results(const char *zone, const char *record,
569 const char *client, unsigned int query,
570 void *dbdata, void *ptr)
572 isc_result_t result;
573 dbinstance_t *dbi = NULL;
574 char *querystring = NULL;
575 LDAPURLDesc *ldap_url = NULL;
576 int ldap_result = 0;
577 LDAPMessage *ldap_msg = NULL;
578 int i;
579 int entries;
581 /* get db instance / connection */
582 #ifdef ISC_PLATFORM_USETHREADS
584 /* find an available DBI from the list */
585 dbi = ldap_find_avail_conn((db_list_t *)
586 ((ldap_instance_t *)dbdata)->db);
588 #else /* ISC_PLATFORM_USETHREADS */
591 * only 1 DBI - no need to lock instance lock either
592 * only 1 thread in the whole process, no possible contention.
594 dbi = (dbinstance_t *) ((ldap_instance_t *)dbdata)->db;
596 #endif /* ISC_PLATFORM_USETHREADS */
598 /* if DBI is null, can't do anything else */
599 if (dbi == NULL)
600 return (ISC_R_FAILURE);
602 /* set fields */
603 if (zone != NULL) {
604 dbi->zone = isc_mem_strdup(ns_g_mctx, zone);
605 if (dbi->zone == NULL) {
606 result = ISC_R_NOMEMORY;
607 goto cleanup;
609 } else {
610 dbi->zone = NULL;
612 if (record != NULL) {
613 dbi->record = isc_mem_strdup(ns_g_mctx, record);
614 if (dbi->record == NULL) {
615 result = ISC_R_NOMEMORY;
616 goto cleanup;
618 } else {
619 dbi->record = NULL;
621 if (client != NULL) {
622 dbi->client = isc_mem_strdup(ns_g_mctx, client);
623 if (dbi->client == NULL) {
624 result = ISC_R_NOMEMORY;
625 goto cleanup;
627 } else {
628 dbi->client = NULL;
631 /* what type of query are we going to run? */
632 switch(query) {
633 case ALLNODES:
635 * if the query was not passed in from the config file
636 * then we can't run it. return not_implemented, so
637 * it's like the code for that operation was never
638 * built into the driver.... AHHH flexibility!!!
640 if (dbi->allnodes_q == NULL) {
641 result = ISC_R_NOTIMPLEMENTED;
642 goto cleanup;
643 } else {
644 querystring = build_querystring(ns_g_mctx,
645 dbi->allnodes_q);
647 break;
648 case ALLOWXFR:
649 /* same as comments as ALLNODES */
650 if (dbi->allowxfr_q == NULL) {
651 result = ISC_R_NOTIMPLEMENTED;
652 goto cleanup;
653 } else {
654 querystring = build_querystring(ns_g_mctx,
655 dbi->allowxfr_q);
657 break;
658 case AUTHORITY:
659 /* same as comments as ALLNODES */
660 if (dbi->authority_q == NULL) {
661 result = ISC_R_NOTIMPLEMENTED;
662 goto cleanup;
663 } else {
664 querystring = build_querystring(ns_g_mctx,
665 dbi->authority_q);
667 break;
668 case FINDZONE:
669 /* this is required. It's the whole point of DLZ! */
670 if (dbi->findzone_q == NULL) {
671 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
672 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
673 "No query specified for findzone. "
674 "Findzone requires a query");
675 result = ISC_R_FAILURE;
676 goto cleanup;
677 } else {
678 querystring = build_querystring(ns_g_mctx,
679 dbi->findzone_q);
681 break;
682 case LOOKUP:
683 /* this is required. It's also a major point of DLZ! */
684 if (dbi->lookup_q == NULL) {
685 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
686 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
687 "No query specified for lookup. "
688 "Lookup requires a query");
689 result = ISC_R_FAILURE;
690 goto cleanup;
691 } else {
692 querystring = build_querystring(ns_g_mctx,
693 dbi->lookup_q);
695 break;
696 default:
698 * this should never happen. If it does, the code is
699 * screwed up!
701 UNEXPECTED_ERROR(__FILE__, __LINE__,
702 "Incorrect query flag passed to "
703 "ldap_get_results");
704 result = ISC_R_UNEXPECTED;
705 goto cleanup;
708 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
709 if (querystring == NULL) {
710 result = ISC_R_NOMEMORY;
711 goto cleanup;
715 * output the full query string during debug so we can see
716 * what lame error the query has.
718 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
719 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
720 "\nQuery String: %s\n", querystring);
722 /* break URL down into it's component parts, if error cleanup */
723 ldap_result = ldap_url_parse(querystring, &ldap_url);
724 if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
725 result = ISC_R_FAILURE;
726 goto cleanup;
729 for (i = 0; i < 3; i++) {
732 * dbi->dbconn may be null if trying to reconnect on a
733 * previous query failed.
735 if (dbi->dbconn == NULL) {
736 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
737 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
738 "LDAP driver attempting to re-connect");
740 result = dlz_ldap_connect((ldap_instance_t *) dbdata,
741 dbi);
742 if (result != ISC_R_SUCCESS) {
743 result = ISC_R_FAILURE;
744 continue;
748 /* perform ldap search syncronously */
749 ldap_result = ldap_search_s((LDAP *) dbi->dbconn,
750 ldap_url->lud_dn,
751 ldap_url->lud_scope,
752 ldap_url->lud_filter,
753 ldap_url->lud_attrs, 0, &ldap_msg);
756 * check return code. No such object is ok, just
757 * didn't find what we wanted
759 switch(ldap_result) {
760 case LDAP_NO_SUCH_OBJECT:
761 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
762 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
763 "No object found matching "
764 "query requirements");
765 result = ISC_R_NOTFOUND;
766 goto cleanup;
767 break;
768 case LDAP_SUCCESS: /* on success do nothing */
769 result = ISC_R_SUCCESS;
770 i = 3;
771 break;
772 case LDAP_SERVER_DOWN:
773 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
774 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
775 "LDAP driver attempting to re-connect");
776 result = dlz_ldap_connect((ldap_instance_t *) dbdata,
777 dbi);
778 if (result != ISC_R_SUCCESS)
779 result = ISC_R_FAILURE;
780 break;
781 default:
783 * other errors not ok. Log error message and
784 * get out
786 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
787 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
788 "LDAP error: %s",
789 ldap_err2string(ldap_result));
790 result = ISC_R_FAILURE;
791 goto cleanup;
792 break;
793 } /* close switch(ldap_result) */
794 } /* end for (int i = 0 i < 3; i++) */
796 if (result != ISC_R_SUCCESS)
797 goto cleanup;
799 switch(query) {
800 case ALLNODES:
801 result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
802 ldap_url->lud_attrs,
803 ptr, isc_boolean_true);
804 break;
805 case AUTHORITY:
806 case LOOKUP:
807 result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
808 ldap_url->lud_attrs,
809 ptr, isc_boolean_false);
810 break;
811 case ALLOWXFR:
812 entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
813 if (entries == 0)
814 result = ISC_R_NOPERM;
815 else if (entries > 0)
816 result = ISC_R_SUCCESS;
817 else
818 result = ISC_R_FAILURE;
819 break;
820 case FINDZONE:
821 entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
822 if (entries == 0)
823 result = ISC_R_NOTFOUND;
824 else if (entries > 0)
825 result = ISC_R_SUCCESS;
826 else
827 result = ISC_R_FAILURE;
828 break;
829 default:
831 * this should never happen. If it does, the code is
832 * screwed up!
834 UNEXPECTED_ERROR(__FILE__, __LINE__,
835 "Incorrect query flag passed to "
836 "ldap_get_results");
837 result = ISC_R_UNEXPECTED;
840 cleanup:
841 /* it's always good to cleanup after yourself */
843 /* if we retrieved results, free them */
844 if (ldap_msg != NULL)
845 ldap_msgfree(ldap_msg);
847 if (ldap_url != NULL)
848 ldap_free_urldesc(ldap_url);
850 /* cleanup */
851 if (dbi->zone != NULL)
852 isc_mem_free(ns_g_mctx, dbi->zone);
853 if (dbi->record != NULL)
854 isc_mem_free(ns_g_mctx, dbi->record);
855 if (dbi->client != NULL)
856 isc_mem_free(ns_g_mctx, dbi->client);
858 #ifdef ISC_PLATFORM_USETHREADS
860 /* release the lock so another thread can use this dbi */
861 isc_mutex_unlock(&dbi->instance_lock);
863 #endif /* ISC_PLATFORM_USETHREADS */
865 /* release query string */
866 if (querystring != NULL)
867 isc_mem_free(ns_g_mctx, querystring );
869 /* return result */
870 return (result);
874 * DLZ methods
876 static isc_result_t
877 dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name,
878 const char *client)
880 isc_result_t result;
882 UNUSED(driverarg);
884 /* check to see if we are authoritative for the zone first */
885 result = dlz_ldap_findzone(driverarg, dbdata, name, NULL, NULL);
886 if (result != ISC_R_SUCCESS) {
887 return (result);
890 /* get all the zone data */
891 result = ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL);
892 return (result);
895 static isc_result_t
896 dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata,
897 dns_sdlzallnodes_t *allnodes)
899 UNUSED(driverarg);
900 return (ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes));
903 static isc_result_t
904 dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata,
905 dns_sdlzlookup_t *lookup)
907 UNUSED(driverarg);
908 return (ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup));
911 static isc_result_t
912 dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name,
913 dns_clientinfomethods_t *methods,
914 dns_clientinfo_t *clientinfo)
916 UNUSED(driverarg);
917 UNUSED(methods);
918 UNUSED(clientinfo);
919 return (ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL));
922 static isc_result_t
923 dlz_ldap_lookup(const char *zone, const char *name, void *driverarg,
924 void *dbdata, dns_sdlzlookup_t *lookup,
925 dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
927 isc_result_t result;
929 UNUSED(driverarg);
930 UNUSED(methods);
931 UNUSED(clientinfo);
933 if (strcmp(name, "*") == 0)
934 result = ldap_get_results(zone, "~", NULL, LOOKUP,
935 dbdata, lookup);
936 else
937 result = ldap_get_results(zone, name, NULL, LOOKUP,
938 dbdata, lookup);
939 return (result);
943 static isc_result_t
944 dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[],
945 void *driverarg, void **dbdata)
947 isc_result_t result;
948 ldap_instance_t *ldap_inst = NULL;
949 dbinstance_t *dbi = NULL;
950 int protocol;
951 int method;
953 #ifdef ISC_PLATFORM_USETHREADS
954 /* if multi-threaded, we need a few extra variables. */
955 int dbcount;
956 char *endp;
957 /* db_list_t *dblist = NULL; */
958 int i;
960 #endif /* ISC_PLATFORM_USETHREADS */
962 UNUSED(dlzname);
963 UNUSED(driverarg);
965 #ifdef ISC_PLATFORM_USETHREADS
966 /* if debugging, let user know we are multithreaded. */
967 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
968 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
969 "LDAP driver running multithreaded");
970 #else /* ISC_PLATFORM_USETHREADS */
971 /* if debugging, let user know we are single threaded. */
972 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
973 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
974 "LDAP driver running single threaded");
975 #endif /* ISC_PLATFORM_USETHREADS */
977 if (argc < 9) {
978 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
979 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
980 "LDAP driver requires at least "
981 "8 command line args.");
982 return (ISC_R_FAILURE);
985 /* no more than 13 arg's should be passed to the driver */
986 if (argc > 12) {
987 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
988 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
989 "LDAP driver cannot accept more than "
990 "11 command line args.");
991 return (ISC_R_FAILURE);
994 /* determine protocol version. */
995 if (strncasecmp(argv[2], V2, strlen(V2)) == 0) {
996 protocol = 2;
997 } else if (strncasecmp(argv[2], V3, strlen(V3)) == 0) {
998 protocol = 3;
999 } else {
1000 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1001 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1002 "LDAP driver protocol must be either %s or %s",
1003 V2, V3);
1004 return (ISC_R_FAILURE);
1007 /* determine connection method. */
1008 if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0) {
1009 method = LDAP_AUTH_SIMPLE;
1010 } else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0) {
1011 method = LDAP_AUTH_KRBV41;
1012 } else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0) {
1013 method = LDAP_AUTH_KRBV42;
1014 } else {
1015 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1016 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1017 "LDAP driver authentication method must be "
1018 "one of %s, %s or %s",
1019 SIMPLE, KRB41, KRB42);
1020 return (ISC_R_FAILURE);
1023 /* multithreaded build can have multiple DB connections */
1024 #ifdef ISC_PLATFORM_USETHREADS
1026 /* check how many db connections we should create */
1027 dbcount = strtol(argv[1], &endp, 10);
1028 if (*endp != '\0' || dbcount < 0) {
1029 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1030 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1031 "LDAP driver database connection count "
1032 "must be positive.");
1033 return (ISC_R_FAILURE);
1035 #endif
1037 /* check that LDAP URL parameters make sense */
1038 switch(argc) {
1039 case 12:
1040 result = dlz_ldap_checkURL(argv[11], 0, "allow zone transfer");
1041 if (result != ISC_R_SUCCESS)
1042 return (result);
1043 case 11:
1044 result = dlz_ldap_checkURL(argv[10], 3, "all nodes");
1045 if (result != ISC_R_SUCCESS)
1046 return (result);
1047 case 10:
1048 if (strlen(argv[9]) > 0) {
1049 result = dlz_ldap_checkURL(argv[9], 3, "authority");
1050 if (result != ISC_R_SUCCESS)
1051 return (result);
1053 case 9:
1054 result = dlz_ldap_checkURL(argv[8], 3, "lookup");
1055 if (result != ISC_R_SUCCESS)
1056 return (result);
1057 result = dlz_ldap_checkURL(argv[7], 0, "find zone");
1058 if (result != ISC_R_SUCCESS)
1059 return (result);
1060 break;
1061 default:
1062 /* not really needed, should shut up compiler. */
1063 result = ISC_R_FAILURE;
1066 /* allocate memory for LDAP instance */
1067 ldap_inst = isc_mem_get(ns_g_mctx, sizeof(ldap_instance_t));
1068 if (ldap_inst == NULL)
1069 return (ISC_R_NOMEMORY);
1070 memset(ldap_inst, 0, sizeof(ldap_instance_t));
1072 /* store info needed to automatically re-connect. */
1073 ldap_inst->protocol = protocol;
1074 ldap_inst->method = method;
1075 ldap_inst->hosts = isc_mem_strdup(ns_g_mctx, argv[6]);
1076 if (ldap_inst->hosts == NULL) {
1077 result = ISC_R_NOMEMORY;
1078 goto cleanup;
1080 ldap_inst->user = isc_mem_strdup(ns_g_mctx, argv[4]);
1081 if (ldap_inst->user == NULL) {
1082 result = ISC_R_NOMEMORY;
1083 goto cleanup;
1085 ldap_inst->cred = isc_mem_strdup(ns_g_mctx, argv[5]);
1086 if (ldap_inst->cred == NULL) {
1087 result = ISC_R_NOMEMORY;
1088 goto cleanup;
1091 #ifdef ISC_PLATFORM_USETHREADS
1092 /* allocate memory for database connection list */
1093 ldap_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1094 if (ldap_inst->db == NULL) {
1095 result = ISC_R_NOMEMORY;
1096 goto cleanup;
1099 /* initialize DB connection list */
1100 ISC_LIST_INIT(*(ldap_inst->db));
1103 * create the appropriate number of database instances (DBI)
1104 * append each new DBI to the end of the list
1106 for (i = 0; i < dbcount; i++) {
1108 #endif /* ISC_PLATFORM_USETHREADS */
1110 /* how many queries were passed in from config file? */
1111 switch(argc) {
1112 case 9:
1113 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1114 NULL, argv[7], argv[8],
1115 NULL, &dbi);
1116 break;
1117 case 10:
1118 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1119 argv[9], argv[7], argv[8],
1120 NULL, &dbi);
1121 break;
1122 case 11:
1123 result = build_sqldbinstance(ns_g_mctx, argv[10], NULL,
1124 argv[9], argv[7], argv[8],
1125 NULL, &dbi);
1126 break;
1127 case 12:
1128 result = build_sqldbinstance(ns_g_mctx, argv[10],
1129 argv[11], argv[9],
1130 argv[7], argv[8],
1131 NULL, &dbi);
1132 break;
1133 default:
1134 /* not really needed, should shut up compiler. */
1135 result = ISC_R_FAILURE;
1138 if (result == ISC_R_SUCCESS) {
1139 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1140 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1141 "LDAP driver created "
1142 "database instance object.");
1143 } else { /* unsuccessful?, log err msg and cleanup. */
1144 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1145 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1146 "LDAP driver could not create "
1147 "database instance object.");
1148 goto cleanup;
1151 #ifdef ISC_PLATFORM_USETHREADS
1152 /* when multithreaded, build a list of DBI's */
1153 ISC_LINK_INIT(dbi, link);
1154 ISC_LIST_APPEND(*(ldap_inst->db), dbi, link);
1155 #else
1157 * when single threaded, hold onto the one connection
1158 * instance.
1160 ldap_inst->db = dbi;
1162 #endif
1163 /* attempt to connect */
1164 result = dlz_ldap_connect(ldap_inst, dbi);
1167 * if db connection cannot be created, log err msg and
1168 * cleanup.
1170 switch(result) {
1171 /* success, do nothing */
1172 case ISC_R_SUCCESS:
1173 break;
1175 * no memory means ldap_init could not
1176 * allocate memory
1178 case ISC_R_NOMEMORY:
1179 #ifdef ISC_PLATFORM_USETHREADS
1180 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1181 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1182 "LDAP driver could not allocate memory "
1183 "for connection number %u",
1184 i+1);
1185 #else
1186 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1187 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1188 "LDAP driver could not allocate memory "
1189 "for connection");
1190 #endif
1191 goto cleanup;
1192 break;
1194 * no perm means ldap_set_option could not set
1195 * protocol version
1197 case ISC_R_NOPERM:
1198 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1199 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1200 "LDAP driver could not "
1201 "set protocol version.");
1202 result = ISC_R_FAILURE;
1203 goto cleanup;
1204 break;
1205 /* failure means couldn't connect to ldap server */
1206 case ISC_R_FAILURE:
1207 #ifdef ISC_PLATFORM_USETHREADS
1208 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1209 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1210 "LDAP driver could not "
1211 "bind connection number %u to server.",
1212 i+1);
1213 #else
1214 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1215 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1216 "LDAP driver could not "
1217 "bind connection to server.");
1218 #endif
1219 goto cleanup;
1220 break;
1222 * default should never happen. If it does,
1223 * major errors.
1225 default:
1226 UNEXPECTED_ERROR(__FILE__, __LINE__,
1227 "dlz_ldap_create() failed: %s",
1228 isc_result_totext(result));
1229 result = ISC_R_UNEXPECTED;
1230 goto cleanup;
1231 break;
1232 } /* end switch(result) */
1235 #ifdef ISC_PLATFORM_USETHREADS
1237 /* set DBI = null for next loop through. */
1238 dbi = NULL;
1239 } /* end for loop */
1241 #endif /* ISC_PLATFORM_USETHREADS */
1244 /* set dbdata to the ldap_instance we created. */
1245 *dbdata = ldap_inst;
1247 /* hey, we got through all of that ok, return success. */
1248 return(ISC_R_SUCCESS);
1250 cleanup:
1251 dlz_ldap_destroy(NULL, ldap_inst);
1253 return(ISC_R_FAILURE);
1256 void
1257 dlz_ldap_destroy(void *driverarg, void *dbdata) {
1258 UNUSED(driverarg);
1260 if (dbdata != NULL) {
1261 #ifdef ISC_PLATFORM_USETHREADS
1262 /* cleanup the list of DBI's */
1263 ldap_destroy_dblist((db_list_t *)
1264 ((ldap_instance_t *)dbdata)->db);
1266 #else /* ISC_PLATFORM_USETHREADS */
1267 if (((ldap_instance_t *)dbdata)->db->dbconn != NULL)
1268 ldap_unbind_s((LDAP *)
1269 ((ldap_instance_t *)dbdata)->db->dbconn);
1271 /* destroy single DB instance */
1272 destroy_sqldbinstance(((ldap_instance_t *)dbdata)->db);
1273 #endif /* ISC_PLATFORM_USETHREADS */
1275 if (((ldap_instance_t *)dbdata)->hosts != NULL)
1276 isc_mem_free(ns_g_mctx,
1277 ((ldap_instance_t *)dbdata)->hosts);
1279 if (((ldap_instance_t *)dbdata)->user != NULL)
1280 isc_mem_free(ns_g_mctx,
1281 ((ldap_instance_t *)dbdata)->user);
1283 if (((ldap_instance_t *)dbdata)->cred != NULL)
1284 isc_mem_free(ns_g_mctx,
1285 ((ldap_instance_t *)dbdata)->cred);
1287 isc_mem_put(ns_g_mctx, dbdata, sizeof(ldap_instance_t));
1291 static dns_sdlzmethods_t dlz_ldap_methods = {
1292 dlz_ldap_create,
1293 dlz_ldap_destroy,
1294 dlz_ldap_findzone,
1295 dlz_ldap_lookup,
1296 dlz_ldap_authority,
1297 dlz_ldap_allnodes,
1298 dlz_ldap_allowzonexfr,
1299 NULL,
1300 NULL,
1301 NULL,
1302 NULL,
1303 NULL,
1304 NULL,
1305 NULL,
1309 * Wrapper around dns_sdlzregister().
1311 isc_result_t
1312 dlz_ldap_init(void) {
1313 isc_result_t result;
1316 * Write debugging message to log
1318 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1319 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1320 "Registering DLZ ldap driver.");
1322 result = dns_sdlzregister("ldap", &dlz_ldap_methods, NULL,
1323 DNS_SDLZFLAG_RELATIVEOWNER |
1324 DNS_SDLZFLAG_RELATIVERDATA,
1325 ns_g_mctx, &dlz_ldap);
1326 if (result != ISC_R_SUCCESS) {
1327 UNEXPECTED_ERROR(__FILE__, __LINE__,
1328 "dns_sdlzregister() failed: %s",
1329 isc_result_totext(result));
1330 result = ISC_R_UNEXPECTED;
1333 return (result);
1337 * Wrapper around dns_sdlzunregister().
1339 void
1340 dlz_ldap_clear(void) {
1342 * Write debugging message to log
1344 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1345 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1346 "Unregistering DLZ ldap driver.");
1348 if (dlz_ldap != NULL)
1349 dns_sdlzunregister(&dlz_ldap);
1352 #endif