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