1/* dlopen.c--Unix dlopen() dynamic loader interface 2 * Rob Siemborski 3 * Rob Earhart 4 * $Id: dlopen.c,v 1.7 2005/05/17 21:56:43 snsimon Exp $ 5 */ 6/* 7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The name "Carnegie Mellon University" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For permission or any other legal 24 * details, please contact 25 * Office of Technology Transfer 26 * Carnegie Mellon University 27 * 5000 Forbes Avenue 28 * Pittsburgh, PA 15213-3890 29 * (412) 268-4387, fax: (412) 268-7395 30 * tech-transfer@andrew.cmu.edu 31 * 32 * 4. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by Computing Services 35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 36 * 37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46#include <config.h> 47#ifdef HAVE_DLFCN_H 48#include <dlfcn.h> 49#endif 50 51#include <stdlib.h> 52#include <errno.h> 53#include <stdio.h> 54#include <limits.h> 55 56#include <sasl.h> 57#include "saslint.h" 58 59#ifndef PIC 60#include <saslplug.h> 61#include "staticopen.h" 62#endif 63 64#ifdef DO_DLOPEN 65#if HAVE_DIRENT_H 66# include <dirent.h> 67# define NAMLEN(dirent) strlen((dirent)->d_name) 68#else /* HAVE_DIRENT_H */ 69# define dirent direct 70# define NAMLEN(dirent) (dirent)->d_namlen 71# if HAVE_SYS_NDIR_H 72# include <sys/ndir.h> 73# endif 74# if HAVE_SYS_DIR_H 75# include <sys/dir.h> 76# endif 77# if HAVE_NDIR_H 78# include <ndir.h> 79# endif 80#endif /* ! HAVE_DIRENT_H */ 81 82#ifndef NAME_MAX 83# ifdef _POSIX_NAME_MAX 84# define NAME_MAX _POSIX_NAME_MAX 85# else 86# define NAME_MAX 16 87# endif 88#endif 89 90#if NAME_MAX < 8 91# define NAME_MAX 8 92#endif 93 94#ifdef __hpux 95#ifndef HAVE_DLFCN_H 96#include <dl.h> 97 98typedef shl_t * dll_handle; 99typedef void * dll_func; 100 101dll_handle 102dlopen(char *fname, int mode) 103{ 104 shl_t h = shl_load(fname, BIND_DEFERRED, 0L); 105 shl_t *hp = NULL; 106 107 if (h) { 108 hp = (shl_t *)malloc(sizeof (shl_t)); 109 if (!hp) { 110 shl_unload(h); 111 } else { 112 *hp = h; 113 } 114 } 115 116 return (dll_handle)hp; 117} 118 119int 120dlclose(dll_handle hp) 121{ 122 shl_t h; 123 124 if (hp != NULL) { 125 h = *((shl_t *)hp); 126 free(hp); 127 return shl_unload(h); 128 } else { 129 /* Return error */ 130 return -1; 131 } 132} 133 134dll_func 135dlsym(dll_handle h, char *n) 136{ 137 dll_func handle; 138 139 if (shl_findsym ((shl_t *)h, n, TYPE_PROCEDURE, &handle)) 140 return NULL; 141 142 return (dll_func)handle; 143} 144 145char *dlerror() 146{ 147 if (errno != 0) { 148 return strerror(errno); 149 } 150 return "Generic shared library error"; 151} 152 153#endif /* HAVE_DLFCN_H */ 154 155#ifdef __ia64 156#define SO_SUFFIX ".so" 157#else 158#define SO_SUFFIX ".sl" 159#endif /* __ia64 */ 160#elif defined(__APPLE__) 161/* APPLE: support both .plugin and .so */ 162#define PLUGIN_SUFFIX ".plugin" 163#define SO_SUFFIX ".so" 164#else /* __APPLE__ */ 165#define SO_SUFFIX ".so" 166#endif 167 168#define LA_SUFFIX ".la" 169 170typedef struct lib_list 171{ 172 struct lib_list *next; 173 void *library; 174} lib_list_t; 175 176static lib_list_t *lib_list_head = NULL; 177 178#endif /* DO_DLOPEN */ 179 180int _sasl_locate_entry(void *library, const char *entryname, 181 void **entry_point) 182{ 183#ifdef DO_DLOPEN 184/* note that we still check for known problem systems in 185 * case we are cross-compiling */ 186#if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__)) 187 char adj_entryname[1024]; 188#else 189#define adj_entryname entryname 190#endif 191 192 if(!entryname) { 193 _sasl_log(NULL, SASL_LOG_ERR, 194 "no entryname in _sasl_locate_entry"); 195 return SASL_BADPARAM; 196 } 197 198 if(!library) { 199 _sasl_log(NULL, SASL_LOG_ERR, 200 "no library in _sasl_locate_entry"); 201 return SASL_BADPARAM; 202 } 203 204 if(!entry_point) { 205 _sasl_log(NULL, SASL_LOG_ERR, 206 "no entrypoint output pointer in _sasl_locate_entry"); 207 return SASL_BADPARAM; 208 } 209 210#if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__)) 211 snprintf(adj_entryname, sizeof adj_entryname, "_%s", entryname); 212#endif 213 214 *entry_point = NULL; 215 *entry_point = dlsym(library, adj_entryname); 216 if (*entry_point == NULL) { 217#if 0 /* This message appears to confuse people */ 218 _sasl_log(NULL, SASL_LOG_DEBUG, 219 "unable to get entry point %s: %s", adj_entryname, 220 dlerror()); 221#endif 222 return SASL_FAIL; 223 } 224 225 return SASL_OK; 226#else 227 return SASL_FAIL; 228#endif /* DO_DLOPEN */ 229} 230 231#ifdef DO_DLOPEN 232 233static int _sasl_plugin_load(char *plugin, void *library, 234 const char *entryname, 235 int (*add_plugin)(const char *, void *)) 236{ 237 void *entry_point; 238 int result; 239 240 result = _sasl_locate_entry(library, entryname, &entry_point); 241 if(result == SASL_OK) { 242 result = add_plugin(plugin, entry_point); 243 if(result != SASL_OK) 244 _sasl_log(NULL, SASL_LOG_DEBUG, 245 "_sasl_plugin_load failed on %s for plugin: %s\n", 246 entryname, plugin); 247 } 248 249 return result; 250} 251 252/* this returns the file to actually open. 253 * out should be a buffer of size PATH_MAX 254 * and may be the same as in. */ 255 256/* We'll use a static buffer for speed unless someone complains */ 257#define MAX_LINE 2048 258 259static int _parse_la(const char *prefix, const char *in, char *out) 260{ 261 FILE *file; 262 size_t length; 263 int has64bit = 1; /* APPLE */ 264 char line[MAX_LINE]; 265 char *ntmp = NULL; 266 267 if(!in || !out || !prefix || out == in) return SASL_BADPARAM; 268 269 /* Set this so we can detect failure */ 270 *out = '\0'; 271 272 length = strlen(in); 273 274 if (strcmp(in + (length - strlen(LA_SUFFIX)), LA_SUFFIX)) { 275 if(!strcmp(in + (length - strlen(SO_SUFFIX)),SO_SUFFIX)) { 276 /* check for a .la file */ 277 strcpy(line, prefix); 278 strcat(line, in); 279 length = strlen(line); 280 *(line + (length - strlen(SO_SUFFIX))) = '\0'; 281 strcat(line, LA_SUFFIX); 282 file = fopen(line, "r"); 283 if(file) { 284 /* We'll get it on the .la open */ 285 fclose(file); 286 return SASL_FAIL; 287 } 288 } 289 strcpy(out, prefix); 290 strcat(out, in); 291 return SASL_OK; 292 } 293 294 strcpy(line, prefix); 295 strcat(line, in); 296 297 file = fopen(line, "r"); 298 if(!file) { 299 _sasl_log(NULL, SASL_LOG_WARN, 300 "unable to open LA file: %s", line); 301 return SASL_FAIL; 302 } 303 304 while(!feof(file)) { 305 if(!fgets(line, MAX_LINE, file)) break; 306 if(line[strlen(line) - 1] != '\n') { 307 _sasl_log(NULL, SASL_LOG_WARN, 308 "LA file has too long of a line: %s", in); 309 return SASL_BUFOVER; 310 } 311 if(line[0] == '\n' || line[0] == '#') continue; 312 if(!strncmp(line, "dlname=", sizeof("dlname=") - 1)) { 313 /* We found the line with the name in it */ 314 char *end; 315 char *start; 316 size_t len; 317 end = strrchr(line, '\''); 318 if(!end) continue; 319 start = &line[sizeof("dlname=")-1]; 320 len = strlen(start); 321 if(len > 3 && start[0] == '\'') { 322 ntmp=&start[1]; 323 *end='\0'; 324 /* Do we have dlname="" ? */ 325 if(ntmp == end) { 326 _sasl_log(NULL, SASL_LOG_DEBUG, 327 "dlname is empty in .la file: %s", in); 328 return SASL_FAIL; 329 } 330 strcpy(out, prefix); 331 strcat(out, ntmp); 332 } 333#if !__LP64__ 334 break; 335#endif 336 } 337#if __LP64__ 338 else 339 if (!strncmp(line, "64bit=", sizeof("64bit=") - 1)) { 340 if (strstr(line, "no") != NULL) { 341 has64bit = 0; 342 } 343 } 344#endif 345 } 346 if(ferror(file) || *out == '\0') { 347 _sasl_log(NULL, SASL_LOG_WARN, 348 "Error reading .la: %s\n", in); 349 fclose(file); 350 return SASL_FAIL; 351 } 352 fclose(file); 353 354#if __LP64__ 355 if (has64bit == 0) 356 return SASL_FAIL; 357#endif 358 359 if(!(*out)) { 360 _sasl_log(NULL, SASL_LOG_WARN, 361 "Could not find a dlname line in .la file: %s", in); 362 return SASL_FAIL; 363 } 364 365 return SASL_OK; 366} 367#endif /* DO_DLOPEN */ 368 369/* loads a plugin library */ 370int _sasl_get_plugin(const char *file, 371 const sasl_callback_t *verifyfile_cb, 372 void **libraryptr) 373{ 374#ifdef DO_DLOPEN 375 int r = 0; 376 int flag; 377 void *library; 378 lib_list_t *newhead; 379 380 r = ((sasl_verifyfile_t *)(verifyfile_cb->proc)) 381 (verifyfile_cb->context, file, SASL_VRFY_PLUGIN); 382 if (r != SASL_OK) return r; 383 384#ifdef RTLD_NOW 385 flag = RTLD_NOW; 386#else 387 flag = 0; 388#endif 389 390 newhead = sasl_ALLOC(sizeof(lib_list_t)); 391 if(!newhead) return SASL_NOMEM; 392 393 if (!(library = dlopen(file, flag))) { 394 _sasl_log(NULL, SASL_LOG_ERR, 395 "unable to dlopen %s: %s", file, dlerror()); 396 sasl_FREE(newhead); 397 return SASL_FAIL; 398 } 399 400 newhead->library = library; 401 newhead->next = lib_list_head; 402 lib_list_head = newhead; 403 404 *libraryptr = library; 405 return SASL_OK; 406#else 407 return SASL_FAIL; 408#endif /* DO_DLOPEN */ 409} 410 411/* gets the list of mechanisms */ 412int _sasl_load_plugins(const add_plugin_list_t *entrypoints, 413 const sasl_callback_t *getpath_cb, 414 const sasl_callback_t *verifyfile_cb) 415{ 416 int result; 417 const add_plugin_list_t *cur_ep; 418#ifdef DO_DLOPEN 419 char str[PATH_MAX], tmp[PATH_MAX+2], prefix[PATH_MAX+2]; 420 /* 1 for '/' 1 for trailing '\0' */ 421 char c; 422 int pos; 423 const char *path=NULL; 424 int position; 425 DIR *dp; 426 struct dirent *dir; 427#endif 428#ifndef PIC 429 add_plugin_t *add_plugin; 430 _sasl_plug_type type; 431 _sasl_plug_rec *p; 432#endif 433 434 if (! entrypoints 435 || ! getpath_cb 436 || getpath_cb->id != SASL_CB_GETPATH 437 || ! getpath_cb->proc 438 || ! verifyfile_cb 439 || verifyfile_cb->id != SASL_CB_VERIFYFILE 440 || ! verifyfile_cb->proc) 441 return SASL_BADPARAM; 442 443#ifndef PIC 444 /* do all the static plugins first */ 445 446 for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { 447 448 /* What type of plugin are we looking for? */ 449 if(!strcmp(cur_ep->entryname, "sasl_server_plug_init")) { 450 type = SERVER; 451 add_plugin = (add_plugin_t *)sasl_server_add_plugin; 452 } else if (!strcmp(cur_ep->entryname, "sasl_client_plug_init")) { 453 type = CLIENT; 454 add_plugin = (add_plugin_t *)sasl_client_add_plugin; 455 } else if (!strcmp(cur_ep->entryname, "sasl_auxprop_plug_init")) { 456 type = AUXPROP; 457 add_plugin = (add_plugin_t *)sasl_auxprop_add_plugin; 458 } else if (!strcmp(cur_ep->entryname, "sasl_canonuser_init")) { 459 type = CANONUSER; 460 add_plugin = (add_plugin_t *)sasl_canonuser_add_plugin; 461 } else { 462 /* What are we looking for then? */ 463 return SASL_FAIL; 464 } 465 for (p=_sasl_static_plugins; p->type; p++) { 466 if(type == p->type) 467 result = add_plugin(p->name, p->plug); 468 } 469 } 470#endif /* !PIC */ 471 472/* only do the following if: 473 * 474 * we support dlopen() 475 * AND we are not staticly compiled 476 * OR we are staticly compiled and TRY_DLOPEN_WHEN_STATIC is defined 477 */ 478#if defined(DO_DLOPEN) && (defined(PIC) || (!defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) 479 /* get the path to the plugins */ 480 result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context, 481 &path); 482 if (result != SASL_OK) return result; 483 if (! path) return SASL_FAIL; 484 485 if (strlen(path) >= PATH_MAX) { /* no you can't buffer overrun */ 486 return SASL_FAIL; 487 } 488 489 position=0; 490 do { 491 pos=0; 492 do { 493 c=path[position]; 494 position++; 495 str[pos]=c; 496 pos++; 497 } while ((c!=':') && (c!='=') && (c!=0)); 498 str[pos-1]='\0'; 499 500 strcpy(prefix,str); 501 strcat(prefix,"/"); 502 503 if ((dp=opendir(str)) !=NULL) /* ignore errors */ 504 { 505 while ((dir=readdir(dp)) != NULL) 506 { 507 size_t length; 508 void *library; 509 char *c; 510 char plugname[PATH_MAX]; 511 char name[PATH_MAX]; 512 513 length = NAMLEN(dir); 514 if (length < 4) 515 continue; /* can not possibly be what we're looking for */ 516 517 if (length + pos>=PATH_MAX) continue; /* too big */ 518 519 if (strcmp(dir->d_name + (length - strlen(SO_SUFFIX)), 520 SO_SUFFIX) 521#ifdef PLUGIN_SUFFIX 522 && strcmp(dir->d_name + (length - strlen(PLUGIN_SUFFIX)), 523 PLUGIN_SUFFIX) 524#endif 525 && strcmp(dir->d_name + (length - strlen(LA_SUFFIX)), 526 LA_SUFFIX)) 527 continue; 528 529 memcpy(name,dir->d_name,length); 530 name[length]='\0'; 531 532 result = _parse_la(prefix, name, tmp); 533 if(result != SASL_OK) 534 continue; 535 536 /* skip "lib" and cut off suffix -- 537 this only need be approximate */ 538 /* APPLE: strlcpy */ 539 strlcpy(plugname, (strncmp(name, "lib", 3) == 0) ? (name + 3) : name, sizeof(plugname)); 540 c = strchr(plugname, (int)'.'); 541 if(c) *c = '\0'; 542 543 result = _sasl_get_plugin(tmp, verifyfile_cb, &library); 544 545 if(result != SASL_OK) 546 continue; 547 548 for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { 549 _sasl_plugin_load(plugname, library, cur_ep->entryname, 550 cur_ep->add_plugin); 551 /* If this fails, it's not the end of the world */ 552 } 553 } 554 555 closedir(dp); 556 } else { 557 _sasl_log(NULL, SASL_LOG_DEBUG, 558 "looking for plugins in '%s', failed to open directory, error: %s", 559 str, 560 strerror(errno)); 561 } 562 563 } while ((c!='=') && (c!=0)); 564#endif /* defined(DO_DLOPEN) && (!defined(PIC) || (defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) */ 565 566 return SASL_OK; 567} 568 569/* APPLE: gets the list of mechanisms */ 570int _sasl_load_plugins_alt(const add_plugin_list_t *entrypoints, 571 const sasl_callback_t *getpath_cb, 572 const sasl_callback_t *verifyfile_cb) 573{ 574 int result; 575 int has_auxprop = 0; 576 const add_plugin_list_t *cur_ep; 577#ifdef DO_DLOPEN 578 char str[PATH_MAX], tmp[PATH_MAX+2], prefix[PATH_MAX+2]; 579 /* 1 for '/' 1 for trailing '\0' */ 580 char c; 581 int pos; 582 const char *path=NULL; 583 int position; 584 DIR *dp; 585 struct dirent *dir; 586#endif 587#ifndef PIC 588 add_plugin_t *add_plugin; 589 _sasl_plug_type type; 590 _sasl_plug_rec *p; 591#endif 592 593 if (! entrypoints 594 || ! getpath_cb 595 || getpath_cb->id != SASL_CB_GETPATH 596 || ! getpath_cb->proc 597 || ! verifyfile_cb 598 || verifyfile_cb->id != SASL_CB_VERIFYFILE 599 || ! verifyfile_cb->proc) 600 return SASL_BADPARAM; 601 602#ifndef PIC 603 /* do all the static plugins first */ 604 605 for (cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { 606 /* What type of plugin are we looking for? */ 607 if(!strcmp(cur_ep->entryname, "sasl_server_plug_init")) { 608 type = SERVER; 609 add_plugin = (add_plugin_t *)sasl_server_add_plugin; 610 } else if (!strcmp(cur_ep->entryname, "sasl_client_plug_init")) { 611 type = CLIENT; 612 add_plugin = (add_plugin_t *)sasl_client_add_plugin; 613 } else if (!strcmp(cur_ep->entryname, "sasl_auxprop_plug_init")) { 614 type = AUXPROP; 615 add_plugin = (add_plugin_t *)sasl_auxprop_add_plugin_nolog; 616 } else if (!strcmp(cur_ep->entryname, "sasl_canonuser_init")) { 617 type = CANONUSER; 618 add_plugin = (add_plugin_t *)sasl_canonuser_add_plugin; 619 } else { 620 /* What are we looking for then? */ 621 return SASL_FAIL; 622 } 623 for (p = _sasl_static_plugins; p->type; p++) { 624 if (type == p->type) { 625 result = add_plugin(p->name, p->plug); 626 if (result == SASL_OK && type == AUXPROP) { 627 has_auxprop = 1; 628 } 629 } 630 } 631 } 632#endif /* !PIC */ 633 634/* only do the following if: 635 * 636 * we support dlopen() 637 * AND we are not staticly compiled 638 * OR we are staticly compiled and TRY_DLOPEN_WHEN_STATIC is defined 639 */ 640#if defined(DO_DLOPEN) && (defined(PIC) || (!defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) 641 /* get the path to the plugins */ 642 result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context, 643 &path); 644 if (result != SASL_OK) return result; 645 if (! path) return SASL_FAIL; 646 647 if (strlen(path) >= PATH_MAX) { /* no you can't buffer overrun */ 648 return SASL_FAIL; 649 } 650 651 position=0; 652 do { 653 pos=0; 654 do { 655 c=path[position]; 656 position++; 657 str[pos]=c; 658 pos++; 659 } while ((c!=':') && (c!='=') && (c!=0)); 660 str[pos-1]='\0'; 661 662 strcpy(prefix,str); 663 strcat(prefix,"/"); 664 665 if ((dp=opendir(str)) !=NULL) /* ignore errors */ 666 { 667 while ((dir=readdir(dp)) != NULL) 668 { 669 size_t length; 670 void *library; 671 char *c; 672 char plugname[PATH_MAX]; 673 char name[PATH_MAX]; 674 675 length = NAMLEN(dir); 676 if (length < 4) 677 continue; /* can not possibly be what we're looking for */ 678 679 if (length + pos>=PATH_MAX) continue; /* too big */ 680 681 if (strcmp(dir->d_name + (length - strlen(SO_SUFFIX)), 682 SO_SUFFIX) 683 && strcmp(dir->d_name + (length - strlen(LA_SUFFIX)), 684 LA_SUFFIX)) 685 continue; 686 687 memcpy(name,dir->d_name,length); 688 name[length]='\0'; 689 690 result = _parse_la(prefix, name, tmp); 691 if(result != SASL_OK) 692 continue; 693 694 /* skip "lib" and cut off suffix -- 695 this only need be approximate */ 696 strlcpy(plugname, (strncmp(name, "lib", 3) == 0) ? (name + 3) : name, sizeof(plugname)); 697 c = strchr(plugname, (int)'.'); 698 if(c) *c = '\0'; 699 700 result = _sasl_get_plugin(tmp, verifyfile_cb, &library); 701 702 if(result != SASL_OK) 703 continue; 704 705 for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) { 706 /* If this fails, it's not the end of the world */ 707 result = _sasl_plugin_load(plugname, library, cur_ep->entryname, 708 cur_ep->add_plugin); 709 if (result == SASL_OK && strcmp(cur_ep->entryname, "sasl_auxprop_plug_init") == 0) 710 has_auxprop = 1; 711 } 712 } 713 714 closedir(dp); 715 } 716 } while ((c!='=') && (c!=0)); 717#endif /* defined(DO_DLOPEN) && (!defined(PIC) || (defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) */ 718 719 return has_auxprop ? SASL_OK : SASL_NOMECH; 720} 721 722int 723_sasl_done_with_plugins(void) 724{ 725#ifdef DO_DLOPEN 726 lib_list_t *libptr, *libptr_next; 727 728 for(libptr = lib_list_head; libptr; libptr = libptr_next) { 729 libptr_next = libptr->next; 730 if(libptr->library) 731 dlclose(libptr->library); 732 sasl_FREE(libptr); 733 } 734 735 lib_list_head = NULL; 736#endif /* DO_DLOPEN */ 737 return SASL_OK; 738} 739