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