1 /* $NetBSD: dlz_ldap_driver.c,v 1.7 2014/12/10 04:37:55 christos Exp $ */
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
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
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.
64 #include <dns/result.h>
67 #include <isc/platform.h>
68 #include <isc/print.h>
69 #include <isc/result.h>
70 #include <isc/string.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
85 #define SIMPLE "simple"
91 static dns_sdlzimplementation_t
*dlz_ldap
= NULL
;
93 #define dbc_search_limit 30
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.
107 #ifdef ISC_PLATFORM_USETHREADS
108 db_list_t
*db
; /*%< handle to a list of DB */
110 dbinstance_t
*db
; /*%< handle to db */
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 */
119 /* forward references */
122 dlz_ldap_findzone(void *driverarg
, void *dbdata
, const char *name
,
123 dns_clientinfomethods_t
*methods
,
124 dns_clientinfo_t
*clientinfo
);
127 dlz_ldap_destroy(void *driverarg
, void *dbdata
);
133 /*% checks that the LDAP URL parameters make sense */
135 dlz_ldap_checkURL(char *URL
, int attrCnt
, const char *msg
) {
136 isc_result_t result
= ISC_R_SUCCESS
;
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
;
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
;
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",
163 result
= ISC_R_FAILURE
;
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
;
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
;
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
;
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.",
197 result
= ISC_R_FAILURE
;
202 if (ldap_url
!= NULL
)
203 ldap_free_urldesc(ldap_url
);
208 /*% Connects / reconnects to LDAP server */
210 dlz_ldap_connect(ldap_instance_t
*dbi
, dbinstance_t
*dbc
) {
214 /* if we have a connection, get ride of it. */
215 if (dbc
->dbconn
!= NULL
) {
216 ldap_unbind_s((LDAP
*) dbc
->dbconn
);
220 /* now connect / reconnect. */
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
,
231 if (ldap_result
!= LDAP_SUCCESS
) {
232 result
= ISC_R_NOPERM
;
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
;
244 return (ISC_R_SUCCESS
);
248 /* cleanup if failure. */
249 if (dbc
->dbconn
!= NULL
) {
250 ldap_unbind_s((LDAP
*) dbc
->dbconn
);
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.
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
) {
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
;
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. */
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",
329 #endif /* ISC_PLATFORM_USETHREADS */
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
;
339 char *attribute
= NULL
;
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
);
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 */
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)
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);
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
;
396 * Make sure data is null termed at the beginning so
397 * we can check if any data was stored to it later.
401 /* reset j to re-use below */
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 */
418 * j initially = 0. Increment j each time we
419 * set a field that way next loop will set
426 * convert text to int, make sure it
429 ttl
= strtol(vals
[0], &endp
, 10);
430 if (*endp
!= '\0' || ttl
< 0) {
431 isc_log_write(dns_lctx
,
432 DNS_LOGCATEGORY_DATABASE
,
435 "LDAP driver ttl must "
436 "be a postive number");
442 type
= isc_mem_strdup(ns_g_mctx
, vals
[0]);
447 host
= isc_mem_strdup(ns_g_mctx
,
450 strcpy(data
, vals
[0]);
455 strcpy(data
, vals
[0]);
458 strcat(data
, vals
[0]);
463 strcat(data
, vals
[0]);
465 } /* end switch(j) */
468 ldap_value_free(vals
);
471 /* increment attibute pointer */
472 attribute
= attrs
[++i
];
473 } /* end while (attribute != 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
;
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
;
493 if (allnodes
&& host
!= NULL
) {
494 if (strcasecmp(host
, "~") == 0)
495 result
= dns_sdlz_putnamedrr(
496 (dns_sdlzallnodes_t
*) ptr
,
497 "*", type
, ttl
, data
);
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
));
511 result
= dns_sdlz_putrr((dns_sdlzlookup_t
*) ptr
,
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",
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.");
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
);
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) */
542 /* de-allocate memory */
544 ldap_value_free(vals
);
546 isc_mem_free(ns_g_mctx
, host
);
548 isc_mem_free(ns_g_mctx
, type
);
550 isc_mem_free(ns_g_mctx
, data
);
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.
568 ldap_get_results(const char *zone
, const char *record
,
569 const char *client
, unsigned int query
,
570 void *dbdata
, void *ptr
)
573 dbinstance_t
*dbi
= NULL
;
574 char *querystring
= NULL
;
575 LDAPURLDesc
*ldap_url
= NULL
;
577 LDAPMessage
*ldap_msg
= NULL
;
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 */
600 return (ISC_R_FAILURE
);
604 dbi
->zone
= isc_mem_strdup(ns_g_mctx
, zone
);
605 if (dbi
->zone
== NULL
) {
606 result
= ISC_R_NOMEMORY
;
612 if (record
!= NULL
) {
613 dbi
->record
= isc_mem_strdup(ns_g_mctx
, record
);
614 if (dbi
->record
== NULL
) {
615 result
= ISC_R_NOMEMORY
;
621 if (client
!= NULL
) {
622 dbi
->client
= isc_mem_strdup(ns_g_mctx
, client
);
623 if (dbi
->client
== NULL
) {
624 result
= ISC_R_NOMEMORY
;
631 /* what type of query are we going to run? */
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
;
644 querystring
= build_querystring(ns_g_mctx
,
649 /* same as comments as ALLNODES */
650 if (dbi
->allowxfr_q
== NULL
) {
651 result
= ISC_R_NOTIMPLEMENTED
;
654 querystring
= build_querystring(ns_g_mctx
,
659 /* same as comments as ALLNODES */
660 if (dbi
->authority_q
== NULL
) {
661 result
= ISC_R_NOTIMPLEMENTED
;
664 querystring
= build_querystring(ns_g_mctx
,
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
;
678 querystring
= build_querystring(ns_g_mctx
,
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
;
692 querystring
= build_querystring(ns_g_mctx
,
698 * this should never happen. If it does, the code is
701 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
702 "Incorrect query flag passed to "
704 result
= ISC_R_UNEXPECTED
;
708 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
709 if (querystring
== NULL
) {
710 result
= ISC_R_NOMEMORY
;
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
;
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
,
742 if (result
!= ISC_R_SUCCESS
) {
743 result
= ISC_R_FAILURE
;
748 /* perform ldap search syncronously */
749 ldap_result
= ldap_search_s((LDAP
*) dbi
->dbconn
,
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
;
768 case LDAP_SUCCESS
: /* on success do nothing */
769 result
= ISC_R_SUCCESS
;
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
,
778 if (result
!= ISC_R_SUCCESS
)
779 result
= ISC_R_FAILURE
;
783 * other errors not ok. Log error message and
786 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
787 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
789 ldap_err2string(ldap_result
));
790 result
= ISC_R_FAILURE
;
793 } /* close switch(ldap_result) */
794 } /* end for (int i = 0 i < 3; i++) */
796 if (result
!= ISC_R_SUCCESS
)
801 result
= ldap_process_results((LDAP
*) dbi
->dbconn
, ldap_msg
,
803 ptr
, isc_boolean_true
);
807 result
= ldap_process_results((LDAP
*) dbi
->dbconn
, ldap_msg
,
809 ptr
, isc_boolean_false
);
812 entries
= ldap_count_entries((LDAP
*) dbi
->dbconn
, ldap_msg
);
814 result
= ISC_R_NOPERM
;
815 else if (entries
> 0)
816 result
= ISC_R_SUCCESS
;
818 result
= ISC_R_FAILURE
;
821 entries
= ldap_count_entries((LDAP
*) dbi
->dbconn
, ldap_msg
);
823 result
= ISC_R_NOTFOUND
;
824 else if (entries
> 0)
825 result
= ISC_R_SUCCESS
;
827 result
= ISC_R_FAILURE
;
831 * this should never happen. If it does, the code is
834 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
835 "Incorrect query flag passed to "
837 result
= ISC_R_UNEXPECTED
;
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
);
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
);
877 dlz_ldap_allowzonexfr(void *driverarg
, void *dbdata
, const char *name
,
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
) {
890 /* get all the zone data */
891 result
= ldap_get_results(name
, NULL
, client
, ALLOWXFR
, dbdata
, NULL
);
896 dlz_ldap_allnodes(const char *zone
, void *driverarg
, void *dbdata
,
897 dns_sdlzallnodes_t
*allnodes
)
900 return (ldap_get_results(zone
, NULL
, NULL
, ALLNODES
, dbdata
, allnodes
));
904 dlz_ldap_authority(const char *zone
, void *driverarg
, void *dbdata
,
905 dns_sdlzlookup_t
*lookup
)
908 return (ldap_get_results(zone
, NULL
, NULL
, AUTHORITY
, dbdata
, lookup
));
912 dlz_ldap_findzone(void *driverarg
, void *dbdata
, const char *name
,
913 dns_clientinfomethods_t
*methods
,
914 dns_clientinfo_t
*clientinfo
)
919 return (ldap_get_results(name
, NULL
, NULL
, FINDZONE
, dbdata
, NULL
));
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
)
933 if (strcmp(name
, "*") == 0)
934 result
= ldap_get_results(zone
, "~", NULL
, LOOKUP
,
937 result
= ldap_get_results(zone
, name
, NULL
, LOOKUP
,
944 dlz_ldap_create(const char *dlzname
, unsigned int argc
, char *argv
[],
945 void *driverarg
, void **dbdata
)
948 ldap_instance_t
*ldap_inst
= NULL
;
949 dbinstance_t
*dbi
= NULL
;
953 #ifdef ISC_PLATFORM_USETHREADS
954 /* if multi-threaded, we need a few extra variables. */
957 /* db_list_t *dblist = NULL; */
960 #endif /* ISC_PLATFORM_USETHREADS */
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 */
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 */
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) {
997 } else if (strncasecmp(argv
[2], V3
, strlen(V3
)) == 0) {
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",
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
;
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
);
1037 /* check that LDAP URL parameters make sense */
1040 result
= dlz_ldap_checkURL(argv
[11], 0, "allow zone transfer");
1041 if (result
!= ISC_R_SUCCESS
)
1044 result
= dlz_ldap_checkURL(argv
[10], 3, "all nodes");
1045 if (result
!= ISC_R_SUCCESS
)
1048 if (strlen(argv
[9]) > 0) {
1049 result
= dlz_ldap_checkURL(argv
[9], 3, "authority");
1050 if (result
!= ISC_R_SUCCESS
)
1054 result
= dlz_ldap_checkURL(argv
[8], 3, "lookup");
1055 if (result
!= ISC_R_SUCCESS
)
1057 result
= dlz_ldap_checkURL(argv
[7], 0, "find zone");
1058 if (result
!= ISC_R_SUCCESS
)
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
;
1080 ldap_inst
->user
= isc_mem_strdup(ns_g_mctx
, argv
[4]);
1081 if (ldap_inst
->user
== NULL
) {
1082 result
= ISC_R_NOMEMORY
;
1085 ldap_inst
->cred
= isc_mem_strdup(ns_g_mctx
, argv
[5]);
1086 if (ldap_inst
->cred
== NULL
) {
1087 result
= ISC_R_NOMEMORY
;
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
;
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? */
1113 result
= build_sqldbinstance(ns_g_mctx
, NULL
, NULL
,
1114 NULL
, argv
[7], argv
[8],
1118 result
= build_sqldbinstance(ns_g_mctx
, NULL
, NULL
,
1119 argv
[9], argv
[7], argv
[8],
1123 result
= build_sqldbinstance(ns_g_mctx
, argv
[10], NULL
,
1124 argv
[9], argv
[7], argv
[8],
1128 result
= build_sqldbinstance(ns_g_mctx
, argv
[10],
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.");
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
);
1157 * when single threaded, hold onto the one connection
1160 ldap_inst
->db
= dbi
;
1163 /* attempt to connect */
1164 result
= dlz_ldap_connect(ldap_inst
, dbi
);
1167 * if db connection cannot be created, log err msg and
1171 /* success, do nothing */
1175 * no memory means ldap_init could not
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",
1186 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1187 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1188 "LDAP driver could not allocate memory "
1194 * no perm means ldap_set_option could not set
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
;
1205 /* failure means couldn't connect to ldap server */
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.",
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.");
1222 * default should never happen. If it does,
1226 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
1227 "dlz_ldap_create() failed: %s",
1228 isc_result_totext(result
));
1229 result
= ISC_R_UNEXPECTED
;
1232 } /* end switch(result) */
1235 #ifdef ISC_PLATFORM_USETHREADS
1237 /* set DBI = null for next loop through. */
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
);
1251 dlz_ldap_destroy(NULL
, ldap_inst
);
1253 return(ISC_R_FAILURE
);
1257 dlz_ldap_destroy(void *driverarg
, void *dbdata
) {
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
= {
1298 dlz_ldap_allowzonexfr
,
1309 * Wrapper around dns_sdlzregister().
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
;
1337 * Wrapper around dns_sdlzunregister().
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
);