1 /* $NetBSD: dlz_ldap_dynamic.c,v 1.1.1.3 2014/12/10 03:34:31 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) 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
66 #include <dlz_minimal.h>
69 #include <dlz_pthread.h>
72 * Need older API functions from ldap.h.
74 #define LDAP_DEPRECATED 1
78 #define SIMPLE "simple"
84 #define dbc_search_limit 30
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.
98 db_list_t
*db
; /*%< handle to a list of DB */
100 dbinstance_t
*db
; /*%< handle to db */
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 */
110 dns_sdlz_putrr_t
*putrr
;
111 dns_sdlz_putnamedrr_t
*putnamedrr
;
112 dns_dlz_writeablezone_t
*writeable_zone
;
115 /* forward references */
117 #if DLZ_DLOPEN_VERSION < 3
119 dlz_findzonedb(void *dbdata
, const char *name
);
122 dlz_findzonedb(void *dbdata
, const char *name
,
123 dns_clientinfomethods_t
*methods
,
124 dns_clientinfo_t
*clientinfo
);
128 dlz_destroy(void *dbdata
);
131 b9_add_helper(ldap_instance_t
*db
, const char *helper_name
, void *ptr
);
137 /*% checks that the LDAP URL parameters make sense */
139 ldap_checkURL(ldap_instance_t
*db
, char *URL
, int attrCnt
, const char *msg
) {
140 isc_result_t result
= ISC_R_SUCCESS
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
196 if (ldap_url
!= NULL
)
197 ldap_free_urldesc(ldap_url
);
202 /*% Connects / reconnects to LDAP server */
204 ldap_connect(ldap_instance_t
*dbi
, dbinstance_t
*dbc
) {
208 /* if we have a connection, get ride of it. */
209 if (dbc
->dbconn
!= NULL
) {
210 ldap_unbind_s((LDAP
*) dbc
->dbconn
);
214 /* now connect / reconnect. */
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
,
225 if (ldap_result
!= LDAP_SUCCESS
) {
226 result
= ISC_R_NOPERM
;
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
;
238 return (ISC_R_SUCCESS
);
242 /* cleanup if failure. */
243 if (dbc
->dbconn
!= NULL
) {
244 ldap_unbind_s((LDAP
*) dbc
->dbconn
);
253 * Properly cleans up a list of database instances.
254 * This function is only used when the driver is compiled for
255 * multithreaded operation.
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
) {
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 */
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
;
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. */
315 ldap
->log(ISC_LOG_INFO
,
316 "LDAP driver unable to find available connection "
317 "after searching %d times", count
);
320 #endif /* PTHREADS */
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
;
330 char *attribute
= NULL
;
339 /* get the first entry to process */
340 entry
= ldap_first_entry(dbc
, msg
);
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 */
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)
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);
373 db
->log(ISC_LOG_ERROR
,
374 "LDAP driver unable to allocate memory "
375 "while processing results");
376 result
= ISC_R_FAILURE
;
381 * Make sure data is null termed at the beginning so
382 * we can check if any data was stored to it later.
386 /* reset j to re-use below */
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 */
403 * j initially = 0. Increment j each time we
404 * set a field that way next loop will set
411 * convert text to int, make sure it
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");
424 type
= strdup(vals
[0]);
429 host
= strdup(vals
[0]);
431 strcpy(data
, vals
[0]);
436 strcpy(data
, vals
[0]);
439 strcat(data
, vals
[0]);
444 strcat(data
, vals
[0]);
449 ldap_value_free(vals
);
452 /* increment attibute pointer */
453 attribute
= attrs
[++i
];
457 db
->log(ISC_LOG_ERROR
,
458 "LDAP driver unable to retrieve DNS type");
459 result
= ISC_R_FAILURE
;
463 if (strlen(data
) < 1) {
464 db
->log(ISC_LOG_ERROR
,
465 "LDAP driver unable to retrieve DNS data");
466 result
= ISC_R_FAILURE
;
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
,
476 result
= db
->putnamedrr(an
, host
, type
,
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
);
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.");
500 /* free memory for type, data and host for next loop */
512 /* get the next entry to process */
513 entry
= ldap_next_entry(dbc
, entry
);
517 /* de-allocate memory */
519 ldap_value_free(vals
);
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.
543 ldap_get_results(const char *zone
, const char *record
,
544 const char *client
, unsigned int query
,
545 void *dbdata
, void *ptr
)
548 ldap_instance_t
*db
= (ldap_instance_t
*)dbdata
;
549 dbinstance_t
*dbi
= NULL
;
550 char *querystring
= NULL
;
551 LDAPURLDesc
*ldap_url
= NULL
;
553 LDAPMessage
*ldap_msg
= NULL
;
557 /* get db instance / connection */
559 /* find an available DBI from the list */
560 dbi
= ldap_find_avail_conn(db
);
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 */
571 return (ISC_R_FAILURE
);
575 dbi
->zone
= strdup(zone
);
576 if (dbi
->zone
== NULL
) {
577 result
= ISC_R_NOMEMORY
;
583 if (record
!= NULL
) {
584 dbi
->record
= strdup(record
);
585 if (dbi
->record
== NULL
) {
586 result
= ISC_R_NOMEMORY
;
592 if (client
!= NULL
) {
593 dbi
->client
= strdup(client
);
594 if (dbi
->client
== NULL
) {
595 result
= ISC_R_NOMEMORY
;
602 /* what type of query are we going to run? */
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
;
615 querystring
= build_querystring(dbi
->allnodes_q
);
618 /* same as comments as ALLNODES */
619 if (dbi
->allowxfr_q
== NULL
) {
620 result
= ISC_R_NOTIMPLEMENTED
;
623 querystring
= build_querystring(dbi
->allowxfr_q
);
626 /* same as comments as ALLNODES */
627 if (dbi
->authority_q
== NULL
) {
628 result
= ISC_R_NOTIMPLEMENTED
;
631 querystring
= build_querystring(dbi
->authority_q
);
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
;
642 querystring
= build_querystring(dbi
->findzone_q
);
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
;
653 querystring
= build_querystring(dbi
->lookup_q
);
657 * this should never happen. If it does, the code is
660 db
->log(ISC_LOG_ERROR
,
661 "Incorrect query flag passed to ldap_get_results");
662 result
= ISC_R_UNEXPECTED
;
666 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
667 if (querystring
== NULL
) {
668 result
= ISC_R_NOMEMORY
;
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
;
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
;
701 /* perform ldap search syncronously */
702 ldap_result
= ldap_search_s((LDAP
*) dbi
->dbconn
,
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
;
719 case LDAP_SUCCESS
: /* on success do nothing */
720 result
= ISC_R_SUCCESS
;
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
;
732 * other errors not ok. Log error message and
735 db
->log(ISC_LOG_ERROR
, "LDAP error: %s",
736 ldap_err2string(ldap_result
));
737 result
= ISC_R_FAILURE
;
743 if (result
!= ISC_R_SUCCESS
)
748 result
= ldap_process_results(db
, (LDAP
*) dbi
->dbconn
,
749 ldap_msg
, ldap_url
->lud_attrs
,
754 result
= ldap_process_results(db
, (LDAP
*) dbi
->dbconn
,
755 ldap_msg
, ldap_url
->lud_attrs
,
759 entries
= ldap_count_entries((LDAP
*) dbi
->dbconn
, ldap_msg
);
761 result
= ISC_R_NOPERM
;
762 else if (entries
> 0)
763 result
= ISC_R_SUCCESS
;
765 result
= ISC_R_FAILURE
;
768 entries
= ldap_count_entries((LDAP
*) dbi
->dbconn
, ldap_msg
);
770 result
= ISC_R_NOTFOUND
;
771 else if (entries
> 0)
772 result
= ISC_R_SUCCESS
;
774 result
= ISC_R_FAILURE
;
778 * this should never happen. If it does, the code is
781 db
->log(ISC_LOG_ERROR
,
782 "Incorrect query flag passed to ldap_get_results");
783 result
= ISC_R_UNEXPECTED
;
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
);
797 if (dbi
->zone
!= NULL
)
799 if (dbi
->record
!= NULL
)
801 if (dbi
->client
!= NULL
)
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
)
820 dlz_allowzonexfr(void *dbdata
, const char *name
, const char *client
) {
823 /* check to see if we are authoritative for the zone first */
824 #if DLZ_DLOPEN_VERSION < 3
825 result
= dlz_findzonedb(dbdata
, name
);
827 result
= dlz_findzonedb(dbdata
, name
, NULL
, NULL
);
829 if (result
!= ISC_R_SUCCESS
) {
833 /* get all the zone data */
834 result
= ldap_get_results(name
, NULL
, client
, ALLOWXFR
, dbdata
, NULL
);
839 dlz_allnodes(const char *zone
, void *dbdata
, dns_sdlzallnodes_t
*allnodes
)
841 return (ldap_get_results(zone
, NULL
, NULL
, ALLNODES
, dbdata
, allnodes
));
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
851 dlz_findzonedb(void *dbdata
, const char *name
)
854 dlz_findzonedb(void *dbdata
, const char *name
,
855 dns_clientinfomethods_t
*methods
,
856 dns_clientinfo_t
*clientinfo
)
859 #if DLZ_DLOPEN_VERSION >= 3
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
)
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
)
878 #if DLZ_DLOPEN_VERSION >= 2
883 if (strcmp(name
, "*") == 0)
884 result
= ldap_get_results(zone
, "~", NULL
, LOOKUP
,
887 result
= ldap_get_results(zone
, name
, NULL
, LOOKUP
,
894 dlz_create(const char *dlzname
, unsigned int argc
, char *argv
[],
897 isc_result_t result
= ISC_R_FAILURE
;
898 ldap_instance_t
*ldap
= NULL
;
899 dbinstance_t
*dbi
= NULL
;
900 const char *helper_name
;
907 #endif /* PTHREADS */
912 /* allocate memory for LDAP instance */
913 ldap
= calloc(1, sizeof(ldap_instance_t
));
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*));
925 /* if debugging, let user know we are multithreaded. */
926 ldap
->log(ISC_LOG_DEBUG(1), "LDAP driver running multithreaded");
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 */
933 ldap
->log(ISC_LOG_ERROR
,
934 "LDAP driver requires at least "
935 "8 command line args.");
939 /* no more than 13 arg's should be passed to the driver */
941 ldap
->log(ISC_LOG_ERROR
,
942 "LDAP driver cannot accept more than "
943 "11 command line args.");
947 /* determine protocol version. */
948 if (strncasecmp(argv
[2], V2
, strlen(V2
)) == 0)
950 else if (strncasecmp(argv
[2], V3
, strlen(V3
)) == 0)
953 ldap
->log(ISC_LOG_ERROR
,
954 "LDAP driver protocol must be either %s or %s",
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
;
967 ldap
->log(ISC_LOG_ERROR
,
968 "LDAP driver authentication method must be "
969 "one of %s, %s or %s", SIMPLE
, KRB41
, KRB42
);
973 /* multithreaded build can have multiple DB connections */
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.");
985 /* check that LDAP URL parameters make sense */
988 result
= ldap_checkURL(ldap
, argv
[11], 0,
989 "allow zone transfer");
990 if (result
!= ISC_R_SUCCESS
)
993 result
= ldap_checkURL(ldap
, argv
[10], 3, "all nodes");
994 if (result
!= ISC_R_SUCCESS
)
997 if (strlen(argv
[9]) > 0) {
998 result
= ldap_checkURL(ldap
, argv
[9], 3, "authority");
999 if (result
!= ISC_R_SUCCESS
)
1003 result
= ldap_checkURL(ldap
, argv
[8], 3, "lookup");
1004 if (result
!= ISC_R_SUCCESS
)
1006 result
= ldap_checkURL(ldap
, argv
[7], 0, "find zone");
1007 if (result
!= ISC_R_SUCCESS
)
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
;
1023 ldap
->user
= strdup(argv
[4]);
1024 if (ldap
->user
== NULL
) {
1025 result
= ISC_R_NOMEMORY
;
1028 ldap
->cred
= strdup(argv
[5]);
1029 if (ldap
->cred
== NULL
) {
1030 result
= ISC_R_NOMEMORY
;
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
;
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? */
1054 result
= build_dbinstance(NULL
, NULL
, NULL
, argv
[7],
1055 argv
[8], NULL
, &dbi
,
1059 result
= build_dbinstance(NULL
, NULL
, argv
[9],
1061 NULL
, &dbi
, ldap
->log
);
1064 result
= build_dbinstance(argv
[10], NULL
, argv
[9],
1066 NULL
, &dbi
, ldap
->log
);
1069 result
= build_dbinstance(argv
[10], argv
[11],
1071 argv
[8], NULL
, &dbi
,
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.");
1091 /* when multithreaded, build a list of DBI's */
1092 DLZ_LINK_INIT(dbi
, link
);
1093 DLZ_LIST_APPEND(*(ldap
->db
), dbi
, link
);
1096 * when single threaded, hold onto the one connection
1101 /* attempt to connect */
1102 result
= ldap_connect(ldap
, dbi
);
1105 * if db connection cannot be created, log err msg and
1109 /* success, do nothing */
1113 * no memory means ldap_init could not
1116 case ISC_R_NOMEMORY
:
1118 ldap
->log(ISC_LOG_ERROR
,
1119 "LDAP driver could not allocate memory "
1120 "for connection number %u", i
+ 1);
1122 ldap
->log(ISC_LOG_ERROR
,
1123 "LDAP driver could not allocate memory "
1128 * no perm means ldap_set_option could not set
1132 ldap
->log(ISC_LOG_ERROR
,
1133 "LDAP driver could not "
1134 "set protocol version.");
1135 result
= ISC_R_FAILURE
;
1137 /* failure means couldn't connect to ldap server */
1140 ldap
->log(ISC_LOG_ERROR
,
1141 "LDAP driver could not bind "
1142 "connection number %u to server.", i
+ 1);
1144 ldap
->log(ISC_LOG_ERROR
,
1145 "LDAP driver could not "
1146 "bind connection to server.");
1150 * default should never happen. If it does,
1154 ldap
->log(ISC_LOG_ERROR
,
1155 "dlz_create() failed (%d)", result
);
1156 result
= ISC_R_UNEXPECTED
;
1161 /* set DBI = null for next loop through. */
1164 #endif /* PTHREADS */
1166 /* set dbdata to the ldap_instance we created. */
1169 return (ISC_R_SUCCESS
);
1178 dlz_destroy(void *dbdata
) {
1179 if (dbdata
!= NULL
) {
1180 ldap_instance_t
*db
= (ldap_instance_t
*)dbdata
;
1182 /* cleanup the list of DBI's */
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
)
1195 if (db
->user
!= NULL
)
1197 if (db
->cred
!= NULL
)
1204 * Return the version of the API
1207 dlz_version(unsigned int *flags
) {
1208 *flags
|= DNS_SDLZFLAG_RELATIVERDATA
;
1210 *flags
|= DNS_SDLZFLAG_THREADSAFE
;
1212 *flags
&= ~DNS_SDLZFLAG_THREADSAFE
;
1214 return (DLZ_DLOPEN_VERSION
);
1218 * Register a helper function from the bind9 dlz_dlopen driver
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
;