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