No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / dlz / drivers / dlz_ldap_driver.c
blob402c0f8afdeafce8e73dd6944f351daee7f8eddb
1 /* $NetBSD$ */
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 {
108 #ifdef ISC_PLATFORM_USETHREADS
109 db_list_t *db; /*%< handle to a list of DB */
110 #else
111 dbinstance_t *db; /*%< handle to db */
112 #endif
113 int method; /*%< security authentication method */
114 char *user; /*%< who is authenticating */
115 char *cred; /*%< password for simple authentication method */
116 int protocol; /*%< LDAP communication protocol version */
117 char *hosts; /*%< LDAP server hosts */
119 } ldap_instance_t;
121 /* forward references */
123 static isc_result_t
124 dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name);
126 static void
127 dlz_ldap_destroy(void *driverarg, void *dbdata);
130 * Private methods
133 /*% checks that the LDAP URL parameters make sense */
135 static isc_result_t
136 dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg) {
138 isc_result_t result = ISC_R_SUCCESS;
139 int ldap_result;
140 LDAPURLDesc *ldap_url = NULL;
142 if (!ldap_is_ldap_url(URL)) {
143 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
144 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
145 "%s query is not a valid LDAP URL", msg);
146 result = ISC_R_FAILURE;
147 goto cleanup;
150 ldap_result = ldap_url_parse(URL, &ldap_url);
151 if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
152 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
153 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
154 "parsing %s query failed", msg);
155 result = ISC_R_FAILURE;
156 goto cleanup;
159 if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) {
160 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
161 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
162 "%s query must specify at least "
163 "%d attributes to return",
164 msg, attrCnt);
165 result = ISC_R_FAILURE;
166 goto cleanup;
169 if (ldap_url->lud_host != NULL) {
170 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
171 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
172 "%s query must not specify a host", msg);
173 result = ISC_R_FAILURE;
174 goto cleanup;
177 if (ldap_url->lud_port != 389) {
178 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
179 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
180 "%s query must not specify a port", msg);
181 result = ISC_R_FAILURE;
182 goto cleanup;
185 if (ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1) {
186 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
187 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
188 "%s query must specify a search base", msg);
189 result = ISC_R_FAILURE;
190 goto cleanup;
193 if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) {
194 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
195 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
196 "%s uses extensions. "
197 "The driver does not support LDAP extensions.",
198 msg);
199 result = ISC_R_FAILURE;
200 goto cleanup;
203 cleanup:
205 if (ldap_url != NULL)
206 ldap_free_urldesc(ldap_url);
208 return result;
210 /*% Connects / reconnects to LDAP server */
212 static isc_result_t
213 dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) {
215 isc_result_t result;
216 int ldap_result;
218 /* if we have a connection, get ride of it. */
219 if (dbc->dbconn != NULL) {
220 ldap_unbind_s((LDAP *) dbc->dbconn);
221 dbc->dbconn = NULL;
224 /* now connect / reconnect. */
226 /* initialize. */
227 dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT);
228 if (dbc->dbconn == NULL)
229 return ISC_R_NOMEMORY;
231 /* set protocol version. */
232 ldap_result = ldap_set_option((LDAP *) dbc->dbconn,
233 LDAP_OPT_PROTOCOL_VERSION,
234 &(dbi->protocol));
235 if (ldap_result != LDAP_SUCCESS) {
236 result = ISC_R_NOPERM;
237 goto cleanup;
240 /* "bind" to server. i.e. send username / pass */
241 ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user,
242 dbi->cred, dbi->method);
243 if (ldap_result != LDAP_SUCCESS) {
244 result = ISC_R_FAILURE;
245 goto cleanup;
248 return ISC_R_SUCCESS;
250 cleanup:
252 /* cleanup if failure. */
253 if (dbc->dbconn != NULL) {
254 ldap_unbind_s((LDAP *) dbc->dbconn);
255 dbc->dbconn = NULL;
258 return result;
261 #ifdef ISC_PLATFORM_USETHREADS
265 * Properly cleans up a list of database instances.
266 * This function is only used when the driver is compiled for
267 * multithreaded operation.
269 static void
270 ldap_destroy_dblist(db_list_t *dblist)
273 dbinstance_t *ndbi = NULL;
274 dbinstance_t *dbi = NULL;
276 /* get the first DBI in the list */
277 ndbi = ISC_LIST_HEAD(*dblist);
279 /* loop through the list */
280 while (ndbi != NULL) {
281 dbi = ndbi;
282 /* get the next DBI in the list */
283 ndbi = ISC_LIST_NEXT(dbi, link);
284 /* release DB connection */
285 if (dbi->dbconn != NULL)
286 ldap_unbind_s((LDAP *) dbi->dbconn);
287 /* release all memory that comprised a DBI */
288 destroy_sqldbinstance(dbi);
290 /* release memory for the list structure */
291 isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
295 * Loops through the list of DB instances, attempting to lock
296 * on the mutex. If successful, the DBI is reserved for use
297 * and the thread can perform queries against the database.
298 * If the lock fails, the next one in the list is tried.
299 * looping continues until a lock is obtained, or until
300 * the list has been searched dbc_search_limit times.
301 * This function is only used when the driver is compiled for
302 * multithreaded operation.
305 static dbinstance_t *
306 ldap_find_avail_conn(db_list_t *dblist)
308 dbinstance_t *dbi = NULL;
309 dbinstance_t *head;
310 int count = 0;
312 /* get top of list */
313 head = dbi = ISC_LIST_HEAD(*dblist);
315 /* loop through list */
316 while (count < dbc_search_limit) {
317 /* try to lock on the mutex */
318 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
319 return dbi; /* success, return the DBI for use. */
321 /* not successful, keep trying */
322 dbi = ISC_LIST_NEXT(dbi, link);
324 /* check to see if we have gone to the top of the list. */
325 if (dbi == NULL) {
326 count++;
327 dbi = head;
330 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
331 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
332 "LDAP driver unable to find available connection "
333 "after searching %d times",
334 count);
335 return NULL;
338 #endif /* ISC_PLATFORM_USETHREADS */
340 static isc_result_t
341 ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs,
342 void *ptr, isc_boolean_t allnodes)
344 isc_result_t result = ISC_R_SUCCESS;
345 int i = 0;
346 int j;
347 int len;
348 char *attribute = NULL;
349 LDAPMessage *entry;
350 char *endp = NULL;
351 char *host = NULL;
352 char *type = NULL;
353 char *data = NULL;
354 char **vals = NULL;
355 int ttl;
357 /* make sure there are at least some attributes to process. */
358 REQUIRE(attrs != NULL || attrs[0] != NULL);
360 /* get the first entry to process */
361 entry = ldap_first_entry(dbc, msg);
362 if (entry == NULL) {
363 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
364 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
365 "LDAP no entries to process.");
366 return ISC_R_FAILURE;
369 /* loop through all entries returned */
370 while (entry != NULL) {
372 /* reset for this loop */
373 ttl = 0;
374 len = 0;
375 i = 0;
376 attribute = attrs[i];
378 /* determine how much space we need for data string */
379 for (j=0; attrs[j] != NULL; j++) {
380 /* get the list of values for this attribute. */
381 vals = ldap_get_values(dbc, entry, attrs[j]);
382 /* skip empty attributes. */
383 if (vals == NULL || ldap_count_values(vals) < 1)
384 continue;
386 * we only use the first value. this driver
387 * does not support multi-valued attributes.
389 len = len + strlen(vals[0]) + 1;
390 /* free vals for next loop */
391 ldap_value_free(vals);
392 } /* end for (j=0; attrs[j] != NULL, j++) loop */
394 /* allocate memory for data string */
395 data = isc_mem_allocate(ns_g_mctx, len + 1);
396 if (data == NULL) {
397 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
398 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
399 "LDAP driver unable to allocate memory "
400 "while processing results");
401 result = ISC_R_FAILURE;
402 goto cleanup;
406 * Make sure data is null termed at the beginning so
407 * we can check if any data was stored to it later.
409 data[0] = '\0';
411 /* reset j to re-use below */
412 j = 0;
414 /* loop through the attributes in the order specified. */
415 while (attribute != NULL) {
417 /* get the list of values for this attribute. */
418 vals = ldap_get_values(dbc, entry, attribute);
420 /* skip empty attributes. */
421 if (vals == NULL || vals[0] == NULL) {
422 /* increment attibute pointer */
423 attribute = attrs[++i];
424 /* start loop over */
425 continue;
429 * j initially = 0. Increment j each time we
430 * set a field that way next loop will set
431 * next field.
433 switch(j) {
434 case 0:
435 j++;
437 * convert text to int, make sure it
438 * worked right
440 ttl = strtol(vals[0], &endp, 10);
441 if (*endp != '\0' || ttl < 0) {
442 isc_log_write(dns_lctx,
443 DNS_LOGCATEGORY_DATABASE,
444 DNS_LOGMODULE_DLZ,
445 ISC_LOG_ERROR,
446 "LDAP driver ttl must "
447 "be a postive number");
448 goto cleanup;
450 break;
451 case 1:
452 j++;
453 type = isc_mem_strdup(ns_g_mctx, vals[0]);
454 break;
455 case 2:
456 j++;
457 if (allnodes == isc_boolean_true) {
458 host = isc_mem_strdup(ns_g_mctx,
459 vals[0]);
460 } else {
461 strcpy(data, vals[0]);
463 break;
464 case 3:
465 j++;
466 if (allnodes == isc_boolean_true) {
467 strcpy(data, vals[0]);
468 } else {
469 strcat(data, " ");
470 strcat(data, vals[0]);
472 break;
473 default:
474 strcat(data, " ");
475 strcat(data, vals[0]);
476 break;
477 } /* end switch(j) */
479 /* free values */
480 ldap_value_free(vals);
481 vals = NULL;
483 /* increment attibute pointer */
484 attribute = attrs[++i];
485 } /* end while (attribute != NULL) */
487 if (type == NULL) {
488 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
489 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
490 "LDAP driver unable "
491 "to retrieve dns type");
492 result = ISC_R_FAILURE;
493 goto cleanup;
495 if (strlen(data) < 1) {
496 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
497 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
498 "LDAP driver unable "
499 "to retrieve dns data");
500 result = ISC_R_FAILURE;
501 goto cleanup;
503 if (allnodes == isc_boolean_true) {
504 if (strcasecmp(host, "~") == 0)
505 result = dns_sdlz_putnamedrr(
506 (dns_sdlzallnodes_t *) ptr,
507 "*", type, ttl, data);
508 else
509 result = dns_sdlz_putnamedrr(
510 (dns_sdlzallnodes_t *) ptr,
511 host, type, ttl, data);
513 else
514 result = dns_sdlz_putrr((dns_sdlzlookup_t *) ptr,
515 type, ttl, data);
517 if (result != ISC_R_SUCCESS) {
518 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
519 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
520 "LDAP driver failed "
521 "while sending data to Bind.");
522 goto cleanup;
525 /* free memory for type, data and host for next loop */
526 isc_mem_free(ns_g_mctx, type);
527 isc_mem_free(ns_g_mctx, data);
528 if (host != NULL)
529 isc_mem_free(ns_g_mctx, host);
531 /* get the next entry to process */
532 entry = ldap_next_entry(dbc, entry);
533 } /* end while (entry != NULL) */
535 cleanup:
537 /* de-allocate memory */
538 if (vals != NULL)
539 ldap_value_free(vals);
540 if (host != NULL)
541 isc_mem_free(ns_g_mctx, host);
542 if (type != NULL)
543 isc_mem_free(ns_g_mctx, type);
544 if (data != NULL)
545 isc_mem_free(ns_g_mctx, data);
547 return result;
551 * This function is the real core of the driver. Zone, record
552 * and client strings are passed in (or NULL is passed if the
553 * string is not available). The type of query we want to run
554 * is indicated by the query flag, and the dbdata object is passed
555 * passed in to. dbdata really holds either:
556 * 1) a list of database instances (in multithreaded mode) OR
557 * 2) a single database instance (in single threaded mode)
558 * The function will construct the query and obtain an available
559 * database instance (DBI). It will then run the query and hopefully
560 * obtain a result set.
562 static isc_result_t
563 ldap_get_results(const char *zone, const char *record,
564 const char *client, unsigned int query,
565 void *dbdata, void *ptr)
567 isc_result_t result;
568 dbinstance_t *dbi = NULL;
569 char *querystring = NULL;
570 LDAPURLDesc *ldap_url = NULL;
571 int ldap_result = 0;
572 LDAPMessage *ldap_msg = NULL;
573 int i;
574 int entries;
576 /* get db instance / connection */
577 #ifdef ISC_PLATFORM_USETHREADS
579 /* find an available DBI from the list */
580 dbi = ldap_find_avail_conn((db_list_t *)
581 ((ldap_instance_t *)dbdata)->db);
583 #else /* ISC_PLATFORM_USETHREADS */
586 * only 1 DBI - no need to lock instance lock either
587 * only 1 thread in the whole process, no possible contention.
589 dbi = (dbinstance_t *) ((ldap_instance_t *)dbdata)->db;
591 #endif /* ISC_PLATFORM_USETHREADS */
593 /* if DBI is null, can't do anything else */
594 if (dbi == NULL)
595 return ISC_R_FAILURE;
597 /* set fields */
598 if (zone != NULL) {
599 dbi->zone = isc_mem_strdup(ns_g_mctx, zone);
600 if (dbi->zone == NULL) {
601 result = ISC_R_NOMEMORY;
602 goto cleanup;
604 } else {
605 dbi->zone = NULL;
607 if (record != NULL) {
608 dbi->record = isc_mem_strdup(ns_g_mctx, record);
609 if (dbi->record == NULL) {
610 result = ISC_R_NOMEMORY;
611 goto cleanup;
613 } else {
614 dbi->record = NULL;
616 if (client != NULL) {
617 dbi->client = isc_mem_strdup(ns_g_mctx, client);
618 if (dbi->client == NULL) {
619 result = ISC_R_NOMEMORY;
620 goto cleanup;
622 } else {
623 dbi->client = NULL;
626 /* what type of query are we going to run? */
627 switch(query) {
628 case ALLNODES:
630 * if the query was not passed in from the config file
631 * then we can't run it. return not_implemented, so
632 * it's like the code for that operation was never
633 * built into the driver.... AHHH flexibility!!!
635 if (dbi->allnodes_q == NULL) {
636 result = ISC_R_NOTIMPLEMENTED;
637 goto cleanup;
638 } else {
639 querystring = build_querystring(ns_g_mctx,
640 dbi->allnodes_q);
642 break;
643 case ALLOWXFR:
644 /* same as comments as ALLNODES */
645 if (dbi->allowxfr_q == NULL) {
646 result = ISC_R_NOTIMPLEMENTED;
647 goto cleanup;
648 } else {
649 querystring = build_querystring(ns_g_mctx,
650 dbi->allowxfr_q);
652 break;
653 case AUTHORITY:
654 /* same as comments as ALLNODES */
655 if (dbi->authority_q == NULL) {
656 result = ISC_R_NOTIMPLEMENTED;
657 goto cleanup;
658 } else {
659 querystring = build_querystring(ns_g_mctx,
660 dbi->authority_q);
662 break;
663 case FINDZONE:
664 /* this is required. It's the whole point of DLZ! */
665 if (dbi->findzone_q == NULL) {
666 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
667 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
668 "No query specified for findzone. "
669 "Findzone requires a query");
670 result = ISC_R_FAILURE;
671 goto cleanup;
672 } else {
673 querystring = build_querystring(ns_g_mctx,
674 dbi->findzone_q);
676 break;
677 case LOOKUP:
678 /* this is required. It's also a major point of DLZ! */
679 if (dbi->lookup_q == NULL) {
680 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
681 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
682 "No query specified for lookup. "
683 "Lookup requires a query");
684 result = ISC_R_FAILURE;
685 goto cleanup;
686 } else {
687 querystring = build_querystring(ns_g_mctx,
688 dbi->lookup_q);
690 break;
691 default:
693 * this should never happen. If it does, the code is
694 * screwed up!
696 UNEXPECTED_ERROR(__FILE__, __LINE__,
697 "Incorrect query flag passed to "
698 "ldap_get_results");
699 result = ISC_R_UNEXPECTED;
700 goto cleanup;
703 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
704 if (querystring == NULL) {
705 result = ISC_R_NOMEMORY;
706 goto cleanup;
710 * output the full query string during debug so we can see
711 * what lame error the query has.
713 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
714 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
715 "\nQuery String: %s\n", querystring);
717 /* break URL down into it's component parts, if error cleanup */
718 ldap_result = ldap_url_parse(querystring, &ldap_url);
719 if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
720 result = ISC_R_FAILURE;
721 goto cleanup;
724 for (i=0; i < 3; i++) {
727 * dbi->dbconn may be null if trying to reconnect on a
728 * previous query failed.
730 if (dbi->dbconn == NULL) {
731 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
732 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
733 "LDAP driver attempting to re-connect");
735 result = dlz_ldap_connect((ldap_instance_t *) dbdata,
736 dbi);
737 if (result != ISC_R_SUCCESS) {
738 result = ISC_R_FAILURE;
739 continue;
743 /* perform ldap search syncronously */
744 ldap_result = ldap_search_s((LDAP *) dbi->dbconn,
745 ldap_url->lud_dn,
746 ldap_url->lud_scope,
747 ldap_url->lud_filter,
748 ldap_url->lud_attrs, 0, &ldap_msg);
751 * check return code. No such object is ok, just
752 * didn't find what we wanted
754 switch(ldap_result) {
755 case LDAP_NO_SUCH_OBJECT:
756 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
757 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
758 "No object found matching "
759 "query requirements");
760 result = ISC_R_NOTFOUND;
761 goto cleanup;
762 break;
763 case LDAP_SUCCESS: /* on success do nothing */
764 result = ISC_R_SUCCESS;
765 i = 3;
766 break;
767 case LDAP_SERVER_DOWN:
768 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
769 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
770 "LDAP driver attempting to re-connect");
771 result = dlz_ldap_connect((ldap_instance_t *) dbdata,
772 dbi);
773 if (result != ISC_R_SUCCESS)
774 result = ISC_R_FAILURE;
775 break;
776 default:
778 * other errors not ok. Log error message and
779 * get out
781 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
782 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
783 "LDAP error: %s",
784 ldap_err2string(ldap_result));
785 result = ISC_R_FAILURE;
786 goto cleanup;
787 break;
788 } /* close switch(ldap_result) */
789 } /* end for (int i=0 i < 3; i++) */
791 if (result != ISC_R_SUCCESS)
792 goto cleanup;
794 switch(query) {
795 case ALLNODES:
796 result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
797 ldap_url->lud_attrs,
798 ptr, isc_boolean_true);
799 break;
800 case AUTHORITY:
801 case LOOKUP:
802 result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
803 ldap_url->lud_attrs,
804 ptr, isc_boolean_false);
805 break;
806 case ALLOWXFR:
807 entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
808 if (entries == 0)
809 result = ISC_R_NOPERM;
810 else if (entries > 0)
811 result = ISC_R_SUCCESS;
812 else
813 result = ISC_R_FAILURE;
814 break;
815 case FINDZONE:
816 entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
817 if (entries == 0)
818 result = ISC_R_NOTFOUND;
819 else if (entries > 0)
820 result = ISC_R_SUCCESS;
821 else
822 result = ISC_R_FAILURE;
823 break;
824 default:
826 * this should never happen. If it does, the code is
827 * screwed up!
829 UNEXPECTED_ERROR(__FILE__, __LINE__,
830 "Incorrect query flag passed to "
831 "ldap_get_results");
832 result = ISC_R_UNEXPECTED;
836 cleanup:
837 /* it's always good to cleanup after yourself */
839 /* if we retrieved results, free them */
840 if (ldap_msg != NULL)
841 ldap_msgfree(ldap_msg);
843 if (ldap_url != NULL)
844 ldap_free_urldesc(ldap_url);
846 /* cleanup */
847 if (dbi->zone != NULL)
848 isc_mem_free(ns_g_mctx, dbi->zone);
849 if (dbi->record != NULL)
850 isc_mem_free(ns_g_mctx, dbi->record);
851 if (dbi->client != NULL)
852 isc_mem_free(ns_g_mctx, dbi->client);
854 #ifdef ISC_PLATFORM_USETHREADS
856 /* release the lock so another thread can use this dbi */
857 isc_mutex_unlock(&dbi->instance_lock);
859 #endif /* ISC_PLATFORM_USETHREADS */
861 /* release query string */
862 if (querystring != NULL)
863 isc_mem_free(ns_g_mctx, querystring );
865 /* return result */
866 return result;
870 * DLZ methods
873 static isc_result_t
874 dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name,
875 const char *client)
877 isc_result_t result;
879 UNUSED(driverarg);
881 /* check to see if we are authoritative for the zone first */
882 result = dlz_ldap_findzone(driverarg, dbdata, name);
883 if (result != ISC_R_SUCCESS) {
884 return result;
887 /* get all the zone data */
888 return ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL);
891 static isc_result_t
892 dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata,
893 dns_sdlzallnodes_t *allnodes)
895 UNUSED(driverarg);
896 return ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes);
899 static isc_result_t
900 dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata,
901 dns_sdlzlookup_t *lookup)
903 UNUSED(driverarg);
904 return ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup);
907 static isc_result_t
908 dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name)
910 UNUSED(driverarg);
911 return ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL);
914 static isc_result_t
915 dlz_ldap_lookup(const char *zone, const char *name, void *driverarg,
916 void *dbdata, dns_sdlzlookup_t *lookup)
918 UNUSED(driverarg);
919 if (strcmp(name, "*") == 0)
920 return ldap_get_results(zone, "~", NULL,
921 LOOKUP, dbdata, lookup);
922 else
923 return ldap_get_results(zone, name, NULL,
924 LOOKUP, dbdata, lookup);
928 static isc_result_t
929 dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[],
930 void *driverarg, void **dbdata)
933 isc_result_t result;
934 ldap_instance_t *ldap_inst = NULL;
935 dbinstance_t *dbi = NULL;
936 int protocol;
937 int method;
939 #ifdef ISC_PLATFORM_USETHREADS
940 /* if multi-threaded, we need a few extra variables. */
941 int dbcount;
942 char *endp;
943 /* db_list_t *dblist = NULL; */
944 int i;
946 #endif /* ISC_PLATFORM_USETHREADS */
948 UNUSED(dlzname);
949 UNUSED(driverarg);
951 #ifdef ISC_PLATFORM_USETHREADS
952 /* if debugging, let user know we are multithreaded. */
953 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
954 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
955 "LDAP driver running multithreaded");
956 #else /* ISC_PLATFORM_USETHREADS */
957 /* if debugging, let user know we are single threaded. */
958 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
959 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
960 "LDAP driver running single threaded");
961 #endif /* ISC_PLATFORM_USETHREADS */
963 if (argc < 9) {
964 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
965 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
966 "LDAP driver requires at least "
967 "8 command line args.");
968 return (ISC_R_FAILURE);
971 /* no more than 13 arg's should be passed to the driver */
972 if (argc > 12) {
973 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
974 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
975 "LDAP driver cannot accept more than "
976 "11 command line args.");
977 return (ISC_R_FAILURE);
980 /* determine protocol version. */
981 if (strncasecmp(argv[2], V2, strlen(V2)) == 0) {
982 protocol = 2;
983 } else if (strncasecmp(argv[2], V3, strlen(V3)) == 0) {
984 protocol = 3;
985 } else {
986 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
987 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
988 "LDAP driver protocol must be either %s or %s",
989 V2, V3);
990 return (ISC_R_FAILURE);
993 /* determine connection method. */
994 if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0) {
995 method = LDAP_AUTH_SIMPLE;
996 } else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0) {
997 method = LDAP_AUTH_KRBV41;
998 } else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0) {
999 method = LDAP_AUTH_KRBV42;
1000 } else {
1001 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1002 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1003 "LDAP driver authentication method must be "
1004 "one of %s, %s or %s",
1005 SIMPLE, KRB41, KRB42);
1006 return (ISC_R_FAILURE);
1009 /* multithreaded build can have multiple DB connections */
1010 #ifdef ISC_PLATFORM_USETHREADS
1012 /* check how many db connections we should create */
1013 dbcount = strtol(argv[1], &endp, 10);
1014 if (*endp != '\0' || dbcount < 0) {
1015 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1016 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1017 "LDAP driver database connection count "
1018 "must be positive.");
1019 return (ISC_R_FAILURE);
1021 #endif
1023 /* check that LDAP URL parameters make sense */
1024 switch(argc) {
1025 case 12:
1026 result = dlz_ldap_checkURL(argv[11], 0, "allow zone transfer");
1027 if (result != ISC_R_SUCCESS)
1028 return result;
1029 case 11:
1030 result = dlz_ldap_checkURL(argv[10], 3, "all nodes");
1031 if (result != ISC_R_SUCCESS)
1032 return result;
1033 case 10:
1034 if (strlen(argv[9]) > 0) {
1035 result = dlz_ldap_checkURL(argv[9], 3, "authority");
1036 if (result != ISC_R_SUCCESS)
1037 return result;
1039 case 9:
1040 result = dlz_ldap_checkURL(argv[8], 3, "lookup");
1041 if (result != ISC_R_SUCCESS)
1042 return result;
1043 result = dlz_ldap_checkURL(argv[7], 0, "find zone");
1044 if (result != ISC_R_SUCCESS)
1045 return result;
1046 break;
1047 default:
1048 /* not really needed, should shut up compiler. */
1049 result = ISC_R_FAILURE;
1052 /* allocate memory for LDAP instance */
1053 ldap_inst = isc_mem_get(ns_g_mctx, sizeof(ldap_instance_t));
1054 if (ldap_inst == NULL)
1055 return (ISC_R_NOMEMORY);
1056 memset(ldap_inst, 0, sizeof(ldap_instance_t));
1058 /* store info needed to automatically re-connect. */
1059 ldap_inst->protocol = protocol;
1060 ldap_inst->method = method;
1061 ldap_inst->hosts = isc_mem_strdup(ns_g_mctx, argv[6]);
1062 if (ldap_inst->hosts == NULL) {
1063 result = ISC_R_NOMEMORY;
1064 goto cleanup;
1066 ldap_inst->user = isc_mem_strdup(ns_g_mctx, argv[4]);
1067 if (ldap_inst->user == NULL) {
1068 result = ISC_R_NOMEMORY;
1069 goto cleanup;
1071 ldap_inst->cred = isc_mem_strdup(ns_g_mctx, argv[5]);
1072 if (ldap_inst->cred == NULL) {
1073 result = ISC_R_NOMEMORY;
1074 goto cleanup;
1077 #ifdef ISC_PLATFORM_USETHREADS
1078 /* allocate memory for database connection list */
1079 ldap_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1080 if (ldap_inst->db == NULL) {
1081 result = ISC_R_NOMEMORY;
1082 goto cleanup;
1085 /* initialize DB connection list */
1086 ISC_LIST_INIT(*(ldap_inst->db));
1089 * create the appropriate number of database instances (DBI)
1090 * append each new DBI to the end of the list
1092 for (i = 0; i < dbcount; i++) {
1094 #endif /* ISC_PLATFORM_USETHREADS */
1096 /* how many queries were passed in from config file? */
1097 switch(argc) {
1098 case 9:
1099 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1100 NULL, argv[7], argv[8],
1101 NULL, &dbi);
1102 break;
1103 case 10:
1104 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1105 argv[9], argv[7], argv[8],
1106 NULL, &dbi);
1107 break;
1108 case 11:
1109 result = build_sqldbinstance(ns_g_mctx, argv[10], NULL,
1110 argv[9], argv[7], argv[8],
1111 NULL, &dbi);
1112 break;
1113 case 12:
1114 result = build_sqldbinstance(ns_g_mctx, argv[10],
1115 argv[11], argv[9],
1116 argv[7], argv[8],
1117 NULL, &dbi);
1118 break;
1119 default:
1120 /* not really needed, should shut up compiler. */
1121 result = ISC_R_FAILURE;
1124 if (result == ISC_R_SUCCESS) {
1125 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1126 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1127 "LDAP driver created "
1128 "database instance object.");
1129 } else { /* unsuccessful?, log err msg and cleanup. */
1130 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1131 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1132 "LDAP driver could not create "
1133 "database instance object.");
1134 goto cleanup;
1137 #ifdef ISC_PLATFORM_USETHREADS
1138 /* when multithreaded, build a list of DBI's */
1139 ISC_LINK_INIT(dbi, link);
1140 ISC_LIST_APPEND(*(ldap_inst->db), dbi, link);
1141 #else
1143 * when single threaded, hold onto the one connection
1144 * instance.
1146 ldap_inst->db = dbi;
1148 #endif
1149 /* attempt to connect */
1150 result = dlz_ldap_connect(ldap_inst, dbi);
1153 * if db connection cannot be created, log err msg and
1154 * cleanup.
1156 switch(result) {
1157 /* success, do nothing */
1158 case ISC_R_SUCCESS:
1159 break;
1161 * no memory means ldap_init could not
1162 * allocate memory
1164 case ISC_R_NOMEMORY:
1165 #ifdef ISC_PLATFORM_USETHREADS
1166 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1167 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1168 "LDAP driver could not allocate memory "
1169 "for connection number %u",
1170 i+1);
1171 #else
1172 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1173 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1174 "LDAP driver could not allocate memory "
1175 "for connection");
1176 #endif
1177 goto cleanup;
1178 break;
1180 * no perm means ldap_set_option could not set
1181 * protocol version
1183 case ISC_R_NOPERM:
1184 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1185 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1186 "LDAP driver could not "
1187 "set protocol version.");
1188 result = ISC_R_FAILURE;
1189 goto cleanup;
1190 break;
1191 /* failure means couldn't connect to ldap server */
1192 case ISC_R_FAILURE:
1193 #ifdef ISC_PLATFORM_USETHREADS
1194 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1195 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1196 "LDAP driver could not "
1197 "bind connection number %u to server.",
1198 i+1);
1199 #else
1200 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1201 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1202 "LDAP driver could not "
1203 "bind connection to server.");
1204 #endif
1205 goto cleanup;
1206 break;
1208 * default should never happen. If it does,
1209 * major errors.
1211 default:
1212 UNEXPECTED_ERROR(__FILE__, __LINE__,
1213 "dlz_ldap_create() failed: %s",
1214 isc_result_totext(result));
1215 result = ISC_R_UNEXPECTED;
1216 goto cleanup;
1217 break;
1218 } /* end switch(result) */
1221 #ifdef ISC_PLATFORM_USETHREADS
1223 /* set DBI = null for next loop through. */
1224 dbi = NULL;
1225 } /* end for loop */
1227 #endif /* ISC_PLATFORM_USETHREADS */
1230 /* set dbdata to the ldap_instance we created. */
1231 *dbdata = ldap_inst;
1233 /* hey, we got through all of that ok, return success. */
1234 return(ISC_R_SUCCESS);
1236 cleanup:
1238 dlz_ldap_destroy(NULL, ldap_inst);
1240 return(ISC_R_FAILURE);
1243 void
1244 dlz_ldap_destroy(void *driverarg, void *dbdata)
1247 UNUSED(driverarg);
1249 if (dbdata != NULL) {
1251 #ifdef ISC_PLATFORM_USETHREADS
1253 /* cleanup the list of DBI's */
1254 ldap_destroy_dblist((db_list_t *)
1255 ((ldap_instance_t *)dbdata)->db);
1257 #else /* ISC_PLATFORM_USETHREADS */
1259 /* release connection */
1260 if (((ldap_instance_t *)dbdata)->db->dbconn != NULL)
1261 ldap_unbind_s((LDAP *)
1262 ((ldap_instance_t *)dbdata)->db->dbconn);
1264 /* destroy single DB instance */
1265 destroy_sqldbinstance(((ldap_instance_t *)dbdata)->db);
1267 #endif /* ISC_PLATFORM_USETHREADS */
1269 if (((ldap_instance_t *)dbdata)->hosts != NULL)
1270 isc_mem_free(ns_g_mctx,
1271 ((ldap_instance_t *)dbdata)->hosts);
1273 if (((ldap_instance_t *)dbdata)->user != NULL)
1274 isc_mem_free(ns_g_mctx,
1275 ((ldap_instance_t *)dbdata)->user);
1277 if (((ldap_instance_t *)dbdata)->cred != NULL)
1278 isc_mem_free(ns_g_mctx,
1279 ((ldap_instance_t *)dbdata)->cred);
1281 isc_mem_put(ns_g_mctx, dbdata, sizeof(ldap_instance_t));
1285 static dns_sdlzmethods_t dlz_ldap_methods = {
1286 dlz_ldap_create,
1287 dlz_ldap_destroy,
1288 dlz_ldap_findzone,
1289 dlz_ldap_lookup,
1290 dlz_ldap_authority,
1291 dlz_ldap_allnodes,
1292 dlz_ldap_allowzonexfr
1296 * Wrapper around dns_sdlzregister().
1298 isc_result_t
1299 dlz_ldap_init(void) {
1300 isc_result_t result;
1303 * Write debugging message to log
1305 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1306 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1307 "Registering DLZ ldap driver.");
1309 result = dns_sdlzregister("ldap", &dlz_ldap_methods, NULL,
1310 DNS_SDLZFLAG_RELATIVEOWNER |
1311 DNS_SDLZFLAG_RELATIVERDATA,
1312 ns_g_mctx, &dlz_ldap);
1313 if (result != ISC_R_SUCCESS) {
1314 UNEXPECTED_ERROR(__FILE__, __LINE__,
1315 "dns_sdlzregister() failed: %s",
1316 isc_result_totext(result));
1317 result = ISC_R_UNEXPECTED;
1321 return result;
1325 * Wrapper around dns_sdlzunregister().
1327 void
1328 dlz_ldap_clear(void) {
1331 * Write debugging message to log
1333 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1334 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1335 "Unregistering DLZ ldap driver.");
1337 if (dlz_ldap != NULL)
1338 dns_sdlzunregister(&dlz_ldap);
1341 #endif