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