1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <synch.h> 33 #include <time.h> 34 #include <libintl.h> 35 #include <thread.h> 36 #include <syslog.h> 37 #include <sys/mman.h> 38 #include <nsswitch.h> 39 #include <nss_dbdefs.h> 40 #include "solaris-priv.h" 41 #include "solaris-int.h" 42 #include "ns_sldap.h" 43 #include "ns_internal.h" 44 #include "ns_cache_door.h" 45 #include "ldappr.h" 46 #include <sys/stat.h> 47 #include <fcntl.h> 48 #include <procfs.h> 49 #include <unistd.h> 50 51 extern unsigned int _sleep(unsigned int); 52 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *, 53 LDAPControl **, LDAPControl **); 54 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip); 55 56 static int openConnection(LDAP **, const char *, const ns_cred_t *, 57 int, ns_ldap_error_t **, int, int); 58 /* 59 * sessionLock, wait4session, sessionTid 60 * are variables to synchronize the creation/retrieval of a connection. 61 * MTperCon is a flag to enable/disable multiple threads sharing the same 62 * connection. 63 * sessionPoolLock is a mutex lock for the connection pool. 64 * sharedConnNumber is the number of sharable connections in the pool. 65 * sharedConnNumberLock is a mutex for sharedConnNumber. 66 */ 67 static mutex_t sessionLock = DEFAULTMUTEX; 68 static int wait4session = 0; 69 static thread_t sessionTid = 0; 70 int MTperConn = 1; 71 static rwlock_t sessionPoolLock = DEFAULTRWLOCK; 72 73 static Connection **sessionPool = NULL; 74 static int sessionPoolSize = 0; 75 static int sharedConnNumber = 0; 76 static mutex_t sharedConnNumberLock = DEFAULTMUTEX; 77 78 79 static mutex_t nscdLock = DEFAULTMUTEX; 80 static int nscdChecked = 0; 81 static pid_t checkedPid = -1; 82 static int isNscd = 0; 83 /* 84 * SSF values are for SASL integrity & privacy. 85 * JES DS5.2 does not support this feature but DS6 does. 86 * The values between 0 and 65535 can work with both server versions. 87 */ 88 #define MAX_SASL_SSF 65535 89 #define MIN_SASL_SSF 0 90 91 /* Number of hostnames to allocate memory for */ 92 #define NUMTOMALLOC 32 93 /* 94 * ns_mtckey is for sharing a ldap connection among multiple 95 * threads; created by ns_ldap_init() in ns_init.c 96 */ 97 extern thread_key_t ns_mtckey; 98 99 /* Per thread LDAP error resides in thread-specific data. */ 100 struct ldap_error { 101 int le_errno; 102 char *le_matched; 103 char *le_errmsg; 104 }; 105 106 static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL}; 107 108 /* destructor */ 109 void 110 ns_tsd_cleanup(void *key) { 111 struct ldap_error *le = (struct ldap_error *)key; 112 113 if (le == NULL) 114 return; 115 if (le->le_matched != NULL) { 116 ldap_memfree(le->le_matched); 117 } 118 if (le->le_errmsg != NULL) { 119 ldap_memfree(le->le_errmsg); 120 } 121 free(le); 122 } 123 124 /* Callback function for allocating a mutex */ 125 static void * 126 ns_mutex_alloc(void) 127 { 128 mutex_t *mutexp = NULL; 129 130 if ((mutexp = malloc(sizeof (mutex_t))) != NULL) { 131 if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) { 132 free(mutexp); 133 mutexp = NULL; 134 } 135 } 136 return (mutexp); 137 } 138 139 /* Callback function for freeing a mutex */ 140 static void 141 ns_mutex_free(void *mutexp) 142 { 143 (void) mutex_destroy((mutex_t *)mutexp); 144 free(mutexp); 145 } 146 147 /* 148 * Function for setting up thread-specific data 149 * where per thread LDAP error is stored 150 */ 151 static int 152 tsd_setup() 153 { 154 void *tsd; 155 int rc; 156 157 /* return success if TSD already set */ 158 rc = thr_getspecific(ns_mtckey, &tsd); 159 if (rc == 0 && tsd != NULL) 160 return (0); 161 162 /* allocate and set TSD */ 163 tsd = (void *) calloc(1, sizeof (struct ldap_error)); 164 if (tsd == NULL) 165 return (-1); 166 rc = thr_setspecific(ns_mtckey, tsd); 167 if (rc != 0) { /* must be ENOMEM */ 168 free(tsd); 169 return (-1); 170 } 171 return (0); 172 173 174 } 175 176 /* Callback function for setting the per thread LDAP error */ 177 /*ARGSUSED*/ 178 static void 179 set_ld_error(int err, char *matched, char *errmsg, void *dummy) 180 { 181 struct ldap_error *le; 182 183 if (thr_getspecific(ns_mtckey, (void **)&le) != 0) { 184 syslog(LOG_ERR, "set_ld_error: thr_getspecific failed. errno" 185 " %d", errno); 186 return; 187 } 188 189 /* play safe, do nothing if TSD pointer is NULL */ 190 if (le == NULL) 191 return; 192 193 le->le_errno = err; 194 if (le->le_matched != NULL) { 195 ldap_memfree(le->le_matched); 196 } 197 le->le_matched = matched; 198 if (le->le_errmsg != NULL) { 199 ldap_memfree(le->le_errmsg); 200 } 201 le->le_errmsg = errmsg; 202 } 203 204 int 205 /* check and allocate the thread-specific data for using a shared connection */ 206 __s_api_check_MTC_tsd() 207 { 208 if (tsd_setup() != 0) 209 return (NS_LDAP_MEMORY); 210 211 return (NS_LDAP_SUCCESS); 212 } 213 214 /* Callback function for getting the per thread LDAP error */ 215 /*ARGSUSED*/ 216 static int 217 get_ld_error(char **matched, char **errmsg, void *dummy) 218 { 219 struct ldap_error *le; 220 221 if (thr_getspecific(ns_mtckey, (void **)&le) != 0) { 222 syslog(LOG_ERR, "get_ld_error: thr_getspecific failed. errno" 223 " %d", errno); 224 return (errno); 225 } 226 227 /* play safe, return NULL error data, if TSD pointer is NULL */ 228 if (le == NULL) 229 le = &ldap_error_NULL; 230 231 if (matched != NULL) { 232 *matched = le->le_matched; 233 } 234 if (errmsg != NULL) { 235 *errmsg = le->le_errmsg; 236 } 237 return (le->le_errno); 238 } 239 240 /* Callback function for setting per thread errno */ 241 static void 242 set_errno(int err) 243 { 244 errno = err; 245 } 246 247 /* Callback function for getting per thread errno */ 248 static int 249 get_errno(void) 250 { 251 return (errno); 252 } 253 254 /* 255 * set up to allow multiple threads to use the same ldap connection 256 */ 257 static int 258 setup_mt_conn(LDAP *ld) 259 { 260 261 struct ldap_thread_fns tfns; 262 struct ldap_extra_thread_fns extrafns; 263 int rc; 264 265 /* 266 * Set the function pointers for dealing with mutexes 267 * and error information 268 */ 269 (void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns)); 270 tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc; 271 tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free; 272 tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock; 273 tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock; 274 tfns.ltf_get_errno = get_errno; 275 tfns.ltf_set_errno = set_errno; 276 tfns.ltf_get_lderrno = get_ld_error; 277 tfns.ltf_set_lderrno = set_ld_error; 278 tfns.ltf_lderrno_arg = NULL; 279 280 /* 281 * Set up this session to use those function pointers 282 */ 283 rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS, 284 (void *) &tfns); 285 if (rc < 0) { 286 syslog(LOG_WARNING, "libsldap: ldap_set_option " 287 "(LDAP_OPT_THREAD_FN_PTRS)"); 288 return (-1); 289 } 290 291 /* 292 * Set the function pointers for working with semaphores 293 */ 294 (void) memset(&extrafns, '\0', 295 sizeof (struct ldap_extra_thread_fns)); 296 extrafns.ltf_threadid_fn = (void * (*)(void))thr_self; 297 extrafns.ltf_mutex_trylock = NULL; 298 extrafns.ltf_sema_alloc = NULL; 299 extrafns.ltf_sema_free = NULL; 300 extrafns.ltf_sema_wait = NULL; 301 extrafns.ltf_sema_post = NULL; 302 303 /* Set up this session to use those function pointers */ 304 rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS, 305 (void *) &extrafns); 306 if (rc < 0) { 307 syslog(LOG_WARNING, "libsldap: ldap_set_option " 308 "(LDAP_OPT_EXTRA_THREAD_FN_PTRS)"); 309 return (-1); 310 } 311 312 return (0); 313 } 314 315 static void 316 ns_setup_mt_conn_and_tsd(LDAP *ld) { 317 thread_t t = thr_self(); 318 void *tsd; 319 /* set up to share this connection among threads */ 320 if (MTperConn == 1) { 321 if (tsd_setup() == -1) { 322 syslog(LOG_ERR, "tid= %d: unable " 323 "to set up TSD\n", t); 324 } else { 325 if (setup_mt_conn(ld) == -1) { 326 /* multiple threads per connection not supported */ 327 syslog(LOG_ERR, "tid= %d: multiple " 328 "threads per connection not " 329 "supported\n", t); 330 (void) thr_getspecific(ns_mtckey, &tsd); 331 ns_tsd_cleanup(tsd); 332 (void) thr_setspecific(ns_mtckey, NULL); 333 MTperConn = 0; 334 } 335 } 336 } 337 } 338 339 /* 340 * Check /proc/PID/psinfo to see if this process is nscd 341 * If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce 342 * constant reconnects for many operations. 343 * A more complete solution is to develop true connection pooling. 344 * However, this is much better than a new connection for every request. 345 */ 346 static int 347 nscd_proc() 348 { 349 pid_t my_pid; 350 psinfo_t pinfo; 351 char fname[BUFSIZ]; 352 int ret; 353 int fd; 354 355 /* Don't bother checking if this process isn't root. */ 356 /* It can't be nscd */ 357 if (getuid() != 0) 358 return (0); 359 360 my_pid = getpid(); 361 if (nscdChecked && (my_pid == checkedPid)) { 362 return (isNscd); 363 } 364 (void) mutex_lock(&nscdLock); 365 if (nscdChecked && (my_pid == checkedPid)) { 366 (void) mutex_unlock(&nscdLock); 367 return (isNscd); 368 } 369 nscdChecked = 1; 370 checkedPid = my_pid; 371 isNscd = 0; 372 if (snprintf(fname, BUFSIZ, "/proc/%d/psinfo", my_pid) != 0) { 373 if ((fd = open(fname, O_RDONLY)) > 0) { 374 ret = read(fd, &pinfo, sizeof (psinfo_t)); 375 (void) close(fd); 376 if (ret == sizeof (psinfo_t) && 377 (strcmp(pinfo.pr_fname, "nscd") == 0)) { 378 /* process runs as root and is named nscd */ 379 /* that's good enough for now */ 380 isNscd = 1; 381 } 382 } 383 } 384 (void) mutex_unlock(&nscdLock); 385 return (isNscd); 386 } 387 388 /* 389 * This function requests a server from the cache manager through 390 * the door functionality 391 */ 392 393 static int 394 __s_api_requestServer(const char *request, const char *server, 395 ns_server_info_t *ret, ns_ldap_error_t **error, const char *addrType) 396 { 397 union { 398 ldap_data_t s_d; 399 char s_b[DOORBUFFERSIZE]; 400 } space; 401 ldap_data_t *sptr; 402 int ndata; 403 int adata; 404 char errstr[MAXERROR]; 405 const char *ireq; 406 char *rbuf, *ptr, *rest; 407 char *dptr; 408 char **mptr, **mptr1, **cptr, **cptr1; 409 int mcnt, ccnt; 410 char **servers; 411 int rc, len; 412 413 if (ret == NULL || error == NULL) { 414 return (NS_LDAP_OP_FAILED); 415 } 416 (void) memset(ret, 0, sizeof (ns_server_info_t)); 417 *error = NULL; 418 419 (void) memset(space.s_b, 0, DOORBUFFERSIZE); 420 421 if (request == NULL) 422 ireq = NS_CACHE_NEW; 423 else 424 ireq = request; 425 426 adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1); 427 if (server != NULL) { 428 adata += strlen(DOORLINESEP) + 1; 429 adata += strlen(server) + 1; 430 } 431 ndata = sizeof (space); 432 len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber); 433 space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER; 434 if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len) 435 return (NS_LDAP_MEMORY); 436 if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >= 437 len) 438 return (NS_LDAP_MEMORY); 439 if (server != NULL) { 440 if (strlcat(space.s_d.ldap_call.ldap_u.domainname, 441 DOORLINESEP, len) >= len) 442 return (NS_LDAP_MEMORY); 443 if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, 444 len) >= len) 445 return (NS_LDAP_MEMORY); 446 } 447 sptr = &space.s_d; 448 449 switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) { 450 case SUCCESS: 451 break; 452 /* this case is for when the $mgr is not running, but ldapclient */ 453 /* is trying to initialize things */ 454 case NOSERVER: 455 /* get first server from config list unavailable otherwise */ 456 servers = NULL; 457 rc = __s_api_getServers(&servers, error); 458 if (rc != NS_LDAP_SUCCESS) { 459 if (servers != NULL) { 460 __s_api_free2dArray(servers); 461 servers = NULL; 462 } 463 return (rc); 464 } 465 if (servers == NULL || servers[0] == NULL) { 466 __s_api_free2dArray(servers); 467 servers = NULL; 468 (void) sprintf(errstr, 469 gettext("No server found in configuration")); 470 MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT, 471 strdup(errstr), NULL); 472 return (NS_LDAP_CONFIG); 473 } 474 ret->server = strdup(servers[0]); 475 if (ret->server == NULL) { 476 __s_api_free2dArray(servers); 477 return (NS_LDAP_MEMORY); 478 } 479 ret->saslMechanisms = NULL; 480 ret->controls = NULL; 481 __s_api_free2dArray(servers); 482 servers = NULL; 483 return (NS_LDAP_SUCCESS); 484 case NOTFOUND: 485 default: 486 return (NS_LDAP_OP_FAILED); 487 } 488 489 /* copy info from door call return structure here */ 490 rbuf = space.s_d.ldap_ret.ldap_u.config; 491 492 /* Get the host */ 493 ptr = strtok_r(rbuf, DOORLINESEP, &rest); 494 if (ptr == NULL) { 495 (void) sprintf(errstr, gettext("No server returned from " 496 "ldap_cachemgr")); 497 MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR, 498 strdup(errstr), NULL); 499 return (NS_LDAP_OP_FAILED); 500 } 501 ret->server = strdup(ptr); 502 if (ret->server == NULL) { 503 return (NS_LDAP_MEMORY); 504 } 505 506 /* get the Supported Controls/SASL mechs */ 507 mptr = NULL; 508 mcnt = 0; 509 cptr = NULL; 510 ccnt = 0; 511 for (; ; ) { 512 ptr = strtok_r(NULL, DOORLINESEP, &rest); 513 if (ptr == NULL) 514 break; 515 if (strncasecmp(ptr, _SASLMECHANISM, 516 _SASLMECHANISM_LEN) == 0) { 517 dptr = strchr(ptr, '='); 518 if (dptr == NULL) 519 continue; 520 dptr++; 521 mptr1 = (char **)realloc((void *)mptr, 522 sizeof (char *) * (mcnt+2)); 523 if (mptr1 == NULL) { 524 __s_api_free2dArray(mptr); 525 if (sptr != &space.s_d) { 526 (void) munmap((char *)sptr, ndata); 527 } 528 __s_api_free2dArray(cptr); 529 free(ret->server); 530 ret->server = NULL; 531 return (NS_LDAP_MEMORY); 532 } 533 mptr = mptr1; 534 mptr[mcnt] = strdup(dptr); 535 if (mptr[mcnt] == NULL) { 536 if (sptr != &space.s_d) { 537 (void) munmap((char *)sptr, ndata); 538 } 539 __s_api_free2dArray(cptr); 540 cptr = NULL; 541 __s_api_free2dArray(mptr); 542 mptr = NULL; 543 free(ret->server); 544 ret->server = NULL; 545 return (NS_LDAP_MEMORY); 546 } 547 mcnt++; 548 mptr[mcnt] = NULL; 549 } 550 if (strncasecmp(ptr, _SUPPORTEDCONTROL, 551 _SUPPORTEDCONTROL_LEN) == 0) { 552 dptr = strchr(ptr, '='); 553 if (dptr == NULL) 554 continue; 555 dptr++; 556 cptr1 = (char **)realloc((void *)cptr, 557 sizeof (char *) * (ccnt+2)); 558 if (cptr1 == NULL) { 559 if (sptr != &space.s_d) { 560 (void) munmap((char *)sptr, ndata); 561 } 562 __s_api_free2dArray(cptr); 563 __s_api_free2dArray(mptr); 564 mptr = NULL; 565 free(ret->server); 566 ret->server = NULL; 567 return (NS_LDAP_MEMORY); 568 } 569 cptr = cptr1; 570 cptr[ccnt] = strdup(dptr); 571 if (cptr[ccnt] == NULL) { 572 if (sptr != &space.s_d) { 573 (void) munmap((char *)sptr, ndata); 574 } 575 __s_api_free2dArray(cptr); 576 cptr = NULL; 577 __s_api_free2dArray(mptr); 578 mptr = NULL; 579 free(ret->server); 580 ret->server = NULL; 581 return (NS_LDAP_MEMORY); 582 } 583 ccnt++; 584 cptr[ccnt] = NULL; 585 } 586 } 587 if (mptr != NULL) { 588 ret->saslMechanisms = mptr; 589 } 590 if (cptr != NULL) { 591 ret->controls = cptr; 592 } 593 594 595 /* clean up door call */ 596 if (sptr != &space.s_d) { 597 (void) munmap((char *)sptr, ndata); 598 } 599 *error = NULL; 600 601 return (NS_LDAP_SUCCESS); 602 } 603 604 605 /* 606 * printCred(): prints the credential structure 607 */ 608 static void 609 printCred(int pri, const ns_cred_t *cred) 610 { 611 thread_t t = thr_self(); 612 613 if (cred == NULL) { 614 syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t); 615 return; 616 } 617 618 syslog(pri, "tid= %d: AuthType=%d", t, cred->auth.type); 619 syslog(pri, "tid= %d: TlsType=%d", t, cred->auth.tlstype); 620 syslog(pri, "tid= %d: SaslMech=%d", t, cred->auth.saslmech); 621 syslog(pri, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt); 622 if (cred->hostcertpath) 623 syslog(pri, "tid= %d: hostCertPath=%s\n", 624 t, cred->hostcertpath); 625 if (cred->cred.unix_cred.userID) 626 syslog(pri, "tid= %d: userID=%s\n", 627 t, cred->cred.unix_cred.userID); 628 #ifdef DEBUG 629 if (cred->cred.unix_cred.passwd) 630 syslog(pri, "tid= %d: passwd=%s\n", 631 t, cred->cred.unix_cred.passwd); 632 #endif 633 } 634 635 /* 636 * printConnection(): prints the connection structure 637 */ 638 static void 639 printConnection(int pri, Connection *con) 640 { 641 thread_t t = thr_self(); 642 643 if (con == NULL) 644 return; 645 646 syslog(pri, "tid= %d: connectionID=%d\n", t, con->connectionId); 647 syslog(pri, "tid= %d: shared=%d\n", t, con->shared); 648 syslog(pri, "tid= %d: usedBit=%d\n", t, con->usedBit); 649 syslog(pri, "tid= %d: threadID=%d\n", t, con->threadID); 650 if (con->serverAddr) { 651 syslog(pri, "tid= %d: serverAddr=%s\n", 652 t, con->serverAddr); 653 } 654 printCred(pri, con->auth); 655 } 656 657 658 659 /* 660 * addConnection(): set up a connection so that it can be shared 661 * among multiple threads and then insert the connection in the 662 * connection list. 663 * Returns: -1 = failure, new Connection ID = success 664 * 665 * This function could exit with sessionLock locked. It will be 666 * be unlocked in __s_api_getConnection() when it exits without getting a 667 * connection. 668 */ 669 static int 670 addConnection(Connection *con) 671 { 672 int i, noMTperC = 0; 673 thread_t t = thr_self(); 674 struct ldap_thread_fns tfns; 675 void *tsd; 676 677 if (!con) 678 return (-1); 679 680 syslog(LOG_DEBUG, "tid= %d: Adding connection (serverAddr=%s)", 681 t, con->serverAddr); 682 683 if (MTperConn == 1) { 684 /* 685 * Make sure ld has proper thread functions and tsd 686 * is set up. 687 */ 688 (void) memset(&tfns, 0, sizeof (struct ldap_thread_fns)); 689 /* 690 * ldap_init sets ltf_get_lderrno and ltf_set_lderrno to NULLs. 691 * It's supposed to be overwritten by ns_setup_mt_conn_and_tsd. 692 */ 693 if (ldap_get_option(con->ld, LDAP_OPT_THREAD_FN_PTRS, 694 (void *)&tfns) != 0 || 695 tfns.ltf_get_lderrno != get_ld_error || 696 tfns.ltf_set_lderrno != set_ld_error) { 697 MTperConn = 0; 698 noMTperC = 1; 699 } else { 700 if (thr_getspecific(ns_mtckey, &tsd) != 0 || 701 tsd == NULL) 702 noMTperC = 1; 703 } 704 705 } else { 706 noMTperC = 1; 707 } 708 709 (void) rw_wrlock(&sessionPoolLock); 710 if (sessionPool == NULL) { 711 sessionPoolSize = SESSION_CACHE_INC; 712 sessionPool = calloc(sessionPoolSize, 713 sizeof (struct connection **)); 714 if (!sessionPool) { 715 (void) rw_unlock(&sessionPoolLock); 716 return (-1); 717 } 718 719 syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t); 720 } 721 for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i) 722 ; 723 if (i == sessionPoolSize) { 724 /* run out of array, need to increase sessionPool */ 725 Connection **cl; 726 cl = (Connection **) realloc(sessionPool, 727 (sessionPoolSize + SESSION_CACHE_INC) * 728 sizeof (Connection *)); 729 if (!cl) { 730 (void) rw_unlock(&sessionPoolLock); 731 return (-1); 732 } 733 (void) memset(cl + sessionPoolSize, 0, 734 SESSION_CACHE_INC * sizeof (struct connection *)); 735 sessionPool = cl; 736 sessionPoolSize += SESSION_CACHE_INC; 737 syslog(LOG_DEBUG, "tid: %d: Increased " 738 "sessionPoolSize to: %d\n", 739 t, sessionPoolSize); 740 } 741 sessionPool[i] = con; 742 if (noMTperC == 0) { 743 con->shared++; 744 con->pid = getpid(); 745 (void) mutex_lock(&sharedConnNumberLock); 746 sharedConnNumber++; 747 (void) mutex_unlock(&sharedConnNumberLock); 748 } else 749 con->usedBit = B_TRUE; 750 751 (void) rw_unlock(&sessionPoolLock); 752 753 con->connectionId = i + CONID_OFFSET; 754 755 syslog(LOG_DEBUG, "tid= %d: Connection added [%d]\n", 756 t, i); 757 printConnection(LOG_DEBUG, con); 758 759 /* 760 * A connection can be shared now, unlock 761 * the session mutex and let other 762 * threads try to use this connection or 763 * get their own. 764 */ 765 if (wait4session != 0 && sessionTid == thr_self()) { 766 wait4session = 0; 767 sessionTid = 0; 768 syslog(LOG_DEBUG, "tid= %d: unlocking sessionLock\n", t); 769 (void) mutex_unlock(&sessionLock); 770 } 771 772 return (i + CONID_OFFSET); 773 } 774 775 /* 776 * See if the specified session matches a currently available 777 */ 778 779 static int 780 findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, 781 Connection **conp) 782 { 783 Connection *cp; 784 int id; 785 786 if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET) 787 return (-1); 788 789 /* if a new connection is requested, no need to continue */ 790 if (flags & NS_LDAP_NEW_CONN) 791 return (-1); 792 793 *conp = NULL; 794 if (sessionPool == NULL) 795 return (-1); 796 id = cID - CONID_OFFSET; 797 if (id < 0 || id >= sessionPoolSize) 798 return (-1); 799 800 (void) rw_rdlock(&sessionPoolLock); 801 if (sessionPool[id] == NULL) { 802 (void) rw_unlock(&sessionPoolLock); 803 return (-1); 804 } 805 cp = sessionPool[id]; 806 807 /* 808 * Make sure the connection has the same type of authentication method 809 */ 810 if ((cp->usedBit) || 811 (cp->notAvail) || 812 (cp->auth->auth.type != auth->auth.type) || 813 (cp->auth->auth.tlstype != auth->auth.tlstype) || 814 (cp->auth->auth.saslmech != auth->auth.saslmech) || 815 (cp->auth->auth.saslopt != auth->auth.saslopt)) { 816 (void) rw_unlock(&sessionPoolLock); 817 return (-1); 818 } 819 if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) && 820 ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) || 821 (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) || 822 (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) && 823 ((cp->auth->cred.unix_cred.userID == NULL) || 824 (strcasecmp(cp->auth->cred.unix_cred.userID, 825 auth->cred.unix_cred.userID) != 0))) { 826 (void) rw_unlock(&sessionPoolLock); 827 return (-1); 828 } 829 830 /* An existing connection is found but it needs to be reset */ 831 if (flags & NS_LDAP_NEW_CONN) { 832 (void) rw_unlock(&sessionPoolLock); 833 DropConnection(cID, 0); 834 return (-1); 835 } 836 /* found an available connection */ 837 cp->usedBit = B_TRUE; 838 (void) rw_unlock(&sessionPoolLock); 839 cp->threadID = thr_self(); 840 *conp = cp; 841 return (cID); 842 } 843 844 /* 845 * findConnection(): find an available connection from the list 846 * that matches the criteria specified in Connection structure. 847 * If serverAddr is NULL, then find a connection to any server 848 * as long as it matches the rest of the parameters. 849 * Returns: -1 = failure, the Connection ID found = success. 850 * 851 * This function could exit with sessionLock locked. It will be 852 * be unlocked in addConnection() when this thread adds the connection 853 * to the pool or in __s_api_getConnection() when it exits without getting a 854 * connection. 855 */ 856 #define TRY_TIMES 10 857 static int 858 findConnection(int flags, const char *serverAddr, 859 const ns_cred_t *auth, Connection **conp) 860 { 861 Connection *cp; 862 int i; 863 int rc; 864 int try; 865 #ifdef DEBUG 866 thread_t t = thr_self(); 867 #endif /* DEBUG */ 868 869 if (auth == NULL || conp == NULL) 870 return (-1); 871 *conp = NULL; 872 873 /* if a new connection is requested, no need to continue */ 874 if (flags & NS_LDAP_NEW_CONN) 875 return (-1); 876 877 #ifdef DEBUG 878 (void) fprintf(stderr, "tid= %d: Find connection\n", t); 879 (void) fprintf(stderr, "tid= %d: Looking for ....\n", t); 880 if (serverAddr && *serverAddr) 881 (void) fprintf(stderr, "tid= %d: serverAddr=%s\n", 882 t, serverAddr); 883 else 884 (void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t); 885 printCred(stderr, auth); 886 fflush(stderr); 887 #endif /* DEBUG */ 888 889 /* 890 * If multiple threads per connection not supported, 891 * no sessionPool means no connection 892 */ 893 (void) rw_rdlock(&sessionPoolLock); 894 if (MTperConn == 0 && sessionPool == NULL) { 895 (void) rw_unlock(&sessionPoolLock); 896 return (-1); 897 } 898 899 /* 900 * If no sharable connections in cache, then serialize the opening 901 * of connections. Make sure only one is being opened 902 * at a time. Otherwise, we may end up with more 903 * connections than we want (if multiple threads get 904 * here at the same time) 905 */ 906 (void) mutex_lock(&sharedConnNumberLock); 907 if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) { 908 (void) mutex_unlock(&sharedConnNumberLock); 909 (void) rw_unlock(&sessionPoolLock); 910 (void) mutex_lock(&sessionLock); 911 (void) mutex_lock(&sharedConnNumberLock); 912 if (sessionPool == NULL || (sharedConnNumber == 0 && 913 MTperConn == 1)) { 914 (void) mutex_unlock(&sharedConnNumberLock); 915 wait4session = 1; 916 sessionTid = thr_self(); 917 #ifdef DEBUG 918 (void) fprintf(stderr, "tid= %d: get " 919 "connection ... \n", t); 920 fflush(stderr); 921 #endif /* DEBUG */ 922 /* 923 * Exit with sessionLock locked. It will be 924 * be unlocked in addConnection() when this 925 * thread adds the connection to the pool or 926 * in __s_api_getConnection() when it exits 927 * without getting a connection. 928 */ 929 return (-1); 930 } 931 932 #ifdef DEBUG 933 (void) fprintf(stderr, "tid= %d: shareable connections " 934 "exist\n", t); 935 fflush(stderr); 936 #endif /* DEBUG */ 937 (void) mutex_unlock(&sharedConnNumberLock); 938 /* 939 * There are sharable connections, check to see if 940 * one can be shared. 941 */ 942 (void) mutex_unlock(&sessionLock); 943 (void) rw_rdlock(&sessionPoolLock); 944 } else 945 (void) mutex_unlock(&sharedConnNumberLock); 946 947 try = 0; 948 check_again: 949 950 for (i = 0; i < sessionPoolSize; ++i) { 951 if (sessionPool[i] == NULL) 952 continue; 953 cp = sessionPool[i]; 954 #ifdef DEBUG 955 (void) fprintf(stderr, "tid= %d: checking connection " 956 "[%d] ....\n", t, i); 957 printConnection(stderr, cp); 958 #endif /* DEBUG */ 959 if ((cp->usedBit) || (cp->notAvail) || 960 (cp->auth->auth.type != auth->auth.type) || 961 (cp->auth->auth.tlstype != auth->auth.tlstype) || 962 (cp->auth->auth.saslmech != auth->auth.saslmech) || 963 (cp->auth->auth.saslopt != auth->auth.saslopt) || 964 (serverAddr && *serverAddr && 965 (strcasecmp(serverAddr, cp->serverAddr) != 0))) 966 continue; 967 if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) && 968 ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) || 969 (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) || 970 (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) && 971 ((cp->auth->cred.unix_cred.userID == NULL) || 972 (cp->auth->cred.unix_cred.passwd == NULL) || 973 ((strcasecmp(cp->auth->cred.unix_cred.userID, 974 auth->cred.unix_cred.userID) != 0)) || 975 ((strcmp(cp->auth->cred.unix_cred.passwd, 976 auth->cred.unix_cred.passwd) != 0)))) 977 continue; 978 /* found an available connection */ 979 if (MTperConn == 0) 980 cp->usedBit = B_TRUE; 981 else { 982 /* 983 * if connection was established in a different 984 * process, drop it and get a new one 985 */ 986 if (cp->pid != getpid()) { 987 (void) rw_unlock(&sessionPoolLock); 988 DropConnection(cp->connectionId, 989 NS_LDAP_NEW_CONN); 990 991 goto get_conn; 992 } 993 /* allocate TSD for per thread ldap error */ 994 rc = tsd_setup(); 995 996 /* if we got TSD, this connection is shared */ 997 if (rc != -1) 998 cp->shared++; 999 else if (cp->shared == 0) { 1000 cp->usedBit = B_TRUE; 1001 cp->threadID = thr_self(); 1002 (void) rw_unlock(&sessionPoolLock); 1003 return (-1); 1004 } 1005 } 1006 (void) rw_unlock(&sessionPoolLock); 1007 1008 *conp = cp; 1009 #ifdef DEBUG 1010 (void) fprintf(stderr, "tid= %d: Connection found " 1011 "cID=%d, shared =%d\n", t, i, cp->shared); 1012 fflush(stderr); 1013 #endif /* DEBUG */ 1014 return (i + CONID_OFFSET); 1015 } 1016 1017 get_conn: 1018 1019 (void) rw_unlock(&sessionPoolLock); 1020 1021 /* 1022 * If multiple threads per connection not supported, 1023 * we are done, just return -1 to tell the caller to 1024 * proceed with opening a connection 1025 */ 1026 if (MTperConn == 0) 1027 return (-1); 1028 1029 /* 1030 * No connection can be shared, test to see if 1031 * one is being opened. If trylock returns 1032 * EBUSY then it is, so wait until the opening 1033 * is done and try to see if the new connection 1034 * can be shared. 1035 */ 1036 rc = mutex_trylock(&sessionLock); 1037 if (rc == EBUSY) { 1038 (void) mutex_lock(&sessionLock); 1039 (void) mutex_unlock(&sessionLock); 1040 (void) rw_rdlock(&sessionPoolLock); 1041 #ifdef DEBUG 1042 (void) fprintf(stderr, "tid= %d: check session " 1043 "pool again\n", t); 1044 fflush(stderr); 1045 #endif /* DEBUG */ 1046 if (try < TRY_TIMES) { 1047 try++; 1048 goto check_again; 1049 } else { 1050 syslog(LOG_WARNING, "libsldap: mutex_trylock " 1051 "%d times. Stop.", TRY_TIMES); 1052 (void) rw_unlock(&sessionPoolLock); 1053 return (-1); 1054 } 1055 } else if (rc == 0) { 1056 /* 1057 * No connection can be shared, none being opened, 1058 * exit with sessionLock locked to open one. The 1059 * mutex will be unlocked in addConnection() when 1060 * this thread adds the new connection to the pool 1061 * or in __s_api_getConnection() when it exits 1062 * without getting a connection. 1063 */ 1064 wait4session = 1; 1065 sessionTid = thr_self(); 1066 #ifdef DEBUG 1067 (void) fprintf(stderr, "tid= %d: no connection found, " 1068 "none being opened, get connection ...\n", t); 1069 fflush(stderr); 1070 #endif /* DEBUG */ 1071 return (-1); 1072 } else { 1073 syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected " 1074 "error %d", rc); 1075 return (-1); 1076 } 1077 } 1078 1079 /* 1080 * Free a Connection structure 1081 */ 1082 static void 1083 freeConnection(Connection *con) 1084 { 1085 if (con == NULL) 1086 return; 1087 if (con->serverAddr) 1088 free(con->serverAddr); 1089 if (con->auth) 1090 (void) __ns_ldap_freeCred(&(con->auth)); 1091 if (con->saslMechanisms) { 1092 __s_api_free2dArray(con->saslMechanisms); 1093 } 1094 if (con->controls) { 1095 __s_api_free2dArray(con->controls); 1096 } 1097 free(con); 1098 } 1099 1100 /* 1101 * Find a connection matching the passed in criteria. If an open 1102 * connection with that criteria exists use it, otherwise open a 1103 * new connection. 1104 * Success: returns the pointer to the Connection structure 1105 * Failure: returns NULL, error code and message should be in errorp 1106 */ 1107 1108 static int 1109 makeConnection(Connection **conp, const char *serverAddr, 1110 const ns_cred_t *auth, ConnectionID *cID, int timeoutSec, 1111 ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd, 1112 int nopasswd_acct_mgmt, int flags, char ***badsrvrs) 1113 { 1114 Connection *con = NULL; 1115 ConnectionID id; 1116 char errmsg[MAXERROR]; 1117 int rc, exit_rc = NS_LDAP_SUCCESS; 1118 ns_server_info_t sinfo; 1119 char *hReq, *host = NULL; 1120 LDAP *ld = NULL; 1121 int passwd_mgmt = 0; 1122 int totalbad = 0; /* Number of servers contacted unsuccessfully */ 1123 short memerr = 0; /* Variable for tracking memory allocation */ 1124 char *serverAddrType = NULL; 1125 1126 1127 if (conp == NULL || errorp == NULL || auth == NULL) 1128 return (NS_LDAP_INVALID_PARAM); 1129 *errorp = NULL; 1130 *conp = NULL; 1131 sinfo.server = NULL; 1132 sinfo.controls = NULL; 1133 sinfo.saslMechanisms = NULL; 1134 1135 if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) { 1136 /* connection found in cache */ 1137 #ifdef DEBUG 1138 (void) fprintf(stderr, "tid= %d: connection found in " 1139 "cache %d\n", thr_self(), id); 1140 fflush(stderr); 1141 #endif /* DEBUG */ 1142 *cID = id; 1143 *conp = con; 1144 return (NS_LDAP_SUCCESS); 1145 } 1146 1147 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) 1148 serverAddrType = NS_CACHE_ADDR_HOSTNAME; 1149 else 1150 serverAddrType = NS_CACHE_ADDR_IP; 1151 1152 if (serverAddr) { 1153 rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr, 1154 &sinfo, errorp, serverAddrType); 1155 if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) { 1156 (void) snprintf(errmsg, sizeof (errmsg), 1157 gettext("makeConnection: unable to get " 1158 "server information for %s"), serverAddr); 1159 syslog(LOG_ERR, "libsldap: %s", errmsg); 1160 return (NS_LDAP_OP_FAILED); 1161 } 1162 rc = openConnection(&ld, sinfo.server, auth, timeoutSec, errorp, 1163 fail_if_new_pwd_reqd, passwd_mgmt); 1164 if (rc == NS_LDAP_SUCCESS || rc == 1165 NS_LDAP_SUCCESS_WITH_INFO) { 1166 exit_rc = rc; 1167 goto create_con; 1168 } else { 1169 return (rc); 1170 } 1171 } 1172 1173 /* No cached connection, create one */ 1174 for (; ; ) { 1175 if (host == NULL) 1176 hReq = NS_CACHE_NEW; 1177 else 1178 hReq = NS_CACHE_NEXT; 1179 rc = __s_api_requestServer(hReq, host, &sinfo, errorp, 1180 serverAddrType); 1181 if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) || 1182 (host && (strcasecmp(host, sinfo.server) == 0))) { 1183 /* Log the error */ 1184 if (*errorp) { 1185 (void) snprintf(errmsg, sizeof (errmsg), 1186 "%s: (%s)", gettext("makeConnection: " 1187 "unable to make LDAP connection, " 1188 "request for a server failed"), 1189 (*errorp)->message); 1190 syslog(LOG_ERR, "libsldap: %s", errmsg); 1191 } 1192 1193 if (sinfo.server) 1194 free(sinfo.server); 1195 __s_api_free2dArray(sinfo.saslMechanisms); 1196 __s_api_free2dArray(sinfo.controls); 1197 if (host) 1198 free(host); 1199 return (NS_LDAP_OP_FAILED); 1200 } 1201 if (host) 1202 free(host); 1203 host = strdup(sinfo.server); 1204 if (host == NULL) { 1205 free(sinfo.server); 1206 __s_api_free2dArray(sinfo.saslMechanisms); 1207 __s_api_free2dArray(sinfo.controls); 1208 return (NS_LDAP_MEMORY); 1209 } 1210 1211 /* check if server supports password management */ 1212 passwd_mgmt = __s_api_contain_passwd_control_oid( 1213 sinfo.controls); 1214 /* check if server supports password less account mgmt */ 1215 if (nopasswd_acct_mgmt && 1216 !__s_api_contain_account_usable_control_oid( 1217 sinfo.controls)) { 1218 syslog(LOG_WARNING, "libsldap: server %s does not " 1219 "provide account information without password", 1220 host); 1221 free(host); 1222 free(sinfo.server); 1223 __s_api_free2dArray(sinfo.saslMechanisms); 1224 __s_api_free2dArray(sinfo.controls); 1225 return (NS_LDAP_OP_FAILED); 1226 } 1227 /* make the connection */ 1228 rc = openConnection(&ld, host, auth, timeoutSec, errorp, 1229 fail_if_new_pwd_reqd, passwd_mgmt); 1230 /* if success, go to create connection structure */ 1231 if (rc == NS_LDAP_SUCCESS || 1232 rc == NS_LDAP_SUCCESS_WITH_INFO) { 1233 exit_rc = rc; 1234 break; 1235 } 1236 1237 /* 1238 * If not able to reach the server, inform the ldap 1239 * cache manager that the server should be removed 1240 * from its server list. Thus, the manager will not 1241 * return this server on the next get-server request 1242 * and will also reduce the server list refresh TTL, 1243 * so that it will find out sooner when the server 1244 * is up again. 1245 */ 1246 if (rc == NS_LDAP_INTERNAL && *errorp != NULL) { 1247 if ((*errorp)->status == LDAP_CONNECT_ERROR || 1248 (*errorp)->status == LDAP_SERVER_DOWN) { 1249 /* Reset memory allocation error */ 1250 memerr = 0; 1251 /* 1252 * We contacted a server that we could 1253 * not either authenticate to or contact. 1254 * If it is due to authentication, then 1255 * we need to try the server again. So, 1256 * do not remove the server yet, but 1257 * add it to the bad server list. 1258 * The caller routine will remove 1259 * the servers if: 1260 * a). A good server is found or 1261 * b). All the possible methods 1262 * are tried without finding 1263 * a good server 1264 */ 1265 if (*badsrvrs == NULL) { 1266 if (!(*badsrvrs = (char **)malloc 1267 (sizeof (char *) * NUMTOMALLOC))) { 1268 memerr = 1; 1269 } 1270 /* Allocate memory in chunks of NUMTOMALLOC */ 1271 } else if ((totalbad % NUMTOMALLOC) == 1272 NUMTOMALLOC - 1) { 1273 char **tmpptr; 1274 if (!(tmpptr = (char **)realloc(*badsrvrs, 1275 (sizeof (char *) * NUMTOMALLOC * 1276 ((totalbad/NUMTOMALLOC) + 2))))) { 1277 memerr = 1; 1278 } else { 1279 *badsrvrs = tmpptr; 1280 } 1281 } 1282 /* 1283 * Store host only if there were no unsuccessful 1284 * memory allocations above 1285 */ 1286 if (!memerr && 1287 !((*badsrvrs)[totalbad++] = strdup(host))) { 1288 memerr = 1; 1289 totalbad--; 1290 } 1291 (*badsrvrs)[totalbad] = NULL; 1292 } 1293 } 1294 1295 /* else, cleanup and go for the next server */ 1296 if (sinfo.server) { 1297 free(sinfo.server); 1298 sinfo.server = NULL; 1299 } 1300 __s_api_free2dArray(sinfo.saslMechanisms); 1301 sinfo.saslMechanisms = NULL; 1302 __s_api_free2dArray(sinfo.controls); 1303 sinfo.controls = NULL; 1304 /* Return if we had memory allocation errors */ 1305 if (memerr) 1306 return (NS_LDAP_MEMORY); 1307 if (*errorp) { 1308 /* 1309 * If openConnection() failed due to 1310 * password policy, or invalid credential, 1311 * keep *errorp and exit 1312 */ 1313 if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD || 1314 (*errorp)->status == LDAP_INVALID_CREDENTIALS) { 1315 free(host); 1316 return (rc); 1317 } else { 1318 (void) __ns_ldap_freeError(errorp); 1319 *errorp = NULL; 1320 } 1321 } 1322 } 1323 1324 create_con: 1325 /* we have created ld, setup con structure */ 1326 if (host) 1327 free(host); 1328 if ((con = calloc(1, sizeof (Connection))) == NULL) { 1329 if (sinfo.server) 1330 free(sinfo.server); 1331 __s_api_free2dArray(sinfo.saslMechanisms); 1332 __s_api_free2dArray(sinfo.controls); 1333 /* 1334 * If password control attached in **errorp, 1335 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 1336 * free the error structure 1337 */ 1338 if (*errorp) { 1339 (void) __ns_ldap_freeError(errorp); 1340 *errorp = NULL; 1341 } 1342 return (NS_LDAP_MEMORY); 1343 } 1344 1345 con->serverAddr = sinfo.server; 1346 con->saslMechanisms = sinfo.saslMechanisms; 1347 con->controls = sinfo.controls; 1348 1349 con->auth = __ns_ldap_dupAuth(auth); 1350 if (con->auth == NULL) { 1351 free(con); 1352 /* 1353 * If password control attached in **errorp, 1354 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 1355 * free the error structure 1356 */ 1357 if (*errorp) { 1358 (void) __ns_ldap_freeError(errorp); 1359 *errorp = NULL; 1360 } 1361 return (NS_LDAP_MEMORY); 1362 } 1363 1364 con->threadID = thr_self(); 1365 con->pid = getpid(); 1366 1367 con->ld = ld; 1368 if ((id = addConnection(con)) == -1) { 1369 freeConnection(con); 1370 /* 1371 * If password control attached in **errorp, 1372 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 1373 * free the error structure 1374 */ 1375 if (*errorp) { 1376 (void) __ns_ldap_freeError(errorp); 1377 *errorp = NULL; 1378 } 1379 return (NS_LDAP_MEMORY); 1380 } 1381 #ifdef DEBUG 1382 (void) fprintf(stderr, "tid= %d: connection added into " 1383 "cache %d\n", thr_self(), id); 1384 fflush(stderr); 1385 #endif /* DEBUG */ 1386 *cID = id; 1387 *conp = con; 1388 return (exit_rc); 1389 } 1390 1391 /* 1392 * Return the specified connection to the pool. If necessary 1393 * delete the connection. 1394 */ 1395 1396 static void 1397 _DropConnection(ConnectionID cID, int flag, int fini) 1398 { 1399 Connection *cp; 1400 int id; 1401 int use_lock = !fini; 1402 #ifdef DEBUG 1403 thread_t t = thr_self(); 1404 #endif /* DEBUG */ 1405 1406 id = cID - CONID_OFFSET; 1407 if (id < 0 || id >= sessionPoolSize) 1408 return; 1409 #ifdef DEBUG 1410 (void) fprintf(stderr, "tid= %d: " 1411 "Dropping connection cID=%d flag=0x%x, fini = %d\n", 1412 t, cID, flag, fini); 1413 fflush(stderr); 1414 #endif /* DEBUG */ 1415 if (use_lock) 1416 (void) rw_wrlock(&sessionPoolLock); 1417 1418 cp = sessionPool[id]; 1419 /* sanity check before removing */ 1420 if (!cp || (!fini && !cp->shared && (!cp->usedBit || 1421 cp->threadID != thr_self()))) { 1422 #ifdef DEBUG 1423 if (cp == NULL) 1424 (void) fprintf(stderr, "tid= %d: no " 1425 "need to remove (fini = %d, cp = %p)\n", t, 1426 fini, cp); 1427 else 1428 (void) fprintf(stderr, "tid= %d: no " 1429 "need to remove (fini = %d, cp = %p, shared = %d)\n", 1430 t, fini, cp, cp->shared); 1431 fflush(stderr); 1432 #endif /* DEBUG */ 1433 if (use_lock) 1434 (void) rw_unlock(&sessionPoolLock); 1435 return; 1436 } 1437 1438 if (!fini && 1439 ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail && 1440 ((flag & NS_LDAP_KEEP_CONN) || 1441 (MTperConn == 0 && nscd_proc()) || 1442 MTperConn)) { 1443 #ifdef DEBUG 1444 (void) fprintf(stderr, "tid= %d: keep alive (fini = %d " 1445 "shared = %d)\n", t, fini, cp->shared); 1446 #endif /* DEBUG */ 1447 /* release Connection (keep alive) */ 1448 if (cp->shared) 1449 cp->shared--; 1450 cp->usedBit = B_FALSE; 1451 cp->threadID = 0; /* unmark the threadID */ 1452 if (use_lock) 1453 (void) rw_unlock(&sessionPoolLock); 1454 } else { 1455 /* delete Connection (disconnect) */ 1456 if (cp->shared > 0) { 1457 #ifdef DEBUG 1458 (void) fprintf(stderr, "tid= %d: Connection no " 1459 "longer available (fini = %d, shared = %d)\n", 1460 t, fini, cp->shared); 1461 fflush(stderr); 1462 #endif /* DEBUG */ 1463 cp->shared--; 1464 /* 1465 * Mark this connection not available and decrement 1466 * sharedConnNumber. There could be multiple threads 1467 * sharing this connection so decrement 1468 * sharedConnNumber only once per connection. 1469 */ 1470 if (cp->notAvail == 0) { 1471 cp->notAvail = 1; 1472 (void) mutex_lock(&sharedConnNumberLock); 1473 sharedConnNumber--; 1474 (void) mutex_unlock(&sharedConnNumberLock); 1475 } 1476 } 1477 1478 if (cp->shared <= 0) { 1479 #ifdef DEBUG 1480 (void) fprintf(stderr, "tid= %d: unbind " 1481 "(fini = %d, shared = %d)\n", 1482 t, fini, cp->shared); 1483 fflush(stderr); 1484 #endif /* DEBUG */ 1485 sessionPool[id] = NULL; 1486 (void) ldap_unbind(cp->ld); 1487 freeConnection(cp); 1488 } 1489 1490 if (use_lock) 1491 (void) rw_unlock(&sessionPoolLock); 1492 } 1493 } 1494 1495 void 1496 DropConnection(ConnectionID cID, int flag) 1497 { 1498 _DropConnection(cID, flag, 0); 1499 } 1500 1501 /* 1502 * This routine is called after a bind operation is 1503 * done in openConnection() to process the password 1504 * management information, if any. 1505 * 1506 * Input: 1507 * bind_type: "simple" or "sasl/DIGEST-MD5" 1508 * ldaprc : ldap rc from the ldap bind operation 1509 * controls : controls returned by the server 1510 * errmsg : error message from the server 1511 * fail_if_new_pwd_reqd: 1512 * flag indicating if connection should be open 1513 * when password needs to change immediately 1514 * passwd_mgmt: 1515 * flag indicating if server supports password 1516 * policy/management 1517 * 1518 * Output : ns_ldap_error structure, which may contain 1519 * password status and number of seconds until 1520 * expired 1521 * 1522 * return rc: 1523 * NS_LDAP_EXTERNAL: error, connection should not open 1524 * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached 1525 * NS_LDAP_SUCCESS: OK to open connection 1526 * 1527 */ 1528 1529 static int 1530 process_pwd_mgmt(char *bind_type, int ldaprc, 1531 LDAPControl **controls, 1532 char *errmsg, ns_ldap_error_t **errorp, 1533 int fail_if_new_pwd_reqd, 1534 int passwd_mgmt) 1535 { 1536 char errstr[MAXERROR]; 1537 LDAPControl **ctrl = NULL; 1538 int exit_rc; 1539 ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD; 1540 int sec_until_exp = 0; 1541 1542 /* 1543 * errmsg may be an empty string, 1544 * even if ldaprc is LDAP_SUCCESS, 1545 * free the empty string if that's the case 1546 */ 1547 if (errmsg && 1548 (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) { 1549 ldap_memfree(errmsg); 1550 errmsg = NULL; 1551 } 1552 1553 if (ldaprc != LDAP_SUCCESS) { 1554 /* 1555 * try to map ldap rc and error message to 1556 * a password status 1557 */ 1558 if (errmsg) { 1559 if (passwd_mgmt) 1560 pwd_status = 1561 __s_api_set_passwd_status( 1562 ldaprc, errmsg); 1563 ldap_memfree(errmsg); 1564 } 1565 1566 (void) snprintf(errstr, sizeof (errstr), 1567 gettext("openConnection: " 1568 "%s bind failed " 1569 "- %s"), bind_type, ldap_err2string(ldaprc)); 1570 1571 if (pwd_status != NS_PASSWD_GOOD) { 1572 MKERROR_PWD_MGMT(*errorp, 1573 ldaprc, strdup(errstr), 1574 pwd_status, 0, NULL); 1575 } else { 1576 MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr), 1577 NULL); 1578 } 1579 if (controls) 1580 ldap_controls_free(controls); 1581 1582 return (NS_LDAP_INTERNAL); 1583 } 1584 1585 /* 1586 * ldaprc is LDAP_SUCCESS, 1587 * process the password management controls, if any 1588 */ 1589 exit_rc = NS_LDAP_SUCCESS; 1590 if (controls && passwd_mgmt) { 1591 /* 1592 * The control with the OID 1593 * 2.16.840.1.113730.3.4.4 (or 1594 * LDAP_CONTROL_PWEXPIRED, as defined 1595 * in the ldap.h header file) is the 1596 * expired password control. 1597 * 1598 * This control is used if the server 1599 * is configured to require users to 1600 * change their passwords when first 1601 * logging in and whenever the 1602 * passwords are reset. 1603 * 1604 * If the user is logging in for the 1605 * first time or if the user's 1606 * password has been reset, the 1607 * server sends this control to 1608 * indicate that the client needs to 1609 * change the password immediately. 1610 * 1611 * At this point, the only operation 1612 * that the client can perform is to 1613 * change the user's password. If the 1614 * client requests any other LDAP 1615 * operation, the server sends back 1616 * an LDAP_UNWILLING_TO_PERFORM 1617 * result code with an expired 1618 * password control. 1619 * 1620 * The control with the OID 1621 * 2.16.840.1.113730.3.4.5 (or 1622 * LDAP_CONTROL_PWEXPIRING, as 1623 * defined in the ldap.h header file) 1624 * is the password expiration warning 1625 * control. 1626 * 1627 * This control is used if the server 1628 * is configured to expire user 1629 * passwords after a certain amount 1630 * of time. 1631 * 1632 * The server sends this control back 1633 * to the client if the client binds 1634 * using a password that will soon 1635 * expire. The ldctl_value field of 1636 * the LDAPControl structure 1637 * specifies the number of seconds 1638 * before the password will expire. 1639 */ 1640 for (ctrl = controls; *ctrl; ctrl++) { 1641 1642 if (strcmp((*ctrl)->ldctl_oid, 1643 LDAP_CONTROL_PWEXPIRED) == 0) { 1644 /* 1645 * if the caller wants this bind 1646 * to fail, set up the error info. 1647 * If call to this function is 1648 * for searching the LDAP directory, 1649 * e.g., __ns_ldap_list(), 1650 * there's really no sense to 1651 * let a connection open and 1652 * then fail immediately afterward 1653 * on the LDAP search operation with 1654 * the LDAP_UNWILLING_TO_PERFORM rc 1655 */ 1656 pwd_status = 1657 NS_PASSWD_CHANGE_NEEDED; 1658 if (fail_if_new_pwd_reqd) { 1659 (void) snprintf(errstr, 1660 sizeof (errstr), 1661 gettext( 1662 "openConnection: " 1663 "%s bind " 1664 "failed " 1665 "- password " 1666 "expired. It " 1667 " needs to change " 1668 "immediately!"), 1669 bind_type); 1670 MKERROR_PWD_MGMT(*errorp, 1671 LDAP_SUCCESS, 1672 strdup(errstr), 1673 pwd_status, 1674 0, 1675 NULL); 1676 exit_rc = NS_LDAP_INTERNAL; 1677 } else { 1678 MKERROR_PWD_MGMT(*errorp, 1679 LDAP_SUCCESS, 1680 NULL, 1681 pwd_status, 1682 0, 1683 NULL); 1684 exit_rc = 1685 NS_LDAP_SUCCESS_WITH_INFO; 1686 } 1687 break; 1688 } else if (strcmp((*ctrl)->ldctl_oid, 1689 LDAP_CONTROL_PWEXPIRING) == 0) { 1690 pwd_status = 1691 NS_PASSWD_ABOUT_TO_EXPIRE; 1692 if ((*ctrl)-> 1693 ldctl_value.bv_len > 0 && 1694 (*ctrl)-> 1695 ldctl_value.bv_val) 1696 sec_until_exp = 1697 atoi((*ctrl)-> 1698 ldctl_value.bv_val); 1699 MKERROR_PWD_MGMT(*errorp, 1700 LDAP_SUCCESS, 1701 NULL, 1702 pwd_status, 1703 sec_until_exp, 1704 NULL); 1705 exit_rc = 1706 NS_LDAP_SUCCESS_WITH_INFO; 1707 break; 1708 } 1709 } 1710 } 1711 1712 if (controls) 1713 ldap_controls_free(controls); 1714 1715 return (exit_rc); 1716 } 1717 1718 static int 1719 ldap_in_hosts_switch() 1720 { 1721 enum __nsw_parse_err pserr; 1722 struct __nsw_switchconfig *conf; 1723 struct __nsw_lookup *lkp; 1724 const char *name; 1725 int found = 0; 1726 1727 conf = __nsw_getconfig("hosts", &pserr); 1728 if (conf == NULL) { 1729 return (-1); 1730 } 1731 1732 /* check for skip and count other backends */ 1733 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { 1734 name = lkp->service_name; 1735 if (strcmp(name, "ldap") == 0) { 1736 found = 1; 1737 break; 1738 } 1739 } 1740 __nsw_freeconfig(conf); 1741 return (found); 1742 } 1743 1744 static int 1745 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, 1746 int timeoutSec, ns_ldap_error_t **errorp, 1747 int fail_if_new_pwd_reqd, int passwd_mgmt) 1748 { 1749 LDAP *ld = NULL; 1750 char *binddn, *passwd; 1751 char *digest_md5_name; 1752 const char *s; 1753 int ldapVersion = LDAP_VERSION3; 1754 int derefOption = LDAP_DEREF_ALWAYS; 1755 int zero = 0; 1756 int rc; 1757 char errstr[MAXERROR]; 1758 int errnum = 0; 1759 LDAPMessage *resultMsg; 1760 int msgId; 1761 int useSSL = 0, port = 0; 1762 struct timeval tv; 1763 AuthType_t bindType; 1764 int timeoutMilliSec = timeoutSec * 1000; 1765 struct berval cred; 1766 char *sslServerAddr; 1767 char *s1; 1768 char *errmsg, *end = NULL; 1769 LDAPControl **controls; 1770 int pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF; 1771 ns_sasl_cb_param_t sasl_param; 1772 1773 *errorp = NULL; 1774 *ldp = NULL; 1775 1776 switch (auth->auth.type) { 1777 case NS_LDAP_AUTH_NONE: 1778 case NS_LDAP_AUTH_SIMPLE: 1779 case NS_LDAP_AUTH_SASL: 1780 bindType = auth->auth.type; 1781 break; 1782 case NS_LDAP_AUTH_TLS: 1783 useSSL = 1; 1784 switch (auth->auth.tlstype) { 1785 case NS_LDAP_TLS_NONE: 1786 bindType = NS_LDAP_AUTH_NONE; 1787 break; 1788 case NS_LDAP_TLS_SIMPLE: 1789 bindType = NS_LDAP_AUTH_SIMPLE; 1790 break; 1791 case NS_LDAP_TLS_SASL: 1792 bindType = NS_LDAP_AUTH_SASL; 1793 break; 1794 default: 1795 (void) sprintf(errstr, 1796 gettext("openConnection: unsupported " 1797 "TLS authentication method " 1798 "(%d)"), auth->auth.tlstype); 1799 MKERROR(LOG_WARNING, *errorp, 1800 LDAP_AUTH_METHOD_NOT_SUPPORTED, 1801 strdup(errstr), NULL); 1802 return (NS_LDAP_INTERNAL); 1803 } 1804 break; 1805 default: 1806 (void) sprintf(errstr, 1807 gettext("openConnection: unsupported " 1808 "authentication method (%d)"), auth->auth.type); 1809 MKERROR(LOG_WARNING, *errorp, 1810 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 1811 NULL); 1812 return (NS_LDAP_INTERNAL); 1813 } 1814 1815 if (useSSL) { 1816 const char *hostcertpath; 1817 char *alloc_hcp = NULL; 1818 #ifdef DEBUG 1819 (void) fprintf(stderr, "tid= %d: +++TLS transport\n", 1820 thr_self()); 1821 #endif /* DEBUG */ 1822 1823 if (prldap_set_session_option(NULL, NULL, 1824 PRLDAP_OPT_IO_MAX_TIMEOUT, 1825 timeoutMilliSec) != LDAP_SUCCESS) { 1826 (void) snprintf(errstr, sizeof (errstr), 1827 gettext("openConnection: failed to initialize " 1828 "TLS security")); 1829 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1830 strdup(errstr), NULL); 1831 return (NS_LDAP_INTERNAL); 1832 } 1833 1834 hostcertpath = auth->hostcertpath; 1835 if (hostcertpath == NULL) { 1836 alloc_hcp = __s_get_hostcertpath(); 1837 hostcertpath = alloc_hcp; 1838 } 1839 1840 if (hostcertpath == NULL) 1841 return (NS_LDAP_MEMORY); 1842 1843 if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) { 1844 if (alloc_hcp) 1845 free(alloc_hcp); 1846 (void) snprintf(errstr, sizeof (errstr), 1847 gettext("openConnection: failed to initialize " 1848 "TLS security (%s)"), 1849 ldapssl_err2string(rc)); 1850 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1851 strdup(errstr), NULL); 1852 return (NS_LDAP_INTERNAL); 1853 } 1854 if (alloc_hcp) 1855 free(alloc_hcp); 1856 1857 /* determine if the host name contains a port number */ 1858 s = strchr(serverAddr, ']'); /* skip over ipv6 addr */ 1859 if (s == NULL) 1860 s = serverAddr; 1861 s = strchr(s, ':'); 1862 if (s != NULL) { 1863 /* 1864 * If we do get a port number, we will try stripping 1865 * it. At present, referrals will always have a 1866 * port number. 1867 */ 1868 sslServerAddr = strdup(serverAddr); 1869 if (sslServerAddr == NULL) 1870 return (NS_LDAP_MEMORY); 1871 s1 = strrchr(sslServerAddr, ':'); 1872 if (s1 != NULL) 1873 *s1 = '\0'; 1874 (void) snprintf(errstr, sizeof (errstr), 1875 gettext("openConnection: cannot use tls with %s. " 1876 "Trying %s"), 1877 serverAddr, sslServerAddr); 1878 syslog(LOG_ERR, "libsldap: %s", errstr); 1879 } else 1880 sslServerAddr = (char *)serverAddr; 1881 1882 ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1); 1883 1884 if (sslServerAddr != serverAddr) 1885 free(sslServerAddr); 1886 1887 if (ld == NULL || 1888 ldapssl_install_gethostbyaddr(ld, "ldap") != 0) { 1889 (void) snprintf(errstr, sizeof (errstr), 1890 gettext("openConnection: failed to connect " 1891 "using TLS (%s)"), strerror(errno)); 1892 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1893 strdup(errstr), NULL); 1894 return (NS_LDAP_INTERNAL); 1895 } 1896 } else { 1897 #ifdef DEBUG 1898 (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n", 1899 thr_self()); 1900 #endif /* DEBUG */ 1901 port = LDAP_PORT; 1902 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI && 1903 (end = strchr(serverAddr, ':')) != NULL) { 1904 /* 1905 * The IP is converted to hostname so it's a 1906 * hostname:port up to this point. 1907 * 1908 * libldap passes hostname:port to the sasl layer. 1909 * The ldap service principal is constructed as 1910 * ldap/hostname:port@REALM. Kerberos authentication 1911 * will fail. So it needs to be parsed to construct 1912 * a valid principal ldap/hostname@REALM. 1913 * 1914 * For useSSL case above, it already parses port so 1915 * no need to parse serverAddr 1916 */ 1917 *end = '\0'; 1918 port = atoi(end + 1); 1919 } 1920 1921 /* Warning message IF cannot connect to host(s) */ 1922 if ((ld = ldap_init((char *)serverAddr, port)) == NULL) { 1923 char *p = strerror(errno); 1924 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1925 strdup(p), NULL); 1926 if (end) 1927 *end = ':'; 1928 return (NS_LDAP_INTERNAL); 1929 } else { 1930 if (end) 1931 *end = ':'; 1932 /* check and avoid gethostname recursion */ 1933 if (ldap_in_hosts_switch() > 0 && 1934 ! __s_api_isipv4((char *)serverAddr) && 1935 ! __s_api_isipv6((char *)serverAddr)) { 1936 /* host: ldap - found, attempt to recover */ 1937 if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB, 1938 "ldap") != 0) { 1939 (void) snprintf(errstr, sizeof (errstr), 1940 gettext("openConnection: " 1941 "unrecoverable gethostname " 1942 "recursion detected " 1943 "in /etc/nsswitch.conf")); 1944 MKERROR(LOG_WARNING, *errorp, 1945 LDAP_CONNECT_ERROR, 1946 strdup(errstr), NULL); 1947 (void) ldap_unbind(ld); 1948 return (NS_LDAP_INTERNAL); 1949 } 1950 } 1951 } 1952 } 1953 1954 ns_setup_mt_conn_and_tsd(ld); 1955 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); 1956 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 1957 /* 1958 * set LDAP_OPT_REFERRALS to OFF. 1959 * This library will handle the referral itself 1960 * based on API flags or configuration file 1961 * specification. If this option is not set 1962 * to OFF, libldap will never pass the 1963 * referral info up to this library 1964 */ 1965 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1966 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 1967 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 1968 /* setup TCP/IP connect timeout */ 1969 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 1970 &timeoutMilliSec); 1971 /* retry if LDAP I/O was interrupted */ 1972 (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1973 1974 switch (bindType) { 1975 case NS_LDAP_AUTH_NONE: 1976 #ifdef DEBUG 1977 (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n", 1978 thr_self()); 1979 #endif /* DEBUG */ 1980 break; 1981 case NS_LDAP_AUTH_SIMPLE: 1982 binddn = auth->cred.unix_cred.userID; 1983 passwd = auth->cred.unix_cred.passwd; 1984 if (passwd == NULL || *passwd == '\0' || 1985 binddn == NULL || *binddn == '\0') { 1986 (void) sprintf(errstr, gettext("openConnection: " 1987 "missing credentials for Simple bind")); 1988 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 1989 strdup(errstr), NULL); 1990 (void) ldap_unbind(ld); 1991 return (NS_LDAP_INTERNAL); 1992 } 1993 1994 #ifdef DEBUG 1995 (void) fprintf(stderr, "tid= %d: +++Simple bind\n", 1996 thr_self()); 1997 #endif /* DEBUG */ 1998 msgId = ldap_simple_bind(ld, binddn, passwd); 1999 2000 if (msgId == -1) { 2001 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 2002 (void *)&errnum); 2003 (void) snprintf(errstr, sizeof (errstr), 2004 gettext("openConnection: simple bind failed " 2005 "- %s"), ldap_err2string(errnum)); 2006 (void) ldap_unbind(ld); 2007 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 2008 NULL); 2009 return (NS_LDAP_INTERNAL); 2010 } 2011 2012 tv.tv_sec = timeoutSec; 2013 tv.tv_usec = 0; 2014 rc = ldap_result(ld, msgId, 0, &tv, &resultMsg); 2015 2016 if ((rc == -1) || (rc == 0)) { 2017 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 2018 (void *)&errnum); 2019 (void) snprintf(errstr, sizeof (errstr), 2020 gettext("openConnection: simple bind failed " 2021 "- %s"), ldap_err2string(errnum)); 2022 (void) ldap_msgfree(resultMsg); 2023 (void) ldap_unbind(ld); 2024 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 2025 NULL); 2026 return (NS_LDAP_INTERNAL); 2027 } 2028 2029 /* 2030 * get ldaprc, controls, and error msg 2031 */ 2032 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 2033 &errmsg, NULL, &controls, 1); 2034 2035 if (rc != LDAP_SUCCESS) { 2036 (void) snprintf(errstr, sizeof (errstr), 2037 gettext("openConnection: simple bind failed " 2038 "- unable to parse result")); 2039 (void) ldap_unbind(ld); 2040 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2041 strdup(errstr), NULL); 2042 return (NS_LDAP_INTERNAL); 2043 } 2044 2045 /* process the password management info, if any */ 2046 pwd_rc = process_pwd_mgmt("simple", 2047 errnum, controls, errmsg, 2048 errorp, 2049 fail_if_new_pwd_reqd, 2050 passwd_mgmt); 2051 2052 if (pwd_rc == NS_LDAP_INTERNAL) { 2053 (void) ldap_unbind(ld); 2054 return (pwd_rc); 2055 } 2056 2057 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 2058 *ldp = ld; 2059 return (pwd_rc); 2060 } 2061 2062 break; 2063 case NS_LDAP_AUTH_SASL: 2064 if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE && 2065 auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) { 2066 (void) sprintf(errstr, 2067 gettext("openConnection: SASL options are " 2068 "not supported (%d) for non-GSSAPI sasl bind"), 2069 auth->auth.saslopt); 2070 MKERROR(LOG_WARNING, *errorp, 2071 LDAP_AUTH_METHOD_NOT_SUPPORTED, 2072 strdup(errstr), NULL); 2073 (void) ldap_unbind(ld); 2074 return (NS_LDAP_INTERNAL); 2075 } 2076 if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) { 2077 binddn = auth->cred.unix_cred.userID; 2078 passwd = auth->cred.unix_cred.passwd; 2079 if (passwd == NULL || *passwd == '\0' || 2080 binddn == NULL || *binddn == '\0') { 2081 (void) sprintf(errstr, 2082 gettext("openConnection: missing credentials " 2083 "for SASL bind")); 2084 MKERROR(LOG_WARNING, *errorp, 2085 LDAP_INVALID_CREDENTIALS, 2086 strdup(errstr), NULL); 2087 (void) ldap_unbind(ld); 2088 return (NS_LDAP_INTERNAL); 2089 } 2090 cred.bv_val = passwd; 2091 cred.bv_len = strlen(passwd); 2092 } 2093 2094 switch (auth->auth.saslmech) { 2095 case NS_LDAP_SASL_CRAM_MD5: 2096 /* 2097 * NOTE: if iDS changes to support cram_md5, 2098 * please add password management code here. 2099 * Since ldap_sasl_cram_md5_bind_s does not 2100 * return anything that could be used to 2101 * extract the ldap rc/errmsg/control to 2102 * determine if bind failed due to password 2103 * policy, a new cram_md5_bind API will need 2104 * to be introduced. See 2105 * ldap_x_sasl_digest_md5_bind() and case 2106 * NS_LDAP_SASL_DIGEST_MD5 below for details. 2107 */ 2108 if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn, 2109 &cred, NULL, NULL)) != LDAP_SUCCESS) { 2110 (void) ldap_get_option(ld, 2111 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 2112 (void) snprintf(errstr, sizeof (errstr), 2113 gettext("openConnection: " 2114 "sasl/CRAM-MD5 bind failed - %s"), 2115 ldap_err2string(errnum)); 2116 MKERROR(LOG_WARNING, *errorp, errnum, 2117 strdup(errstr), NULL); 2118 (void) ldap_unbind(ld); 2119 return (NS_LDAP_INTERNAL); 2120 } 2121 break; 2122 case NS_LDAP_SASL_DIGEST_MD5: 2123 digest_md5_name = malloc(strlen(binddn) + 5); 2124 /* 5 = strlen("dn: ") + 1 */ 2125 if (digest_md5_name == NULL) { 2126 (void) ldap_unbind(ld); 2127 return (NS_LDAP_MEMORY); 2128 } 2129 (void) strcpy(digest_md5_name, "dn: "); 2130 (void) strcat(digest_md5_name, binddn); 2131 2132 tv.tv_sec = timeoutSec; 2133 tv.tv_usec = 0; 2134 rc = ldap_x_sasl_digest_md5_bind(ld, 2135 digest_md5_name, &cred, NULL, NULL, 2136 &tv, &resultMsg); 2137 2138 if (resultMsg == NULL) { 2139 free(digest_md5_name); 2140 (void) ldap_get_option(ld, 2141 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 2142 (void) snprintf(errstr, sizeof (errstr), 2143 gettext("openConnection: " 2144 "DIGEST-MD5 bind failed - %s"), 2145 ldap_err2string(errnum)); 2146 (void) ldap_unbind(ld); 2147 MKERROR(LOG_WARNING, *errorp, errnum, 2148 strdup(errstr), NULL); 2149 return (NS_LDAP_INTERNAL); 2150 } 2151 2152 /* 2153 * get ldaprc, controls, and error msg 2154 */ 2155 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 2156 &errmsg, NULL, &controls, 1); 2157 2158 if (rc != LDAP_SUCCESS) { 2159 free(digest_md5_name); 2160 (void) snprintf(errstr, sizeof (errstr), 2161 gettext("openConnection: " 2162 "DIGEST-MD5 bind failed " 2163 "- unable to parse result")); 2164 (void) ldap_unbind(ld); 2165 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2166 strdup(errstr), NULL); 2167 return (NS_LDAP_INTERNAL); 2168 } 2169 2170 /* process the password management info, if any */ 2171 pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5", 2172 errnum, controls, errmsg, 2173 errorp, 2174 fail_if_new_pwd_reqd, 2175 passwd_mgmt); 2176 2177 if (pwd_rc == NS_LDAP_INTERNAL) { 2178 free(digest_md5_name); 2179 (void) ldap_unbind(ld); 2180 return (pwd_rc); 2181 } 2182 2183 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 2184 *ldp = ld; 2185 return (pwd_rc); 2186 } 2187 2188 free(digest_md5_name); 2189 break; 2190 case NS_LDAP_SASL_GSSAPI: 2191 if (sasl_gssapi_inited == 0) { 2192 rc = __s_api_sasl_gssapi_init(); 2193 if (rc != NS_LDAP_SUCCESS) { 2194 (void) snprintf(errstr, sizeof (errstr), 2195 gettext("openConnection: " 2196 "GSSAPI initialization " 2197 "failed")); 2198 (void) ldap_unbind(ld); 2199 MKERROR(LOG_WARNING, *errorp, rc, 2200 strdup(errstr), NULL); 2201 return (rc); 2202 } 2203 } 2204 (void) memset(&sasl_param, 0, 2205 sizeof (ns_sasl_cb_param_t)); 2206 sasl_param.authid = NULL; 2207 sasl_param.authzid = ""; 2208 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN, 2209 (void *)&min_ssf); 2210 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX, 2211 (void *)&max_ssf); 2212 2213 rc = ldap_sasl_interactive_bind_s( 2214 ld, NULL, "GSSAPI", 2215 NULL, NULL, LDAP_SASL_INTERACTIVE, 2216 __s_api_sasl_bind_callback, 2217 &sasl_param); 2218 2219 if (rc != LDAP_SUCCESS) { 2220 (void) snprintf(errstr, sizeof (errstr), 2221 gettext("openConnection: " 2222 "GSSAPI bind failed " 2223 "- %d %s"), rc, ldap_err2string(rc)); 2224 (void) ldap_unbind(ld); 2225 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2226 strdup(errstr), NULL); 2227 return (NS_LDAP_INTERNAL); 2228 } 2229 2230 break; 2231 default: 2232 (void) ldap_unbind(ld); 2233 (void) sprintf(errstr, 2234 gettext("openConnection: unsupported SASL " 2235 "mechanism (%d)"), auth->auth.saslmech); 2236 MKERROR(LOG_WARNING, *errorp, 2237 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 2238 NULL); 2239 return (NS_LDAP_INTERNAL); 2240 } 2241 } 2242 2243 *ldp = ld; 2244 return (NS_LDAP_SUCCESS); 2245 } 2246 2247 /* 2248 * FUNCTION: __s_api_getDefaultAuth 2249 * 2250 * Constructs a credential for authentication using the config module. 2251 * 2252 * RETURN VALUES: 2253 * 2254 * NS_LDAP_SUCCESS If successful 2255 * NS_LDAP_CONFIG If there are any config errors. 2256 * NS_LDAP_MEMORY Memory errors. 2257 * NS_LDAP_OP_FAILED If there are no more authentication methods so can 2258 * not build a new authp. 2259 * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the 2260 * necessary fields of a cred for a given auth method 2261 * are not provided. 2262 * INPUT: 2263 * 2264 * cLevel Currently requested credential level to be tried 2265 * 2266 * aMethod Currently requested authentication method to be tried 2267 * 2268 * OUTPUT: 2269 * 2270 * authp authentication method to use. 2271 */ 2272 static int 2273 __s_api_getDefaultAuth( 2274 int *cLevel, 2275 ns_auth_t *aMethod, 2276 ns_cred_t **authp) 2277 { 2278 void **paramVal = NULL; 2279 char *modparamVal = NULL; 2280 int getUid = 0; 2281 int getPasswd = 0; 2282 int getCertpath = 0; 2283 int rc = 0; 2284 ns_ldap_error_t *errorp = NULL; 2285 2286 #ifdef DEBUG 2287 (void) fprintf(stderr, "__s_api_getDefaultAuth START\n"); 2288 #endif 2289 2290 if (aMethod == NULL) { 2291 /* Require an Auth */ 2292 return (NS_LDAP_INVALID_PARAM); 2293 2294 } 2295 /* 2296 * credential level "self" can work with auth method sasl/GSSAPI only 2297 */ 2298 if (cLevel && *cLevel == NS_LDAP_CRED_SELF && 2299 aMethod->saslmech != NS_LDAP_SASL_GSSAPI) 2300 return (NS_LDAP_INVALID_PARAM); 2301 2302 *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); 2303 if ((*authp) == NULL) 2304 return (NS_LDAP_MEMORY); 2305 2306 (*authp)->auth = *aMethod; 2307 2308 switch (aMethod->type) { 2309 case NS_LDAP_AUTH_NONE: 2310 return (NS_LDAP_SUCCESS); 2311 case NS_LDAP_AUTH_SIMPLE: 2312 getUid++; 2313 getPasswd++; 2314 break; 2315 case NS_LDAP_AUTH_SASL: 2316 if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 2317 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) { 2318 getUid++; 2319 getPasswd++; 2320 } else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) { 2321 (void) __ns_ldap_freeCred(authp); 2322 *authp = NULL; 2323 return (NS_LDAP_INVALID_PARAM); 2324 } 2325 break; 2326 case NS_LDAP_AUTH_TLS: 2327 if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) || 2328 ((aMethod->tlstype == NS_LDAP_TLS_SASL) && 2329 ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 2330 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) { 2331 getUid++; 2332 getPasswd++; 2333 getCertpath++; 2334 } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) { 2335 getCertpath++; 2336 } else { 2337 (void) __ns_ldap_freeCred(authp); 2338 *authp = NULL; 2339 return (NS_LDAP_INVALID_PARAM); 2340 } 2341 break; 2342 } 2343 2344 if (getUid) { 2345 paramVal = NULL; 2346 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P, 2347 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 2348 (void) __ns_ldap_freeCred(authp); 2349 (void) __ns_ldap_freeError(&errorp); 2350 *authp = NULL; 2351 return (rc); 2352 } 2353 2354 if (paramVal == NULL || *paramVal == NULL) { 2355 (void) __ns_ldap_freeCred(authp); 2356 *authp = NULL; 2357 return (NS_LDAP_INVALID_PARAM); 2358 } 2359 2360 (*authp)->cred.unix_cred.userID = strdup((char *)*paramVal); 2361 (void) __ns_ldap_freeParam(¶mVal); 2362 if ((*authp)->cred.unix_cred.userID == NULL) { 2363 (void) __ns_ldap_freeCred(authp); 2364 *authp = NULL; 2365 return (NS_LDAP_MEMORY); 2366 } 2367 } 2368 if (getPasswd) { 2369 paramVal = NULL; 2370 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P, 2371 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 2372 (void) __ns_ldap_freeCred(authp); 2373 (void) __ns_ldap_freeError(&errorp); 2374 *authp = NULL; 2375 return (rc); 2376 } 2377 2378 if (paramVal == NULL || *paramVal == NULL) { 2379 (void) __ns_ldap_freeCred(authp); 2380 *authp = NULL; 2381 return (NS_LDAP_INVALID_PARAM); 2382 } 2383 2384 modparamVal = dvalue((char *)*paramVal); 2385 (void) __ns_ldap_freeParam(¶mVal); 2386 if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) { 2387 (void) __ns_ldap_freeCred(authp); 2388 if (modparamVal != NULL) 2389 free(modparamVal); 2390 *authp = NULL; 2391 return (NS_LDAP_INVALID_PARAM); 2392 } 2393 2394 (*authp)->cred.unix_cred.passwd = modparamVal; 2395 } 2396 if (getCertpath) { 2397 paramVal = NULL; 2398 if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P, 2399 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 2400 (void) __ns_ldap_freeCred(authp); 2401 (void) __ns_ldap_freeError(&errorp); 2402 *authp = NULL; 2403 return (rc); 2404 } 2405 2406 if (paramVal == NULL || *paramVal == NULL) { 2407 (void) __ns_ldap_freeCred(authp); 2408 *authp = NULL; 2409 return (NS_LDAP_INVALID_PARAM); 2410 } 2411 2412 (*authp)->hostcertpath = strdup((char *)*paramVal); 2413 (void) __ns_ldap_freeParam(¶mVal); 2414 if ((*authp)->hostcertpath == NULL) { 2415 (void) __ns_ldap_freeCred(authp); 2416 *authp = NULL; 2417 return (NS_LDAP_MEMORY); 2418 } 2419 } 2420 return (NS_LDAP_SUCCESS); 2421 } 2422 2423 /* 2424 * FUNCTION: __s_api_getConnection 2425 * 2426 * Bind to the specified server or one from the server 2427 * list and return the pointer. 2428 * 2429 * This function can rebind or not (NS_LDAP_HARD), it can require a 2430 * credential or bind anonymously 2431 * 2432 * This function follows the DUA configuration schema algorithm 2433 * 2434 * RETURN VALUES: 2435 * 2436 * NS_LDAP_SUCCESS A connection was made successfully. 2437 * NS_LDAP_SUCCESS_WITH_INFO 2438 * A connection was made successfully, but with 2439 * password management info in *errorp 2440 * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function. 2441 * NS_LDAP_CONFIG If there are any config errors. 2442 * NS_LDAP_MEMORY Memory errors. 2443 * NS_LDAP_INTERNAL If there was a ldap error. 2444 * 2445 * INPUT: 2446 * 2447 * server Bind to this LDAP server only 2448 * flags If NS_LDAP_HARD is set function will not return until it has 2449 * a connection unless there is a authentication problem. 2450 * If NS_LDAP_NEW_CONN is set the function must force a new 2451 * connection to be created 2452 * If NS_LDAP_KEEP_CONN is set the connection is to be kept open 2453 * auth Credentials for bind. This could be NULL in which case 2454 * a default cred built from the config module is used. 2455 * sessionId cookie that points to a previous session 2456 * fail_if_new_pwd_reqd 2457 * a flag indicating this function should fail if the passwd 2458 * in auth needs to change immediately 2459 * nopasswd_acct_mgmt 2460 * a flag indicating that makeConnection should check before 2461 * binding if server supports LDAP V3 password less 2462 * account management 2463 * 2464 * OUTPUT: 2465 * 2466 * session pointer to a session with connection information 2467 * errorp Set if there are any INTERNAL, or CONFIG error. 2468 */ 2469 int 2470 __s_api_getConnection( 2471 const char *server, 2472 const int flags, 2473 const ns_cred_t *cred, /* credentials for bind */ 2474 ConnectionID *sessionId, 2475 Connection **session, 2476 ns_ldap_error_t **errorp, 2477 int fail_if_new_pwd_reqd, 2478 int nopasswd_acct_mgmt) 2479 { 2480 char errmsg[MAXERROR]; 2481 ns_auth_t **aMethod = NULL; 2482 ns_auth_t **aNext = NULL; 2483 int **cLevel = NULL; 2484 int **cNext = NULL; 2485 int timeoutSec = NS_DEFAULT_BIND_TIMEOUT; 2486 int rc; 2487 Connection *con = NULL; 2488 int sec = 1; 2489 ns_cred_t *authp = NULL; 2490 ns_cred_t anon; 2491 int version = NS_LDAP_V2, self_gssapi_only = 0; 2492 void **paramVal = NULL; 2493 char **badSrvrs = NULL; /* List of problem hostnames */ 2494 2495 if ((session == NULL) || (sessionId == NULL)) { 2496 return (NS_LDAP_INVALID_PARAM); 2497 } 2498 *session = NULL; 2499 2500 /* if we already have a session id try to reuse connection */ 2501 if (*sessionId > 0) { 2502 rc = findConnectionById(flags, cred, *sessionId, &con); 2503 if (rc == *sessionId && con) { 2504 *session = con; 2505 return (NS_LDAP_SUCCESS); 2506 } 2507 *sessionId = 0; 2508 } 2509 2510 /* get profile version number */ 2511 if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, 2512 ¶mVal, errorp)) != NS_LDAP_SUCCESS) 2513 return (rc); 2514 if (paramVal == NULL) { 2515 (void) sprintf(errmsg, gettext("getConnection: no file " 2516 "version")); 2517 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg), 2518 NS_LDAP_CONFIG); 2519 return (NS_LDAP_CONFIG); 2520 } 2521 if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0) 2522 version = NS_LDAP_V1; 2523 (void) __ns_ldap_freeParam((void ***)¶mVal); 2524 2525 /* Get the bind timeout value */ 2526 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, ¶mVal, errorp); 2527 if (paramVal != NULL && *paramVal != NULL) { 2528 timeoutSec = **((int **)paramVal); 2529 (void) __ns_ldap_freeParam(¶mVal); 2530 } 2531 if (*errorp) 2532 (void) __ns_ldap_freeError(errorp); 2533 2534 if (cred == NULL) { 2535 /* Get the authentication method list */ 2536 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, 2537 (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS) 2538 return (rc); 2539 if (aMethod == NULL) { 2540 aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *)); 2541 if (aMethod == NULL) 2542 return (NS_LDAP_MEMORY); 2543 aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t)); 2544 if (aMethod[0] == NULL) { 2545 free(aMethod); 2546 return (NS_LDAP_MEMORY); 2547 } 2548 if (version == NS_LDAP_V1) 2549 (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE; 2550 else { 2551 (aMethod[0])->type = NS_LDAP_AUTH_SASL; 2552 (aMethod[0])->saslmech = 2553 NS_LDAP_SASL_DIGEST_MD5; 2554 (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE; 2555 } 2556 } 2557 2558 /* Get the credential level list */ 2559 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P, 2560 (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) { 2561 (void) __ns_ldap_freeParam((void ***)&aMethod); 2562 return (rc); 2563 } 2564 if (cLevel == NULL) { 2565 cLevel = (int **)calloc(2, sizeof (int *)); 2566 if (cLevel == NULL) 2567 return (NS_LDAP_MEMORY); 2568 cLevel[0] = (int *)calloc(1, sizeof (int)); 2569 if (cLevel[0] == NULL) 2570 return (NS_LDAP_MEMORY); 2571 if (version == NS_LDAP_V1) 2572 *(cLevel[0]) = NS_LDAP_CRED_PROXY; 2573 else 2574 *(cLevel[0]) = NS_LDAP_CRED_ANON; 2575 } 2576 } 2577 2578 /* setup the anon credential for anonymous connection */ 2579 (void) memset(&anon, 0, sizeof (ns_cred_t)); 2580 anon.auth.type = NS_LDAP_AUTH_NONE; 2581 2582 for (; ; ) { 2583 if (cred != NULL) { 2584 /* using specified auth method */ 2585 rc = makeConnection(&con, server, cred, 2586 sessionId, timeoutSec, errorp, 2587 fail_if_new_pwd_reqd, 2588 nopasswd_acct_mgmt, flags, &badSrvrs); 2589 if (rc == NS_LDAP_SUCCESS || 2590 rc == NS_LDAP_SUCCESS_WITH_INFO) { 2591 *session = con; 2592 break; 2593 } 2594 } else { 2595 self_gssapi_only = __s_api_self_gssapi_only_get(); 2596 /* for every cred level */ 2597 for (cNext = cLevel; *cNext != NULL; cNext++) { 2598 if (self_gssapi_only && 2599 **cNext != NS_LDAP_CRED_SELF) 2600 continue; 2601 if (**cNext == NS_LDAP_CRED_ANON) { 2602 /* 2603 * make connection anonymously 2604 * Free the down server list before 2605 * looping through 2606 */ 2607 if (badSrvrs && *badSrvrs) { 2608 __s_api_free2dArray(badSrvrs); 2609 badSrvrs = NULL; 2610 } 2611 rc = makeConnection(&con, server, &anon, 2612 sessionId, timeoutSec, errorp, 2613 fail_if_new_pwd_reqd, 2614 nopasswd_acct_mgmt, flags, 2615 &badSrvrs); 2616 if (rc == NS_LDAP_SUCCESS || 2617 rc == 2618 NS_LDAP_SUCCESS_WITH_INFO) { 2619 *session = con; 2620 goto done; 2621 } 2622 continue; 2623 } 2624 /* for each cred level */ 2625 for (aNext = aMethod; *aNext != NULL; aNext++) { 2626 if (self_gssapi_only && 2627 (*aNext)->saslmech != 2628 NS_LDAP_SASL_GSSAPI) 2629 continue; 2630 /* 2631 * self coexists with sasl/GSSAPI only 2632 * and non-self coexists with non-gssapi 2633 * only 2634 */ 2635 if ((**cNext == NS_LDAP_CRED_SELF && 2636 (*aNext)->saslmech != 2637 NS_LDAP_SASL_GSSAPI) || 2638 (**cNext != NS_LDAP_CRED_SELF && 2639 (*aNext)->saslmech == 2640 NS_LDAP_SASL_GSSAPI)) 2641 continue; 2642 /* make connection and authenticate */ 2643 /* with default credentials */ 2644 authp = NULL; 2645 rc = __s_api_getDefaultAuth(*cNext, 2646 *aNext, &authp); 2647 if (rc != NS_LDAP_SUCCESS) { 2648 continue; 2649 } 2650 /* 2651 * Free the down server list before 2652 * looping through 2653 */ 2654 if (badSrvrs && *badSrvrs) { 2655 __s_api_free2dArray(badSrvrs); 2656 badSrvrs = NULL; 2657 } 2658 rc = makeConnection(&con, server, authp, 2659 sessionId, timeoutSec, errorp, 2660 fail_if_new_pwd_reqd, 2661 nopasswd_acct_mgmt, flags, 2662 &badSrvrs); 2663 (void) __ns_ldap_freeCred(&authp); 2664 if (rc == NS_LDAP_SUCCESS || 2665 rc == 2666 NS_LDAP_SUCCESS_WITH_INFO) { 2667 *session = con; 2668 goto done; 2669 } 2670 } 2671 } 2672 } 2673 if (flags & NS_LDAP_HARD) { 2674 if (sec < LDAPMAXHARDLOOKUPTIME) 2675 sec *= 2; 2676 _sleep(sec); 2677 } else { 2678 break; 2679 } 2680 } 2681 2682 done: 2683 /* 2684 * If unable to get a connection, and this is 2685 * the thread opening the shared connection, 2686 * unlock the session mutex and let other 2687 * threads try to get their own connection. 2688 */ 2689 if (wait4session != 0 && sessionTid == thr_self()) { 2690 wait4session = 0; 2691 sessionTid = 0; 2692 #ifdef DEBUG 2693 (void) fprintf(stderr, "tid= %d: __s_api_getConnection: " 2694 "unlocking sessionLock \n", thr_self()); 2695 fflush(stderr); 2696 #endif /* DEBUG */ 2697 (void) mutex_unlock(&sessionLock); 2698 } 2699 if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) { 2700 /* 2701 * self_gssapi_only is true but no self/sasl/gssapi is 2702 * configured 2703 */ 2704 rc = NS_LDAP_CONFIG; 2705 } 2706 2707 (void) __ns_ldap_freeParam((void ***)&aMethod); 2708 (void) __ns_ldap_freeParam((void ***)&cLevel); 2709 2710 if (badSrvrs && *badSrvrs) { 2711 /* 2712 * At this point, either we have a successful 2713 * connection or exhausted all the possible auths. 2714 * and creds. Mark the problem servers as down 2715 * so that the problem servers are not contacted 2716 * again until the refresh_ttl expires. 2717 */ 2718 (void) __s_api_removeBadServers(badSrvrs); 2719 __s_api_free2dArray(badSrvrs); 2720 } 2721 return (rc); 2722 } 2723 2724 #pragma fini(_free_sessionPool) 2725 static void 2726 _free_sessionPool() 2727 { 2728 int id; 2729 2730 (void) rw_wrlock(&sessionPoolLock); 2731 if (sessionPool != NULL) { 2732 for (id = 0; id < sessionPoolSize; id++) 2733 _DropConnection(id + CONID_OFFSET, 0, 1); 2734 free(sessionPool); 2735 sessionPool = NULL; 2736 sessionPoolSize = 0; 2737 } 2738 (void) rw_unlock(&sessionPoolLock); 2739 } 2740