Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / contrib / dlz / modules / ldap / dlz_ldap_dynamic.c
blob4a80db8bb605f7a81435e4b21ca97373c7ab833e
1 /* $NetBSD: dlz_ldap_dynamic.c,v 1.1.1.3 2014/12/10 03:34:31 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) 2013 Internet Systems Consortium, Inc. ("ISC")
40 * Copyright (C) 1999-2001 Internet Software Consortium.
42 * Permission to use, copy, modify, and/or distribute this software for any
43 * purpose with or without fee is hereby granted, provided that the above
44 * copyright notice and this permission notice appear in all copies.
46 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
47 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
48 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
49 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
50 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
51 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
52 * PERFORMANCE OF THIS SOFTWARE.
56 * This provides the externally loadable ldap DLZ module, without
57 * update support
60 #include <stdio.h>
61 #include <string.h>
62 #include <stdarg.h>
63 #include <stdint.h>
64 #include <stdlib.h>
66 #include <dlz_minimal.h>
67 #include <dlz_list.h>
68 #include <dlz_dbi.h>
69 #include <dlz_pthread.h>
72 * Need older API functions from ldap.h.
74 #define LDAP_DEPRECATED 1
76 #include <ldap.h>
78 #define SIMPLE "simple"
79 #define KRB41 "krb41"
80 #define KRB42 "krb42"
81 #define V2 "v2"
82 #define V3 "v3"
84 #define dbc_search_limit 30
85 #define ALLNODES 1
86 #define ALLOWXFR 2
87 #define AUTHORITY 3
88 #define FINDZONE 4
89 #define LOOKUP 5
91 /*%
92 * Structure to hold everthing needed by this "instance" of the LDAP
93 * driver remember, the driver code is only loaded once, but may have
94 * many separate instances.
96 typedef struct {
97 #if PTHREADS
98 db_list_t *db; /*%< handle to a list of DB */
99 #else
100 dbinstance_t *db; /*%< handle to db */
101 #endif
102 int method; /*%< security authentication method */
103 char *user; /*%< who is authenticating */
104 char *cred; /*%< password for simple authentication method */
105 int protocol; /*%< LDAP communication protocol version */
106 char *hosts; /*%< LDAP server hosts */
108 /* Helper functions from the dlz_dlopen driver */
109 log_t *log;
110 dns_sdlz_putrr_t *putrr;
111 dns_sdlz_putnamedrr_t *putnamedrr;
112 dns_dlz_writeablezone_t *writeable_zone;
113 } ldap_instance_t;
115 /* forward references */
117 #if DLZ_DLOPEN_VERSION < 3
118 isc_result_t
119 dlz_findzonedb(void *dbdata, const char *name);
120 #else
121 isc_result_t
122 dlz_findzonedb(void *dbdata, const char *name,
123 dns_clientinfomethods_t *methods,
124 dns_clientinfo_t *clientinfo);
125 #endif
127 void
128 dlz_destroy(void *dbdata);
130 static void
131 b9_add_helper(ldap_instance_t *db, const char *helper_name, void *ptr);
134 * Private methods
137 /*% checks that the LDAP URL parameters make sense */
138 static isc_result_t
139 ldap_checkURL(ldap_instance_t *db, char *URL, int attrCnt, const char *msg) {
140 isc_result_t result = ISC_R_SUCCESS;
141 int ldap_result;
142 LDAPURLDesc *ldap_url = NULL;
144 if (!ldap_is_ldap_url(URL)) {
145 db->log(ISC_LOG_ERROR,
146 "%s query is not a valid LDAP URL", msg);
147 result = ISC_R_FAILURE;
148 goto cleanup;
151 ldap_result = ldap_url_parse(URL, &ldap_url);
152 if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
153 db->log(ISC_LOG_ERROR, "parsing %s query failed", msg);
154 result = ISC_R_FAILURE;
155 goto cleanup;
158 if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) {
159 db->log(ISC_LOG_ERROR,
160 "%s query must specify at least "
161 "%d attributes to return", msg, attrCnt);
162 result = ISC_R_FAILURE;
163 goto cleanup;
166 if (ldap_url->lud_host != NULL) {
167 db->log(ISC_LOG_ERROR,
168 "%s query must not specify a host", msg);
169 result = ISC_R_FAILURE;
170 goto cleanup;
173 if (ldap_url->lud_port != 389) {
174 db->log(ISC_LOG_ERROR,
175 "%s query must not specify a port", msg);
176 result = ISC_R_FAILURE;
177 goto cleanup;
180 if (ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1) {
181 db->log(ISC_LOG_ERROR,
182 "%s query must specify a search base", msg);
183 result = ISC_R_FAILURE;
184 goto cleanup;
187 if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) {
188 db->log(ISC_LOG_ERROR,
189 "%s uses extensions. "
190 "The driver does not support LDAP extensions.", msg);
191 result = ISC_R_FAILURE;
192 goto cleanup;
195 cleanup:
196 if (ldap_url != NULL)
197 ldap_free_urldesc(ldap_url);
199 return (result);
202 /*% Connects / reconnects to LDAP server */
203 static isc_result_t
204 ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) {
205 isc_result_t result;
206 int ldap_result;
208 /* if we have a connection, get ride of it. */
209 if (dbc->dbconn != NULL) {
210 ldap_unbind_s((LDAP *) dbc->dbconn);
211 dbc->dbconn = NULL;
214 /* now connect / reconnect. */
216 /* initialize. */
217 dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT);
218 if (dbc->dbconn == NULL)
219 return (ISC_R_NOMEMORY);
221 /* set protocol version. */
222 ldap_result = ldap_set_option((LDAP *) dbc->dbconn,
223 LDAP_OPT_PROTOCOL_VERSION,
224 &(dbi->protocol));
225 if (ldap_result != LDAP_SUCCESS) {
226 result = ISC_R_NOPERM;
227 goto cleanup;
230 /* "bind" to server. i.e. send username / pass */
231 ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user,
232 dbi->cred, dbi->method);
233 if (ldap_result != LDAP_SUCCESS) {
234 result = ISC_R_FAILURE;
235 goto cleanup;
238 return (ISC_R_SUCCESS);
240 cleanup:
242 /* cleanup if failure. */
243 if (dbc->dbconn != NULL) {
244 ldap_unbind_s((LDAP *) dbc->dbconn);
245 dbc->dbconn = NULL;
248 return (result);
251 #if PTHREADS
253 * Properly cleans up a list of database instances.
254 * This function is only used when the driver is compiled for
255 * multithreaded operation.
257 static void
258 ldap_destroy_dblist(db_list_t *dblist) {
259 dbinstance_t *ndbi = NULL;
260 dbinstance_t *dbi = NULL;
262 /* get the first DBI in the list */
263 ndbi = DLZ_LIST_HEAD(*dblist);
265 /* loop through the list */
266 while (ndbi != NULL) {
267 dbi = ndbi;
268 /* get the next DBI in the list */
269 ndbi = DLZ_LIST_NEXT(dbi, link);
270 /* release DB connection */
271 if (dbi->dbconn != NULL)
272 ldap_unbind_s((LDAP *) dbi->dbconn);
273 /* release all memory that comprised a DBI */
274 destroy_dbinstance(dbi);
276 /* release memory for the list structure */
277 free(dblist);
281 * Loops through the list of DB instances, attempting to lock
282 * on the mutex. If successful, the DBI is reserved for use
283 * and the thread can perform queries against the database.
284 * If the lock fails, the next one in the list is tried.
285 * looping continues until a lock is obtained, or until
286 * the list has been searched dbc_search_limit times.
287 * This function is only used when the driver is compiled for
288 * multithreaded operation.
290 static dbinstance_t *
291 ldap_find_avail_conn(ldap_instance_t *ldap) {
292 dbinstance_t *dbi = NULL;
293 dbinstance_t *head;
294 int count = 0;
296 /* get top of list */
297 head = dbi = DLZ_LIST_HEAD(*ldap->db);
299 /* loop through list */
300 while (count < dbc_search_limit) {
301 /* try to lock on the mutex */
302 if (dlz_mutex_trylock(&dbi->lock) == 0)
303 return (dbi); /* success, return the DBI for use. */
305 /* not successful, keep trying */
306 dbi = DLZ_LIST_NEXT(dbi, link);
308 /* check to see if we have gone to the top of the list. */
309 if (dbi == NULL) {
310 count++;
311 dbi = head;
315 ldap->log(ISC_LOG_INFO,
316 "LDAP driver unable to find available connection "
317 "after searching %d times", count);
318 return (NULL);
320 #endif /* PTHREADS */
322 static isc_result_t
323 ldap_process_results(ldap_instance_t *db, LDAP *dbc, LDAPMessage *msg,
324 char **attrs, void *ptr, isc_boolean_t allnodes)
326 isc_result_t result = ISC_R_SUCCESS;
327 int i = 0;
328 int j;
329 int len;
330 char *attribute = NULL;
331 LDAPMessage *entry;
332 char *endp = NULL;
333 char *host = NULL;
334 char *type = NULL;
335 char *data = NULL;
336 char **vals = NULL;
337 int ttl;
339 /* get the first entry to process */
340 entry = ldap_first_entry(dbc, msg);
341 if (entry == NULL) {
342 db->log(ISC_LOG_INFO, "LDAP no entries to process.");
343 return (ISC_R_FAILURE);
346 /* loop through all entries returned */
347 while (entry != NULL) {
348 /* reset for this loop */
349 ttl = 0;
350 len = 0;
351 i = 0;
352 attribute = attrs[i];
354 /* determine how much space we need for data string */
355 for (j = 0; attrs[j] != NULL; j++) {
356 /* get the list of values for this attribute. */
357 vals = ldap_get_values(dbc, entry, attrs[j]);
358 /* skip empty attributes. */
359 if (vals == NULL || ldap_count_values(vals) < 1)
360 continue;
362 * we only use the first value. this driver
363 * does not support multi-valued attributes.
365 len = len + strlen(vals[0]) + 1;
366 /* free vals for next loop */
367 ldap_value_free(vals);
370 /* allocate memory for data string */
371 data = malloc(len + 1);
372 if (data == NULL) {
373 db->log(ISC_LOG_ERROR,
374 "LDAP driver unable to allocate memory "
375 "while processing results");
376 result = ISC_R_FAILURE;
377 goto cleanup;
381 * Make sure data is null termed at the beginning so
382 * we can check if any data was stored to it later.
384 data[0] = '\0';
386 /* reset j to re-use below */
387 j = 0;
389 /* loop through the attributes in the order specified. */
390 while (attribute != NULL) {
391 /* get the list of values for this attribute. */
392 vals = ldap_get_values(dbc, entry, attribute);
394 /* skip empty attributes. */
395 if (vals == NULL || vals[0] == NULL) {
396 /* increment attibute pointer */
397 attribute = attrs[++i];
398 /* start loop over */
399 continue;
403 * j initially = 0. Increment j each time we
404 * set a field that way next loop will set
405 * next field.
407 switch (j) {
408 case 0:
409 j++;
411 * convert text to int, make sure it
412 * worked right
414 ttl = strtol(vals[0], &endp, 10);
415 if (*endp != '\0' || ttl < 0) {
416 db->log(ISC_LOG_ERROR,
417 "LDAP driver ttl must "
418 "be a postive number");
419 goto cleanup;
421 break;
422 case 1:
423 j++;
424 type = strdup(vals[0]);
425 break;
426 case 2:
427 j++;
428 if (allnodes)
429 host = strdup(vals[0]);
430 else
431 strcpy(data, vals[0]);
432 break;
433 case 3:
434 j++;
435 if (allnodes)
436 strcpy(data, vals[0]);
437 else {
438 strcat(data, " ");
439 strcat(data, vals[0]);
441 break;
442 default:
443 strcat(data, " ");
444 strcat(data, vals[0]);
445 break;
448 /* free values */
449 ldap_value_free(vals);
450 vals = NULL;
452 /* increment attibute pointer */
453 attribute = attrs[++i];
456 if (type == NULL) {
457 db->log(ISC_LOG_ERROR,
458 "LDAP driver unable to retrieve DNS type");
459 result = ISC_R_FAILURE;
460 goto cleanup;
463 if (strlen(data) < 1) {
464 db->log(ISC_LOG_ERROR,
465 "LDAP driver unable to retrieve DNS data");
466 result = ISC_R_FAILURE;
467 goto cleanup;
470 if (allnodes && host != NULL) {
471 dns_sdlzallnodes_t *an = (dns_sdlzallnodes_t *) ptr;
472 if (strcasecmp(host, "~") == 0)
473 result = db->putnamedrr(an, "*", type,
474 ttl, data);
475 else
476 result = db->putnamedrr(an, host, type,
477 ttl, data);
478 if (result != ISC_R_SUCCESS)
479 db->log(ISC_LOG_ERROR,
480 "ldap_dynamic: putnamedrr failed "
481 "for \"%s %s %u %s\" (%d)",
482 host, type, ttl, data, result);
483 } else {
484 dns_sdlzlookup_t *lookup = (dns_sdlzlookup_t *) ptr;
485 result = db->putrr(lookup, type, ttl, data);
486 if (result != ISC_R_SUCCESS)
487 db->log(ISC_LOG_ERROR,
488 "ldap_dynamic: putrr failed "
489 "for \"%s %u %s\" (%s)",
490 type, ttl, data, result);
493 if (result != ISC_R_SUCCESS) {
494 db->log(ISC_LOG_ERROR,
495 "LDAP driver failed "
496 "while sending data to BIND.");
497 goto cleanup;
500 /* free memory for type, data and host for next loop */
501 free(type);
502 type = NULL;
504 free(data);
505 data = NULL;
507 if (host != NULL) {
508 free(host);
509 host = NULL;
512 /* get the next entry to process */
513 entry = ldap_next_entry(dbc, entry);
516 cleanup:
517 /* de-allocate memory */
518 if (vals != NULL)
519 ldap_value_free(vals);
520 if (host != NULL)
521 free(host);
522 if (type != NULL)
523 free(type);
524 if (data != NULL)
525 free(data);
527 return (result);
531 * This function is the real core of the driver. Zone, record
532 * and client strings are passed in (or NULL is passed if the
533 * string is not available). The type of query we want to run
534 * is indicated by the query flag, and the dbdata object is passed
535 * passed in to. dbdata really holds either:
536 * 1) a list of database instances (in multithreaded mode) OR
537 * 2) a single database instance (in single threaded mode)
538 * The function will construct the query and obtain an available
539 * database instance (DBI). It will then run the query and hopefully
540 * obtain a result set.
542 static isc_result_t
543 ldap_get_results(const char *zone, const char *record,
544 const char *client, unsigned int query,
545 void *dbdata, void *ptr)
547 isc_result_t result;
548 ldap_instance_t *db = (ldap_instance_t *)dbdata;
549 dbinstance_t *dbi = NULL;
550 char *querystring = NULL;
551 LDAPURLDesc *ldap_url = NULL;
552 int ldap_result = 0;
553 LDAPMessage *ldap_msg = NULL;
554 int i;
555 int entries;
557 /* get db instance / connection */
558 #if PTHREADS
559 /* find an available DBI from the list */
560 dbi = ldap_find_avail_conn(db);
561 #else /* PTHREADS */
563 * only 1 DBI - no need to lock instance lock either
564 * only 1 thread in the whole process, no possible contention.
566 dbi = (dbinstance_t *)(db->db);
567 #endif /* PTHREADS */
569 /* if DBI is null, can't do anything else */
570 if (dbi == NULL)
571 return (ISC_R_FAILURE);
573 /* set fields */
574 if (zone != NULL) {
575 dbi->zone = strdup(zone);
576 if (dbi->zone == NULL) {
577 result = ISC_R_NOMEMORY;
578 goto cleanup;
580 } else
581 dbi->zone = NULL;
583 if (record != NULL) {
584 dbi->record = strdup(record);
585 if (dbi->record == NULL) {
586 result = ISC_R_NOMEMORY;
587 goto cleanup;
589 } else
590 dbi->record = NULL;
592 if (client != NULL) {
593 dbi->client = strdup(client);
594 if (dbi->client == NULL) {
595 result = ISC_R_NOMEMORY;
596 goto cleanup;
598 } else
599 dbi->client = NULL;
602 /* what type of query are we going to run? */
603 switch (query) {
604 case ALLNODES:
606 * if the query was not passed in from the config file
607 * then we can't run it. return not_implemented, so
608 * it's like the code for that operation was never
609 * built into the driver.... AHHH flexibility!!!
611 if (dbi->allnodes_q == NULL) {
612 result = ISC_R_NOTIMPLEMENTED;
613 goto cleanup;
614 } else
615 querystring = build_querystring(dbi->allnodes_q);
616 break;
617 case ALLOWXFR:
618 /* same as comments as ALLNODES */
619 if (dbi->allowxfr_q == NULL) {
620 result = ISC_R_NOTIMPLEMENTED;
621 goto cleanup;
622 } else
623 querystring = build_querystring(dbi->allowxfr_q);
624 break;
625 case AUTHORITY:
626 /* same as comments as ALLNODES */
627 if (dbi->authority_q == NULL) {
628 result = ISC_R_NOTIMPLEMENTED;
629 goto cleanup;
630 } else
631 querystring = build_querystring(dbi->authority_q);
632 break;
633 case FINDZONE:
634 /* this is required. It's the whole point of DLZ! */
635 if (dbi->findzone_q == NULL) {
636 db->log(ISC_LOG_DEBUG(2),
637 "No query specified for findzone. "
638 "Findzone requires a query");
639 result = ISC_R_FAILURE;
640 goto cleanup;
641 } else
642 querystring = build_querystring(dbi->findzone_q);
643 break;
644 case LOOKUP:
645 /* this is required. It's also a major point of DLZ! */
646 if (dbi->lookup_q == NULL) {
647 db->log(ISC_LOG_DEBUG(2),
648 "No query specified for lookup. "
649 "Lookup requires a query");
650 result = ISC_R_FAILURE;
651 goto cleanup;
652 } else
653 querystring = build_querystring(dbi->lookup_q);
654 break;
655 default:
657 * this should never happen. If it does, the code is
658 * screwed up!
660 db->log(ISC_LOG_ERROR,
661 "Incorrect query flag passed to ldap_get_results");
662 result = ISC_R_UNEXPECTED;
663 goto cleanup;
666 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
667 if (querystring == NULL) {
668 result = ISC_R_NOMEMORY;
669 goto cleanup;
673 * output the full query string during debug so we can see
674 * what lame error the query has.
676 db->log(ISC_LOG_DEBUG(1), "Query String: %s", querystring);
678 /* break URL down into it's component parts, if error cleanup */
679 ldap_result = ldap_url_parse(querystring, &ldap_url);
680 if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
681 result = ISC_R_FAILURE;
682 goto cleanup;
685 for (i = 0; i < 3; i++) {
687 * dbi->dbconn may be null if trying to reconnect on a
688 * previous query failed.
690 if (dbi->dbconn == NULL) {
691 db->log(ISC_LOG_INFO,
692 "LDAP driver attempting to re-connect");
694 result = ldap_connect((ldap_instance_t *) dbdata, dbi);
695 if (result != ISC_R_SUCCESS) {
696 result = ISC_R_FAILURE;
697 continue;
701 /* perform ldap search syncronously */
702 ldap_result = ldap_search_s((LDAP *) dbi->dbconn,
703 ldap_url->lud_dn,
704 ldap_url->lud_scope,
705 ldap_url->lud_filter,
706 ldap_url->lud_attrs, 0, &ldap_msg);
709 * check return code. No such object is ok, just
710 * didn't find what we wanted
712 switch (ldap_result) {
713 case LDAP_NO_SUCH_OBJECT:
714 db->log(ISC_LOG_DEBUG(1),
715 "No object found matching query requirements");
716 result = ISC_R_NOTFOUND;
717 goto cleanup;
718 break;
719 case LDAP_SUCCESS: /* on success do nothing */
720 result = ISC_R_SUCCESS;
721 i = 3;
722 break;
723 case LDAP_SERVER_DOWN:
724 db->log(ISC_LOG_INFO,
725 "LDAP driver attempting to re-connect");
726 result = ldap_connect((ldap_instance_t *) dbdata, dbi);
727 if (result != ISC_R_SUCCESS)
728 result = ISC_R_FAILURE;
729 break;
730 default:
732 * other errors not ok. Log error message and
733 * get out
735 db->log(ISC_LOG_ERROR, "LDAP error: %s",
736 ldap_err2string(ldap_result));
737 result = ISC_R_FAILURE;
738 goto cleanup;
739 break;
743 if (result != ISC_R_SUCCESS)
744 goto cleanup;
746 switch (query) {
747 case ALLNODES:
748 result = ldap_process_results(db, (LDAP *) dbi->dbconn,
749 ldap_msg, ldap_url->lud_attrs,
750 ptr, ISC_TRUE);
751 break;
752 case AUTHORITY:
753 case LOOKUP:
754 result = ldap_process_results(db, (LDAP *) dbi->dbconn,
755 ldap_msg, ldap_url->lud_attrs,
756 ptr, ISC_FALSE);
757 break;
758 case ALLOWXFR:
759 entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
760 if (entries == 0)
761 result = ISC_R_NOPERM;
762 else if (entries > 0)
763 result = ISC_R_SUCCESS;
764 else
765 result = ISC_R_FAILURE;
766 break;
767 case FINDZONE:
768 entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
769 if (entries == 0)
770 result = ISC_R_NOTFOUND;
771 else if (entries > 0)
772 result = ISC_R_SUCCESS;
773 else
774 result = ISC_R_FAILURE;
775 break;
776 default:
778 * this should never happen. If it does, the code is
779 * screwed up!
781 db->log(ISC_LOG_ERROR,
782 "Incorrect query flag passed to ldap_get_results");
783 result = ISC_R_UNEXPECTED;
786 cleanup:
787 /* it's always good to cleanup after yourself */
789 /* if we retrieved results, free them */
790 if (ldap_msg != NULL)
791 ldap_msgfree(ldap_msg);
793 if (ldap_url != NULL)
794 ldap_free_urldesc(ldap_url);
796 /* cleanup */
797 if (dbi->zone != NULL)
798 free(dbi->zone);
799 if (dbi->record != NULL)
800 free(dbi->record);
801 if (dbi->client != NULL)
802 free(dbi->client);
803 dbi->zone = dbi->record = dbi->client = NULL;
805 /* release the lock so another thread can use this dbi */
806 (void) dlz_mutex_unlock(&dbi->lock);
808 /* release query string */
809 if (querystring != NULL)
810 free(querystring);
812 /* return result */
813 return (result);
817 * DLZ methods
819 isc_result_t
820 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
821 isc_result_t result;
823 /* check to see if we are authoritative for the zone first */
824 #if DLZ_DLOPEN_VERSION < 3
825 result = dlz_findzonedb(dbdata, name);
826 #else
827 result = dlz_findzonedb(dbdata, name, NULL, NULL);
828 #endif
829 if (result != ISC_R_SUCCESS) {
830 return (result);
833 /* get all the zone data */
834 result = ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL);
835 return (result);
838 isc_result_t
839 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes)
841 return (ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes));
844 isc_result_t
845 dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
846 return (ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup));
849 #if DLZ_DLOPEN_VERSION < 3
850 isc_result_t
851 dlz_findzonedb(void *dbdata, const char *name)
852 #else
853 isc_result_t
854 dlz_findzonedb(void *dbdata, const char *name,
855 dns_clientinfomethods_t *methods,
856 dns_clientinfo_t *clientinfo)
857 #endif
859 #if DLZ_DLOPEN_VERSION >= 3
860 UNUSED(methods);
861 UNUSED(clientinfo);
862 #endif
863 return (ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL));
866 #if DLZ_DLOPEN_VERSION == 1
867 isc_result_t dlz_lookup(const char *zone, const char *name,
868 void *dbdata, dns_sdlzlookup_t *lookup)
869 #else
870 isc_result_t dlz_lookup(const char *zone, const char *name,
871 void *dbdata, dns_sdlzlookup_t *lookup,
872 dns_clientinfomethods_t *methods,
873 dns_clientinfo_t *clientinfo)
874 #endif
876 isc_result_t result;
878 #if DLZ_DLOPEN_VERSION >= 2
879 UNUSED(methods);
880 UNUSED(clientinfo);
881 #endif
883 if (strcmp(name, "*") == 0)
884 result = ldap_get_results(zone, "~", NULL, LOOKUP,
885 dbdata, lookup);
886 else
887 result = ldap_get_results(zone, name, NULL, LOOKUP,
888 dbdata, lookup);
889 return (result);
893 isc_result_t
894 dlz_create(const char *dlzname, unsigned int argc, char *argv[],
895 void **dbdata, ...)
897 isc_result_t result = ISC_R_FAILURE;
898 ldap_instance_t *ldap = NULL;
899 dbinstance_t *dbi = NULL;
900 const char *helper_name;
901 int protocol;
902 int method;
903 #if PTHREADS
904 int dbcount;
905 char *endp;
906 int i;
907 #endif /* PTHREADS */
908 va_list ap;
910 UNUSED(dlzname);
912 /* allocate memory for LDAP instance */
913 ldap = calloc(1, sizeof(ldap_instance_t));
914 if (ldap == NULL)
915 return (ISC_R_NOMEMORY);
916 memset(ldap, 0, sizeof(ldap_instance_t));
918 /* Fill in the helper functions */
919 va_start(ap, dbdata);
920 while ((helper_name = va_arg(ap, const char*)) != NULL)
921 b9_add_helper(ldap, helper_name, va_arg(ap, void*));
922 va_end(ap);
924 #if PTHREADS
925 /* if debugging, let user know we are multithreaded. */
926 ldap->log(ISC_LOG_DEBUG(1), "LDAP driver running multithreaded");
927 #else /* PTHREADS */
928 /* if debugging, let user know we are single threaded. */
929 ldap->log(ISC_LOG_DEBUG(1), "LDAP driver running single threaded");
930 #endif /* PTHREADS */
932 if (argc < 9) {
933 ldap->log(ISC_LOG_ERROR,
934 "LDAP driver requires at least "
935 "8 command line args.");
936 goto cleanup;
939 /* no more than 13 arg's should be passed to the driver */
940 if (argc > 12) {
941 ldap->log(ISC_LOG_ERROR,
942 "LDAP driver cannot accept more than "
943 "11 command line args.");
944 goto cleanup;
947 /* determine protocol version. */
948 if (strncasecmp(argv[2], V2, strlen(V2)) == 0)
949 protocol = 2;
950 else if (strncasecmp(argv[2], V3, strlen(V3)) == 0)
951 protocol = 3;
952 else {
953 ldap->log(ISC_LOG_ERROR,
954 "LDAP driver protocol must be either %s or %s",
955 V2, V3);
956 goto cleanup;
959 /* determine connection method. */
960 if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0)
961 method = LDAP_AUTH_SIMPLE;
962 else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0)
963 method = LDAP_AUTH_KRBV41;
964 else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0)
965 method = LDAP_AUTH_KRBV42;
966 else {
967 ldap->log(ISC_LOG_ERROR,
968 "LDAP driver authentication method must be "
969 "one of %s, %s or %s", SIMPLE, KRB41, KRB42);
970 goto cleanup;
973 /* multithreaded build can have multiple DB connections */
974 #if PTHREADS
975 /* check how many db connections we should create */
976 dbcount = strtol(argv[1], &endp, 10);
977 if (*endp != '\0' || dbcount < 0) {
978 ldap->log(ISC_LOG_ERROR,
979 "LDAP driver database connection count "
980 "must be positive.");
981 goto cleanup;
983 #endif
985 /* check that LDAP URL parameters make sense */
986 switch (argc) {
987 case 12:
988 result = ldap_checkURL(ldap, argv[11], 0,
989 "allow zone transfer");
990 if (result != ISC_R_SUCCESS)
991 goto cleanup;
992 case 11:
993 result = ldap_checkURL(ldap, argv[10], 3, "all nodes");
994 if (result != ISC_R_SUCCESS)
995 goto cleanup;
996 case 10:
997 if (strlen(argv[9]) > 0) {
998 result = ldap_checkURL(ldap, argv[9], 3, "authority");
999 if (result != ISC_R_SUCCESS)
1000 goto cleanup;
1002 case 9:
1003 result = ldap_checkURL(ldap, argv[8], 3, "lookup");
1004 if (result != ISC_R_SUCCESS)
1005 goto cleanup;
1006 result = ldap_checkURL(ldap, argv[7], 0, "find zone");
1007 if (result != ISC_R_SUCCESS)
1008 goto cleanup;
1009 break;
1010 default:
1011 /* not really needed, should shut up compiler. */
1012 result = ISC_R_FAILURE;
1015 /* store info needed to automatically re-connect. */
1016 ldap->protocol = protocol;
1017 ldap->method = method;
1018 ldap->hosts = strdup(argv[6]);
1019 if (ldap->hosts == NULL) {
1020 result = ISC_R_NOMEMORY;
1021 goto cleanup;
1023 ldap->user = strdup(argv[4]);
1024 if (ldap->user == NULL) {
1025 result = ISC_R_NOMEMORY;
1026 goto cleanup;
1028 ldap->cred = strdup(argv[5]);
1029 if (ldap->cred == NULL) {
1030 result = ISC_R_NOMEMORY;
1031 goto cleanup;
1034 #if PTHREADS
1035 /* allocate memory for database connection list */
1036 ldap->db = calloc(1, sizeof(db_list_t));
1037 if (ldap->db == NULL) {
1038 result = ISC_R_NOMEMORY;
1039 goto cleanup;
1042 /* initialize DB connection list */
1043 DLZ_LIST_INIT(*(ldap->db));
1046 * create the appropriate number of database instances (DBI)
1047 * append each new DBI to the end of the list
1049 for (i = 0; i < dbcount; i++) {
1050 #endif /* PTHREADS */
1051 /* how many queries were passed in from config file? */
1052 switch (argc) {
1053 case 9:
1054 result = build_dbinstance(NULL, NULL, NULL, argv[7],
1055 argv[8], NULL, &dbi,
1056 ldap->log);
1057 break;
1058 case 10:
1059 result = build_dbinstance(NULL, NULL, argv[9],
1060 argv[7], argv[8],
1061 NULL, &dbi, ldap->log);
1062 break;
1063 case 11:
1064 result = build_dbinstance(argv[10], NULL, argv[9],
1065 argv[7], argv[8],
1066 NULL, &dbi, ldap->log);
1067 break;
1068 case 12:
1069 result = build_dbinstance(argv[10], argv[11],
1070 argv[9], argv[7],
1071 argv[8], NULL, &dbi,
1072 ldap->log);
1073 break;
1074 default:
1075 /* not really needed, should shut up compiler. */
1076 result = ISC_R_FAILURE;
1079 if (result == ISC_R_SUCCESS) {
1080 ldap->log(ISC_LOG_DEBUG(2),
1081 "LDAP driver created "
1082 "database instance object.");
1083 } else { /* unsuccessful?, log err msg and cleanup. */
1084 ldap->log(ISC_LOG_ERROR,
1085 "LDAP driver could not create "
1086 "database instance object.");
1087 goto cleanup;
1090 #if PTHREADS
1091 /* when multithreaded, build a list of DBI's */
1092 DLZ_LINK_INIT(dbi, link);
1093 DLZ_LIST_APPEND(*(ldap->db), dbi, link);
1094 #else
1096 * when single threaded, hold onto the one connection
1097 * instance.
1099 ldap->db = dbi;
1100 #endif
1101 /* attempt to connect */
1102 result = ldap_connect(ldap, dbi);
1105 * if db connection cannot be created, log err msg and
1106 * cleanup.
1108 switch (result) {
1109 /* success, do nothing */
1110 case ISC_R_SUCCESS:
1111 break;
1113 * no memory means ldap_init could not
1114 * allocate memory
1116 case ISC_R_NOMEMORY:
1117 #if PTHREADS
1118 ldap->log(ISC_LOG_ERROR,
1119 "LDAP driver could not allocate memory "
1120 "for connection number %u", i + 1);
1121 #else
1122 ldap->log(ISC_LOG_ERROR,
1123 "LDAP driver could not allocate memory "
1124 "for connection");
1125 #endif
1126 goto cleanup;
1128 * no perm means ldap_set_option could not set
1129 * protocol version
1131 case ISC_R_NOPERM:
1132 ldap->log(ISC_LOG_ERROR,
1133 "LDAP driver could not "
1134 "set protocol version.");
1135 result = ISC_R_FAILURE;
1136 goto cleanup;
1137 /* failure means couldn't connect to ldap server */
1138 case ISC_R_FAILURE:
1139 #if PTHREADS
1140 ldap->log(ISC_LOG_ERROR,
1141 "LDAP driver could not bind "
1142 "connection number %u to server.", i + 1);
1143 #else
1144 ldap->log(ISC_LOG_ERROR,
1145 "LDAP driver could not "
1146 "bind connection to server.");
1147 #endif
1148 goto cleanup;
1150 * default should never happen. If it does,
1151 * major errors.
1153 default:
1154 ldap->log(ISC_LOG_ERROR,
1155 "dlz_create() failed (%d)", result);
1156 result = ISC_R_UNEXPECTED;
1157 goto cleanup;
1160 #if PTHREADS
1161 /* set DBI = null for next loop through. */
1162 dbi = NULL;
1164 #endif /* PTHREADS */
1166 /* set dbdata to the ldap_instance we created. */
1167 *dbdata = ldap;
1169 return (ISC_R_SUCCESS);
1171 cleanup:
1172 dlz_destroy(ldap);
1174 return (result);
1177 void
1178 dlz_destroy(void *dbdata) {
1179 if (dbdata != NULL) {
1180 ldap_instance_t *db = (ldap_instance_t *)dbdata;
1181 #if PTHREADS
1182 /* cleanup the list of DBI's */
1183 if (db->db != NULL)
1184 ldap_destroy_dblist((db_list_t *)(db->db));
1185 #else /* PTHREADS */
1186 if (db->db->dbconn != NULL)
1187 ldap_unbind_s((LDAP *)(db->db->dbconn));
1189 /* destroy single DB instance */
1190 destroy_dbinstance(db->db);
1191 #endif /* PTHREADS */
1193 if (db->hosts != NULL)
1194 free(db->hosts);
1195 if (db->user != NULL)
1196 free(db->user);
1197 if (db->cred != NULL)
1198 free(db->cred);
1199 free(dbdata);
1204 * Return the version of the API
1207 dlz_version(unsigned int *flags) {
1208 *flags |= DNS_SDLZFLAG_RELATIVERDATA;
1209 #if PTHREADS
1210 *flags |= DNS_SDLZFLAG_THREADSAFE;
1211 #else
1212 *flags &= ~DNS_SDLZFLAG_THREADSAFE;
1213 #endif
1214 return (DLZ_DLOPEN_VERSION);
1218 * Register a helper function from the bind9 dlz_dlopen driver
1220 static void
1221 b9_add_helper(ldap_instance_t *db, const char *helper_name, void *ptr) {
1222 if (strcmp(helper_name, "log") == 0)
1223 db->log = (log_t *)ptr;
1224 if (strcmp(helper_name, "putrr") == 0)
1225 db->putrr = (dns_sdlz_putrr_t *)ptr;
1226 if (strcmp(helper_name, "putnamedrr") == 0)
1227 db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
1228 if (strcmp(helper_name, "writeable_zone") == 0)
1229 db->writeable_zone = (dns_dlz_writeablezone_t *)ptr;