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 /* 1364 * We're given the server address, just use it. 1365 * In case of sasl/GSSAPI, serverAddr would need to be a FQDN. 1366 * We assume this is the case for now. 1367 * 1368 * Only the server address fields of sinfo structure are filled 1369 * in since these are the only relevant data that we have. Other 1370 * fields of this structure (controls, saslMechanisms) are 1371 * kept to NULL. 1372 */ 1373 sinfo.server = strdup(serverAddr); 1374 if (sinfo.server == NULL) { 1375 return (NS_LDAP_MEMORY); 1376 } 1377 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) { 1378 sinfo.serverFQDN = strdup(serverAddr); 1379 if (sinfo.serverFQDN == NULL) { 1380 free(sinfo.server); 1381 return (NS_LDAP_MEMORY); 1382 } 1383 } 1384 rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp, 1385 fail_if_new_pwd_reqd, passwd_mgmt); 1386 if (rc == NS_LDAP_SUCCESS || rc == 1387 NS_LDAP_SUCCESS_WITH_INFO) { 1388 exit_rc = rc; 1389 goto create_con; 1390 } else { 1391 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) { 1392 (void) snprintf(errmsg, sizeof (errmsg), 1393 "%s %s", gettext("makeConnection: " 1394 "failed to open connection using " 1395 "sasl/GSSAPI to"), *bindHost); 1396 } else { 1397 (void) snprintf(errmsg, sizeof (errmsg), 1398 "%s %s", gettext("makeConnection: " 1399 "failed to open connection to"), 1400 *bindHost); 1401 } 1402 syslog(LOG_ERR, "libsldap: %s", errmsg); 1403 __s_api_free_server_info(&sinfo); 1404 return (rc); 1405 } 1406 } 1407 1408 /* No cached connection, create one */ 1409 for (; ; ) { 1410 if (host == NULL) 1411 hReq = NS_CACHE_NEW; 1412 else 1413 hReq = NS_CACHE_NEXT; 1414 rc = __s_api_requestServer(hReq, host, &sinfo, errorp, 1415 serverAddrType); 1416 if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) || 1417 (host && (strcasecmp(host, sinfo.server) == 0))) { 1418 /* Log the error */ 1419 if (*errorp) { 1420 (void) snprintf(errmsg, sizeof (errmsg), 1421 "%s: (%s)", gettext("makeConnection: " 1422 "unable to make LDAP connection, " 1423 "request for a server failed"), 1424 (*errorp)->message); 1425 syslog(LOG_ERR, "libsldap: %s", errmsg); 1426 } 1427 1428 __s_api_free_server_info(&sinfo); 1429 if (host) 1430 free(host); 1431 return (NS_LDAP_OP_FAILED); 1432 } 1433 if (host) 1434 free(host); 1435 host = strdup(sinfo.server); 1436 if (host == NULL) { 1437 __s_api_free_server_info(&sinfo); 1438 return (NS_LDAP_MEMORY); 1439 } 1440 1441 /* check if server supports password management */ 1442 passwd_mgmt = __s_api_contain_passwd_control_oid( 1443 sinfo.controls); 1444 /* check if server supports password less account mgmt */ 1445 if (nopasswd_acct_mgmt && 1446 !__s_api_contain_account_usable_control_oid( 1447 sinfo.controls)) { 1448 syslog(LOG_WARNING, "libsldap: server %s does not " 1449 "provide account information without password", 1450 host); 1451 free(host); 1452 __s_api_free_server_info(&sinfo); 1453 return (NS_LDAP_OP_FAILED); 1454 } 1455 /* make the connection */ 1456 rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp, 1457 fail_if_new_pwd_reqd, passwd_mgmt); 1458 /* if success, go to create connection structure */ 1459 if (rc == NS_LDAP_SUCCESS || 1460 rc == NS_LDAP_SUCCESS_WITH_INFO) { 1461 exit_rc = rc; 1462 break; 1463 } 1464 1465 /* 1466 * If not able to reach the server, inform the ldap 1467 * cache manager that the server should be removed 1468 * from its server list. Thus, the manager will not 1469 * return this server on the next get-server request 1470 * and will also reduce the server list refresh TTL, 1471 * so that it will find out sooner when the server 1472 * is up again. 1473 */ 1474 if (rc == NS_LDAP_INTERNAL && *errorp != NULL) { 1475 if ((*errorp)->status == LDAP_CONNECT_ERROR || 1476 (*errorp)->status == LDAP_SERVER_DOWN) { 1477 /* Reset memory allocation error */ 1478 memerr = 0; 1479 /* 1480 * We contacted a server that we could 1481 * not either authenticate to or contact. 1482 * If it is due to authentication, then 1483 * we need to try the server again. So, 1484 * do not remove the server yet, but 1485 * add it to the bad server list. 1486 * The caller routine will remove 1487 * the servers if: 1488 * a). A good server is found or 1489 * b). All the possible methods 1490 * are tried without finding 1491 * a good server 1492 */ 1493 if (*badsrvrs == NULL) { 1494 if (!(*badsrvrs = (char **)malloc 1495 (sizeof (char *) * NUMTOMALLOC))) { 1496 memerr = 1; 1497 } 1498 /* Allocate memory in chunks of NUMTOMALLOC */ 1499 } else if ((totalbad % NUMTOMALLOC) == 1500 NUMTOMALLOC - 1) { 1501 char **tmpptr; 1502 if (!(tmpptr = (char **)realloc( 1503 *badsrvrs, 1504 (sizeof (char *) * NUMTOMALLOC * 1505 ((totalbad/NUMTOMALLOC) + 2))))) { 1506 memerr = 1; 1507 } else { 1508 *badsrvrs = tmpptr; 1509 } 1510 } 1511 /* 1512 * Store host only if there were no unsuccessful 1513 * memory allocations above 1514 */ 1515 if (!memerr && 1516 !((*badsrvrs)[totalbad++] = strdup(host))) { 1517 memerr = 1; 1518 totalbad--; 1519 } 1520 (*badsrvrs)[totalbad] = NULL; 1521 } 1522 } 1523 1524 /* else, cleanup and go for the next server */ 1525 __s_api_free_server_info(&sinfo); 1526 1527 /* Return if we had memory allocation errors */ 1528 if (memerr) 1529 return (NS_LDAP_MEMORY); 1530 if (*errorp) { 1531 /* 1532 * If openConnection() failed due to 1533 * password policy, or invalid credential, 1534 * keep *errorp and exit 1535 */ 1536 if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD || 1537 (*errorp)->status == LDAP_INVALID_CREDENTIALS) { 1538 free(host); 1539 return (rc); 1540 } else { 1541 (void) __ns_ldap_freeError(errorp); 1542 *errorp = NULL; 1543 } 1544 } 1545 } 1546 1547 create_con: 1548 /* we have created ld, setup con structure */ 1549 if (host) 1550 free(host); 1551 if ((con = calloc(1, sizeof (Connection))) == NULL) { 1552 __s_api_free_server_info(&sinfo); 1553 /* 1554 * If password control attached in **errorp, 1555 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 1556 * free the error structure 1557 */ 1558 if (*errorp) { 1559 (void) __ns_ldap_freeError(errorp); 1560 *errorp = NULL; 1561 } 1562 (void) ldap_unbind(ld); 1563 return (NS_LDAP_MEMORY); 1564 } 1565 1566 con->serverAddr = sinfo.server; /* Store original format */ 1567 if (sinfo.serverFQDN != NULL) { 1568 free(sinfo.serverFQDN); 1569 sinfo.serverFQDN = NULL; 1570 } 1571 con->saslMechanisms = sinfo.saslMechanisms; 1572 con->controls = sinfo.controls; 1573 1574 con->auth = __ns_ldap_dupAuth(auth); 1575 if (con->auth == NULL) { 1576 (void) ldap_unbind(ld); 1577 freeConnection(con); 1578 /* 1579 * If password control attached in **errorp, 1580 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 1581 * free the error structure 1582 */ 1583 if (*errorp) { 1584 (void) __ns_ldap_freeError(errorp); 1585 *errorp = NULL; 1586 } 1587 return (NS_LDAP_MEMORY); 1588 } 1589 1590 con->threadID = thr_self(); 1591 con->pid = getpid(); 1592 1593 con->ld = ld; 1594 if ((id = addConnection(con)) == -1) { 1595 (void) ldap_unbind(ld); 1596 freeConnection(con); 1597 /* 1598 * If password control attached in **errorp, 1599 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 1600 * free the error structure 1601 */ 1602 if (*errorp) { 1603 (void) __ns_ldap_freeError(errorp); 1604 *errorp = NULL; 1605 } 1606 return (NS_LDAP_MEMORY); 1607 } 1608 #ifdef DEBUG 1609 (void) fprintf(stderr, "tid= %d: connection added into " 1610 "cache %d\n", thr_self(), id); 1611 fflush(stderr); 1612 #endif /* DEBUG */ 1613 *cID = id; 1614 *conp = con; 1615 return (exit_rc); 1616 } 1617 1618 /* 1619 * Return the specified connection to the pool. If necessary 1620 * delete the connection. 1621 */ 1622 1623 static void 1624 _DropConnection(ConnectionID cID, int flag, int fini) 1625 { 1626 Connection *cp; 1627 int id; 1628 int use_lock = !fini; 1629 #ifdef DEBUG 1630 thread_t t = thr_self(); 1631 #endif /* DEBUG */ 1632 1633 id = cID - CONID_OFFSET; 1634 if (id < 0 || id >= sessionPoolSize) 1635 return; 1636 #ifdef DEBUG 1637 (void) fprintf(stderr, "tid= %d: " 1638 "Dropping connection cID=%d flag=0x%x, fini = %d\n", 1639 t, cID, flag, fini); 1640 fflush(stderr); 1641 #endif /* DEBUG */ 1642 if (use_lock) 1643 (void) rw_wrlock(&sessionPoolLock); 1644 1645 cp = sessionPool[id]; 1646 /* sanity check before removing */ 1647 if (!cp || (!fini && !cp->shared && !cp->usedBit)) { 1648 #ifdef DEBUG 1649 if (cp == NULL) 1650 (void) fprintf(stderr, "tid= %d: no " 1651 "need to remove (fini = %d, cp = %p)\n", t, 1652 fini, cp); 1653 else 1654 (void) fprintf(stderr, "tid= %d: no " 1655 "need to remove (fini = %d, cp = %p, shared = %d)" 1656 "\n", t, fini, cp, cp->shared); 1657 fflush(stderr); 1658 #endif /* DEBUG */ 1659 if (use_lock) 1660 (void) rw_unlock(&sessionPoolLock); 1661 return; 1662 } 1663 1664 if (!fini && 1665 ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail && 1666 ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc())) { 1667 #ifdef DEBUG 1668 (void) fprintf(stderr, "tid= %d: keep alive (fini = %d " 1669 "shared = %d)\n", t, fini, cp->shared); 1670 #endif /* DEBUG */ 1671 /* release Connection (keep alive) */ 1672 if (cp->shared) 1673 cp->shared--; 1674 cp->usedBit = B_FALSE; 1675 cp->threadID = 0; /* unmark the threadID */ 1676 if (use_lock) 1677 (void) rw_unlock(&sessionPoolLock); 1678 } else { 1679 /* delete Connection (disconnect) */ 1680 if (cp->shared > 0) { 1681 #ifdef DEBUG 1682 (void) fprintf(stderr, "tid= %d: Connection no " 1683 "longer available (fini = %d, shared = %d)\n", 1684 t, fini, cp->shared); 1685 fflush(stderr); 1686 #endif /* DEBUG */ 1687 cp->shared--; 1688 /* 1689 * Mark this connection not available and decrement 1690 * sharedConnNumber. There could be multiple threads 1691 * sharing this connection so decrement 1692 * sharedConnNumber only once per connection. 1693 */ 1694 if (cp->notAvail == 0) { 1695 cp->notAvail = 1; 1696 (void) mutex_lock(&sharedConnNumberLock); 1697 sharedConnNumber--; 1698 (void) mutex_unlock(&sharedConnNumberLock); 1699 } 1700 } 1701 1702 if (cp->shared <= 0) { 1703 #ifdef DEBUG 1704 (void) fprintf(stderr, "tid= %d: unbind " 1705 "(fini = %d, shared = %d)\n", 1706 t, fini, cp->shared); 1707 fflush(stderr); 1708 #endif /* DEBUG */ 1709 sessionPool[id] = NULL; 1710 (void) ldap_unbind(cp->ld); 1711 freeConnection(cp); 1712 } 1713 1714 if (use_lock) 1715 (void) rw_unlock(&sessionPoolLock); 1716 } 1717 } 1718 1719 void 1720 DropConnection(ConnectionID cID, int flag) 1721 { 1722 _DropConnection(cID, flag, 0); 1723 } 1724 1725 /* 1726 * This routine is called after a bind operation is 1727 * done in openConnection() to process the password 1728 * management information, if any. 1729 * 1730 * Input: 1731 * bind_type: "simple" or "sasl/DIGEST-MD5" 1732 * ldaprc : ldap rc from the ldap bind operation 1733 * controls : controls returned by the server 1734 * errmsg : error message from the server 1735 * fail_if_new_pwd_reqd: 1736 * flag indicating if connection should be open 1737 * when password needs to change immediately 1738 * passwd_mgmt: 1739 * flag indicating if server supports password 1740 * policy/management 1741 * 1742 * Output : ns_ldap_error structure, which may contain 1743 * password status and number of seconds until 1744 * expired 1745 * 1746 * return rc: 1747 * NS_LDAP_EXTERNAL: error, connection should not open 1748 * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached 1749 * NS_LDAP_SUCCESS: OK to open connection 1750 * 1751 */ 1752 1753 static int 1754 process_pwd_mgmt(char *bind_type, int ldaprc, 1755 LDAPControl **controls, 1756 char *errmsg, ns_ldap_error_t **errorp, 1757 int fail_if_new_pwd_reqd, 1758 int passwd_mgmt) 1759 { 1760 char errstr[MAXERROR]; 1761 LDAPControl **ctrl = NULL; 1762 int exit_rc; 1763 ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD; 1764 int sec_until_exp = 0; 1765 1766 /* 1767 * errmsg may be an empty string, 1768 * even if ldaprc is LDAP_SUCCESS, 1769 * free the empty string if that's the case 1770 */ 1771 if (errmsg && 1772 (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) { 1773 ldap_memfree(errmsg); 1774 errmsg = NULL; 1775 } 1776 1777 if (ldaprc != LDAP_SUCCESS) { 1778 /* 1779 * try to map ldap rc and error message to 1780 * a password status 1781 */ 1782 if (errmsg) { 1783 if (passwd_mgmt) 1784 pwd_status = 1785 __s_api_set_passwd_status( 1786 ldaprc, errmsg); 1787 ldap_memfree(errmsg); 1788 } 1789 1790 (void) snprintf(errstr, sizeof (errstr), 1791 gettext("openConnection: " 1792 "%s bind failed " 1793 "- %s"), bind_type, ldap_err2string(ldaprc)); 1794 1795 if (pwd_status != NS_PASSWD_GOOD) { 1796 MKERROR_PWD_MGMT(*errorp, 1797 ldaprc, strdup(errstr), 1798 pwd_status, 0, NULL); 1799 } else { 1800 MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr), 1801 NULL); 1802 } 1803 if (controls) 1804 ldap_controls_free(controls); 1805 1806 return (NS_LDAP_INTERNAL); 1807 } 1808 1809 /* 1810 * ldaprc is LDAP_SUCCESS, 1811 * process the password management controls, if any 1812 */ 1813 exit_rc = NS_LDAP_SUCCESS; 1814 if (controls && passwd_mgmt) { 1815 /* 1816 * The control with the OID 1817 * 2.16.840.1.113730.3.4.4 (or 1818 * LDAP_CONTROL_PWEXPIRED, as defined 1819 * in the ldap.h header file) is the 1820 * expired password control. 1821 * 1822 * This control is used if the server 1823 * is configured to require users to 1824 * change their passwords when first 1825 * logging in and whenever the 1826 * passwords are reset. 1827 * 1828 * If the user is logging in for the 1829 * first time or if the user's 1830 * password has been reset, the 1831 * server sends this control to 1832 * indicate that the client needs to 1833 * change the password immediately. 1834 * 1835 * At this point, the only operation 1836 * that the client can perform is to 1837 * change the user's password. If the 1838 * client requests any other LDAP 1839 * operation, the server sends back 1840 * an LDAP_UNWILLING_TO_PERFORM 1841 * result code with an expired 1842 * password control. 1843 * 1844 * The control with the OID 1845 * 2.16.840.1.113730.3.4.5 (or 1846 * LDAP_CONTROL_PWEXPIRING, as 1847 * defined in the ldap.h header file) 1848 * is the password expiration warning 1849 * control. 1850 * 1851 * This control is used if the server 1852 * is configured to expire user 1853 * passwords after a certain amount 1854 * of time. 1855 * 1856 * The server sends this control back 1857 * to the client if the client binds 1858 * using a password that will soon 1859 * expire. The ldctl_value field of 1860 * the LDAPControl structure 1861 * specifies the number of seconds 1862 * before the password will expire. 1863 */ 1864 for (ctrl = controls; *ctrl; ctrl++) { 1865 1866 if (strcmp((*ctrl)->ldctl_oid, 1867 LDAP_CONTROL_PWEXPIRED) == 0) { 1868 /* 1869 * if the caller wants this bind 1870 * to fail, set up the error info. 1871 * If call to this function is 1872 * for searching the LDAP directory, 1873 * e.g., __ns_ldap_list(), 1874 * there's really no sense to 1875 * let a connection open and 1876 * then fail immediately afterward 1877 * on the LDAP search operation with 1878 * the LDAP_UNWILLING_TO_PERFORM rc 1879 */ 1880 pwd_status = 1881 NS_PASSWD_CHANGE_NEEDED; 1882 if (fail_if_new_pwd_reqd) { 1883 (void) snprintf(errstr, 1884 sizeof (errstr), 1885 gettext( 1886 "openConnection: " 1887 "%s bind " 1888 "failed " 1889 "- password " 1890 "expired. It " 1891 " needs to change " 1892 "immediately!"), 1893 bind_type); 1894 MKERROR_PWD_MGMT(*errorp, 1895 LDAP_SUCCESS, 1896 strdup(errstr), 1897 pwd_status, 1898 0, 1899 NULL); 1900 exit_rc = NS_LDAP_INTERNAL; 1901 } else { 1902 MKERROR_PWD_MGMT(*errorp, 1903 LDAP_SUCCESS, 1904 NULL, 1905 pwd_status, 1906 0, 1907 NULL); 1908 exit_rc = 1909 NS_LDAP_SUCCESS_WITH_INFO; 1910 } 1911 break; 1912 } else if (strcmp((*ctrl)->ldctl_oid, 1913 LDAP_CONTROL_PWEXPIRING) == 0) { 1914 pwd_status = 1915 NS_PASSWD_ABOUT_TO_EXPIRE; 1916 if ((*ctrl)-> 1917 ldctl_value.bv_len > 0 && 1918 (*ctrl)-> 1919 ldctl_value.bv_val) 1920 sec_until_exp = 1921 atoi((*ctrl)-> 1922 ldctl_value.bv_val); 1923 MKERROR_PWD_MGMT(*errorp, 1924 LDAP_SUCCESS, 1925 NULL, 1926 pwd_status, 1927 sec_until_exp, 1928 NULL); 1929 exit_rc = 1930 NS_LDAP_SUCCESS_WITH_INFO; 1931 break; 1932 } 1933 } 1934 } 1935 1936 if (controls) 1937 ldap_controls_free(controls); 1938 1939 return (exit_rc); 1940 } 1941 1942 static int 1943 ldap_in_hosts_switch() 1944 { 1945 enum __nsw_parse_err pserr; 1946 struct __nsw_switchconfig *conf; 1947 struct __nsw_lookup *lkp; 1948 const char *name; 1949 int found = 0; 1950 1951 conf = __nsw_getconfig("hosts", &pserr); 1952 if (conf == NULL) { 1953 return (-1); 1954 } 1955 1956 /* check for skip and count other backends */ 1957 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { 1958 name = lkp->service_name; 1959 if (strcmp(name, "ldap") == 0) { 1960 found = 1; 1961 break; 1962 } 1963 } 1964 __nsw_freeconfig(conf); 1965 return (found); 1966 } 1967 1968 static int 1969 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, 1970 int timeoutSec, ns_ldap_error_t **errorp, 1971 int fail_if_new_pwd_reqd, int passwd_mgmt) 1972 { 1973 LDAP *ld = NULL; 1974 char *binddn, *passwd; 1975 char *digest_md5_name; 1976 const char *s; 1977 int ldapVersion = LDAP_VERSION3; 1978 int derefOption = LDAP_DEREF_ALWAYS; 1979 int zero = 0; 1980 int rc; 1981 char errstr[MAXERROR]; 1982 int errnum = 0; 1983 LDAPMessage *resultMsg; 1984 int msgId; 1985 int useSSL = 0, port = 0; 1986 struct timeval tv; 1987 AuthType_t bindType; 1988 int timeoutMilliSec = timeoutSec * 1000; 1989 struct berval cred; 1990 char *sslServerAddr; 1991 char *s1; 1992 char *errmsg, *end = NULL; 1993 LDAPControl **controls; 1994 int pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF; 1995 ns_sasl_cb_param_t sasl_param; 1996 1997 *errorp = NULL; 1998 *ldp = NULL; 1999 2000 switch (auth->auth.type) { 2001 case NS_LDAP_AUTH_NONE: 2002 case NS_LDAP_AUTH_SIMPLE: 2003 case NS_LDAP_AUTH_SASL: 2004 bindType = auth->auth.type; 2005 break; 2006 case NS_LDAP_AUTH_TLS: 2007 useSSL = 1; 2008 switch (auth->auth.tlstype) { 2009 case NS_LDAP_TLS_NONE: 2010 bindType = NS_LDAP_AUTH_NONE; 2011 break; 2012 case NS_LDAP_TLS_SIMPLE: 2013 bindType = NS_LDAP_AUTH_SIMPLE; 2014 break; 2015 case NS_LDAP_TLS_SASL: 2016 bindType = NS_LDAP_AUTH_SASL; 2017 break; 2018 default: 2019 (void) sprintf(errstr, 2020 gettext("openConnection: unsupported " 2021 "TLS authentication method " 2022 "(%d)"), auth->auth.tlstype); 2023 MKERROR(LOG_WARNING, *errorp, 2024 LDAP_AUTH_METHOD_NOT_SUPPORTED, 2025 strdup(errstr), NULL); 2026 return (NS_LDAP_INTERNAL); 2027 } 2028 break; 2029 default: 2030 (void) sprintf(errstr, 2031 gettext("openConnection: unsupported " 2032 "authentication method (%d)"), auth->auth.type); 2033 MKERROR(LOG_WARNING, *errorp, 2034 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 2035 NULL); 2036 return (NS_LDAP_INTERNAL); 2037 } 2038 2039 if (useSSL) { 2040 const char *hostcertpath; 2041 char *alloc_hcp = NULL; 2042 #ifdef DEBUG 2043 (void) fprintf(stderr, "tid= %d: +++TLS transport\n", 2044 thr_self()); 2045 #endif /* DEBUG */ 2046 2047 if (prldap_set_session_option(NULL, NULL, 2048 PRLDAP_OPT_IO_MAX_TIMEOUT, 2049 timeoutMilliSec) != LDAP_SUCCESS) { 2050 (void) snprintf(errstr, sizeof (errstr), 2051 gettext("openConnection: failed to initialize " 2052 "TLS security")); 2053 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 2054 strdup(errstr), NULL); 2055 return (NS_LDAP_INTERNAL); 2056 } 2057 2058 hostcertpath = auth->hostcertpath; 2059 if (hostcertpath == NULL) { 2060 alloc_hcp = __s_get_hostcertpath(); 2061 hostcertpath = alloc_hcp; 2062 } 2063 2064 if (hostcertpath == NULL) 2065 return (NS_LDAP_MEMORY); 2066 2067 if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) { 2068 if (alloc_hcp) 2069 free(alloc_hcp); 2070 (void) snprintf(errstr, sizeof (errstr), 2071 gettext("openConnection: failed to initialize " 2072 "TLS security (%s)"), 2073 ldapssl_err2string(rc)); 2074 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 2075 strdup(errstr), NULL); 2076 return (NS_LDAP_INTERNAL); 2077 } 2078 if (alloc_hcp) 2079 free(alloc_hcp); 2080 2081 /* determine if the host name contains a port number */ 2082 s = strchr(serverAddr, ']'); /* skip over ipv6 addr */ 2083 if (s == NULL) 2084 s = serverAddr; 2085 s = strchr(s, ':'); 2086 if (s != NULL) { 2087 /* 2088 * If we do get a port number, we will try stripping 2089 * it. At present, referrals will always have a 2090 * port number. 2091 */ 2092 sslServerAddr = strdup(serverAddr); 2093 if (sslServerAddr == NULL) 2094 return (NS_LDAP_MEMORY); 2095 s1 = strrchr(sslServerAddr, ':'); 2096 if (s1 != NULL) 2097 *s1 = '\0'; 2098 (void) snprintf(errstr, sizeof (errstr), 2099 gettext("openConnection: cannot use tls with %s. " 2100 "Trying %s"), 2101 serverAddr, sslServerAddr); 2102 syslog(LOG_ERR, "libsldap: %s", errstr); 2103 } else 2104 sslServerAddr = (char *)serverAddr; 2105 2106 ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1); 2107 2108 if (sslServerAddr != serverAddr) 2109 free(sslServerAddr); 2110 2111 if (ld == NULL || 2112 ldapssl_install_gethostbyaddr(ld, "ldap") != 0) { 2113 (void) snprintf(errstr, sizeof (errstr), 2114 gettext("openConnection: failed to connect " 2115 "using TLS (%s)"), strerror(errno)); 2116 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 2117 strdup(errstr), NULL); 2118 return (NS_LDAP_INTERNAL); 2119 } 2120 } else { 2121 #ifdef DEBUG 2122 (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n", 2123 thr_self()); 2124 #endif /* DEBUG */ 2125 port = LDAP_PORT; 2126 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI && 2127 (end = strchr(serverAddr, ':')) != NULL) { 2128 /* 2129 * The IP is converted to hostname so it's a 2130 * hostname:port up to this point. 2131 * 2132 * libldap passes hostname:port to the sasl layer. 2133 * The ldap service principal is constructed as 2134 * ldap/hostname:port@REALM. Kerberos authentication 2135 * will fail. So it needs to be parsed to construct 2136 * a valid principal ldap/hostname@REALM. 2137 * 2138 * For useSSL case above, it already parses port so 2139 * no need to parse serverAddr 2140 */ 2141 *end = '\0'; 2142 port = atoi(end + 1); 2143 } 2144 2145 /* Warning message IF cannot connect to host(s) */ 2146 if ((ld = ldap_init((char *)serverAddr, port)) == NULL) { 2147 char *p = strerror(errno); 2148 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 2149 strdup(p), NULL); 2150 if (end) 2151 *end = ':'; 2152 return (NS_LDAP_INTERNAL); 2153 } else { 2154 if (end) 2155 *end = ':'; 2156 /* check and avoid gethostname recursion */ 2157 if (ldap_in_hosts_switch() > 0 && 2158 ! __s_api_isipv4((char *)serverAddr) && 2159 ! __s_api_isipv6((char *)serverAddr)) { 2160 /* host: ldap - found, attempt to recover */ 2161 if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB, 2162 "ldap") != 0) { 2163 (void) snprintf(errstr, sizeof (errstr), 2164 gettext("openConnection: " 2165 "unrecoverable gethostname " 2166 "recursion detected " 2167 "in /etc/nsswitch.conf")); 2168 MKERROR(LOG_WARNING, *errorp, 2169 LDAP_CONNECT_ERROR, 2170 strdup(errstr), NULL); 2171 (void) ldap_unbind(ld); 2172 return (NS_LDAP_INTERNAL); 2173 } 2174 } 2175 } 2176 } 2177 2178 ns_setup_mt_conn_and_tsd(ld); 2179 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); 2180 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 2181 /* 2182 * set LDAP_OPT_REFERRALS to OFF. 2183 * This library will handle the referral itself 2184 * based on API flags or configuration file 2185 * specification. If this option is not set 2186 * to OFF, libldap will never pass the 2187 * referral info up to this library 2188 */ 2189 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 2190 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 2191 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 2192 /* setup TCP/IP connect timeout */ 2193 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 2194 &timeoutMilliSec); 2195 /* retry if LDAP I/O was interrupted */ 2196 (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 2197 2198 switch (bindType) { 2199 case NS_LDAP_AUTH_NONE: 2200 #ifdef DEBUG 2201 (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n", 2202 thr_self()); 2203 #endif /* DEBUG */ 2204 break; 2205 case NS_LDAP_AUTH_SIMPLE: 2206 binddn = auth->cred.unix_cred.userID; 2207 passwd = auth->cred.unix_cred.passwd; 2208 if (passwd == NULL || *passwd == '\0' || 2209 binddn == NULL || *binddn == '\0') { 2210 (void) sprintf(errstr, gettext("openConnection: " 2211 "missing credentials for Simple bind")); 2212 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 2213 strdup(errstr), NULL); 2214 (void) ldap_unbind(ld); 2215 return (NS_LDAP_INTERNAL); 2216 } 2217 2218 #ifdef DEBUG 2219 (void) fprintf(stderr, "tid= %d: +++Simple bind\n", 2220 thr_self()); 2221 #endif /* DEBUG */ 2222 msgId = ldap_simple_bind(ld, binddn, passwd); 2223 2224 if (msgId == -1) { 2225 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 2226 (void *)&errnum); 2227 (void) snprintf(errstr, sizeof (errstr), 2228 gettext("openConnection: simple bind failed " 2229 "- %s"), ldap_err2string(errnum)); 2230 (void) ldap_unbind(ld); 2231 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 2232 NULL); 2233 return (NS_LDAP_INTERNAL); 2234 } 2235 2236 tv.tv_sec = timeoutSec; 2237 tv.tv_usec = 0; 2238 rc = ldap_result(ld, msgId, 0, &tv, &resultMsg); 2239 2240 if ((rc == -1) || (rc == 0)) { 2241 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 2242 (void *)&errnum); 2243 (void) snprintf(errstr, sizeof (errstr), 2244 gettext("openConnection: simple bind failed " 2245 "- %s"), ldap_err2string(errnum)); 2246 (void) ldap_msgfree(resultMsg); 2247 (void) ldap_unbind(ld); 2248 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 2249 NULL); 2250 return (NS_LDAP_INTERNAL); 2251 } 2252 2253 /* 2254 * get ldaprc, controls, and error msg 2255 */ 2256 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 2257 &errmsg, NULL, &controls, 1); 2258 2259 if (rc != LDAP_SUCCESS) { 2260 (void) snprintf(errstr, sizeof (errstr), 2261 gettext("openConnection: simple bind failed " 2262 "- unable to parse result")); 2263 (void) ldap_unbind(ld); 2264 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2265 strdup(errstr), NULL); 2266 return (NS_LDAP_INTERNAL); 2267 } 2268 2269 /* process the password management info, if any */ 2270 pwd_rc = process_pwd_mgmt("simple", 2271 errnum, controls, errmsg, 2272 errorp, 2273 fail_if_new_pwd_reqd, 2274 passwd_mgmt); 2275 2276 if (pwd_rc == NS_LDAP_INTERNAL) { 2277 (void) ldap_unbind(ld); 2278 return (pwd_rc); 2279 } 2280 2281 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 2282 *ldp = ld; 2283 return (pwd_rc); 2284 } 2285 2286 break; 2287 case NS_LDAP_AUTH_SASL: 2288 if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE && 2289 auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) { 2290 (void) sprintf(errstr, 2291 gettext("openConnection: SASL options are " 2292 "not supported (%d) for non-GSSAPI sasl bind"), 2293 auth->auth.saslopt); 2294 MKERROR(LOG_WARNING, *errorp, 2295 LDAP_AUTH_METHOD_NOT_SUPPORTED, 2296 strdup(errstr), NULL); 2297 (void) ldap_unbind(ld); 2298 return (NS_LDAP_INTERNAL); 2299 } 2300 if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) { 2301 binddn = auth->cred.unix_cred.userID; 2302 passwd = auth->cred.unix_cred.passwd; 2303 if (passwd == NULL || *passwd == '\0' || 2304 binddn == NULL || *binddn == '\0') { 2305 (void) sprintf(errstr, 2306 gettext("openConnection: missing " 2307 "credentials for SASL bind")); 2308 MKERROR(LOG_WARNING, *errorp, 2309 LDAP_INVALID_CREDENTIALS, 2310 strdup(errstr), NULL); 2311 (void) ldap_unbind(ld); 2312 return (NS_LDAP_INTERNAL); 2313 } 2314 cred.bv_val = passwd; 2315 cred.bv_len = strlen(passwd); 2316 } 2317 2318 switch (auth->auth.saslmech) { 2319 case NS_LDAP_SASL_CRAM_MD5: 2320 /* 2321 * NOTE: if iDS changes to support cram_md5, 2322 * please add password management code here. 2323 * Since ldap_sasl_cram_md5_bind_s does not 2324 * return anything that could be used to 2325 * extract the ldap rc/errmsg/control to 2326 * determine if bind failed due to password 2327 * policy, a new cram_md5_bind API will need 2328 * to be introduced. See 2329 * ldap_x_sasl_digest_md5_bind() and case 2330 * NS_LDAP_SASL_DIGEST_MD5 below for details. 2331 */ 2332 if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn, 2333 &cred, NULL, NULL)) != LDAP_SUCCESS) { 2334 (void) ldap_get_option(ld, 2335 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 2336 (void) snprintf(errstr, sizeof (errstr), 2337 gettext("openConnection: " 2338 "sasl/CRAM-MD5 bind failed - %s"), 2339 ldap_err2string(errnum)); 2340 MKERROR(LOG_WARNING, *errorp, errnum, 2341 strdup(errstr), NULL); 2342 (void) ldap_unbind(ld); 2343 return (NS_LDAP_INTERNAL); 2344 } 2345 break; 2346 case NS_LDAP_SASL_DIGEST_MD5: 2347 digest_md5_name = malloc(strlen(binddn) + 5); 2348 /* 5 = strlen("dn: ") + 1 */ 2349 if (digest_md5_name == NULL) { 2350 (void) ldap_unbind(ld); 2351 return (NS_LDAP_MEMORY); 2352 } 2353 (void) strcpy(digest_md5_name, "dn: "); 2354 (void) strcat(digest_md5_name, binddn); 2355 2356 tv.tv_sec = timeoutSec; 2357 tv.tv_usec = 0; 2358 rc = ldap_x_sasl_digest_md5_bind(ld, 2359 digest_md5_name, &cred, NULL, NULL, 2360 &tv, &resultMsg); 2361 2362 if (resultMsg == NULL) { 2363 free(digest_md5_name); 2364 (void) ldap_get_option(ld, 2365 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 2366 (void) snprintf(errstr, sizeof (errstr), 2367 gettext("openConnection: " 2368 "DIGEST-MD5 bind failed - %s"), 2369 ldap_err2string(errnum)); 2370 (void) ldap_unbind(ld); 2371 MKERROR(LOG_WARNING, *errorp, errnum, 2372 strdup(errstr), NULL); 2373 return (NS_LDAP_INTERNAL); 2374 } 2375 2376 /* 2377 * get ldaprc, controls, and error msg 2378 */ 2379 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 2380 &errmsg, NULL, &controls, 1); 2381 2382 if (rc != LDAP_SUCCESS) { 2383 free(digest_md5_name); 2384 (void) snprintf(errstr, sizeof (errstr), 2385 gettext("openConnection: " 2386 "DIGEST-MD5 bind failed " 2387 "- unable to parse result")); 2388 (void) ldap_unbind(ld); 2389 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2390 strdup(errstr), NULL); 2391 return (NS_LDAP_INTERNAL); 2392 } 2393 2394 /* process the password management info, if any */ 2395 pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5", 2396 errnum, controls, errmsg, 2397 errorp, 2398 fail_if_new_pwd_reqd, 2399 passwd_mgmt); 2400 2401 if (pwd_rc == NS_LDAP_INTERNAL) { 2402 free(digest_md5_name); 2403 (void) ldap_unbind(ld); 2404 return (pwd_rc); 2405 } 2406 2407 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 2408 *ldp = ld; 2409 return (pwd_rc); 2410 } 2411 2412 free(digest_md5_name); 2413 break; 2414 case NS_LDAP_SASL_GSSAPI: 2415 if (sasl_gssapi_inited == 0) { 2416 rc = __s_api_sasl_gssapi_init(); 2417 if (rc != NS_LDAP_SUCCESS) { 2418 (void) snprintf(errstr, sizeof (errstr), 2419 gettext("openConnection: " 2420 "GSSAPI initialization " 2421 "failed")); 2422 (void) ldap_unbind(ld); 2423 MKERROR(LOG_WARNING, *errorp, rc, 2424 strdup(errstr), NULL); 2425 return (rc); 2426 } 2427 } 2428 (void) memset(&sasl_param, 0, 2429 sizeof (ns_sasl_cb_param_t)); 2430 sasl_param.authid = NULL; 2431 sasl_param.authzid = ""; 2432 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN, 2433 (void *)&min_ssf); 2434 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX, 2435 (void *)&max_ssf); 2436 2437 rc = ldap_sasl_interactive_bind_s( 2438 ld, NULL, "GSSAPI", 2439 NULL, NULL, LDAP_SASL_INTERACTIVE, 2440 __s_api_sasl_bind_callback, 2441 &sasl_param); 2442 2443 if (rc != LDAP_SUCCESS) { 2444 (void) snprintf(errstr, sizeof (errstr), 2445 gettext("openConnection: " 2446 "GSSAPI bind failed " 2447 "- %d %s"), rc, ldap_err2string(rc)); 2448 (void) ldap_unbind(ld); 2449 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2450 strdup(errstr), NULL); 2451 return (NS_LDAP_INTERNAL); 2452 } 2453 2454 break; 2455 default: 2456 (void) ldap_unbind(ld); 2457 (void) sprintf(errstr, 2458 gettext("openConnection: unsupported SASL " 2459 "mechanism (%d)"), auth->auth.saslmech); 2460 MKERROR(LOG_WARNING, *errorp, 2461 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 2462 NULL); 2463 return (NS_LDAP_INTERNAL); 2464 } 2465 } 2466 2467 *ldp = ld; 2468 return (NS_LDAP_SUCCESS); 2469 } 2470 2471 /* 2472 * FUNCTION: __s_api_getDefaultAuth 2473 * 2474 * Constructs a credential for authentication using the config module. 2475 * 2476 * RETURN VALUES: 2477 * 2478 * NS_LDAP_SUCCESS If successful 2479 * NS_LDAP_CONFIG If there are any config errors. 2480 * NS_LDAP_MEMORY Memory errors. 2481 * NS_LDAP_OP_FAILED If there are no more authentication methods so can 2482 * not build a new authp. 2483 * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the 2484 * necessary fields of a cred for a given auth method 2485 * are not provided. 2486 * INPUT: 2487 * 2488 * cLevel Currently requested credential level to be tried 2489 * 2490 * aMethod Currently requested authentication method to be tried 2491 * 2492 * OUTPUT: 2493 * 2494 * authp authentication method to use. 2495 */ 2496 static int 2497 __s_api_getDefaultAuth( 2498 int *cLevel, 2499 ns_auth_t *aMethod, 2500 ns_cred_t **authp) 2501 { 2502 void **paramVal = NULL; 2503 char *modparamVal = NULL; 2504 int getUid = 0; 2505 int getPasswd = 0; 2506 int getCertpath = 0; 2507 int rc = 0; 2508 ns_ldap_error_t *errorp = NULL; 2509 2510 #ifdef DEBUG 2511 (void) fprintf(stderr, "__s_api_getDefaultAuth START\n"); 2512 #endif 2513 2514 if (aMethod == NULL) { 2515 /* Require an Auth */ 2516 return (NS_LDAP_INVALID_PARAM); 2517 2518 } 2519 /* 2520 * credential level "self" can work with auth method sasl/GSSAPI only 2521 */ 2522 if (cLevel && *cLevel == NS_LDAP_CRED_SELF && 2523 aMethod->saslmech != NS_LDAP_SASL_GSSAPI) 2524 return (NS_LDAP_INVALID_PARAM); 2525 2526 *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); 2527 if ((*authp) == NULL) 2528 return (NS_LDAP_MEMORY); 2529 2530 (*authp)->auth = *aMethod; 2531 2532 switch (aMethod->type) { 2533 case NS_LDAP_AUTH_NONE: 2534 return (NS_LDAP_SUCCESS); 2535 case NS_LDAP_AUTH_SIMPLE: 2536 getUid++; 2537 getPasswd++; 2538 break; 2539 case NS_LDAP_AUTH_SASL: 2540 if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 2541 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) { 2542 getUid++; 2543 getPasswd++; 2544 } else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) { 2545 (void) __ns_ldap_freeCred(authp); 2546 *authp = NULL; 2547 return (NS_LDAP_INVALID_PARAM); 2548 } 2549 break; 2550 case NS_LDAP_AUTH_TLS: 2551 if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) || 2552 ((aMethod->tlstype == NS_LDAP_TLS_SASL) && 2553 ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 2554 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) { 2555 getUid++; 2556 getPasswd++; 2557 getCertpath++; 2558 } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) { 2559 getCertpath++; 2560 } else { 2561 (void) __ns_ldap_freeCred(authp); 2562 *authp = NULL; 2563 return (NS_LDAP_INVALID_PARAM); 2564 } 2565 break; 2566 } 2567 2568 if (getUid) { 2569 paramVal = NULL; 2570 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P, 2571 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 2572 (void) __ns_ldap_freeCred(authp); 2573 (void) __ns_ldap_freeError(&errorp); 2574 *authp = NULL; 2575 return (rc); 2576 } 2577 2578 if (paramVal == NULL || *paramVal == NULL) { 2579 (void) __ns_ldap_freeCred(authp); 2580 *authp = NULL; 2581 return (NS_LDAP_INVALID_PARAM); 2582 } 2583 2584 (*authp)->cred.unix_cred.userID = strdup((char *)*paramVal); 2585 (void) __ns_ldap_freeParam(¶mVal); 2586 if ((*authp)->cred.unix_cred.userID == NULL) { 2587 (void) __ns_ldap_freeCred(authp); 2588 *authp = NULL; 2589 return (NS_LDAP_MEMORY); 2590 } 2591 } 2592 if (getPasswd) { 2593 paramVal = NULL; 2594 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_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 modparamVal = dvalue((char *)*paramVal); 2609 (void) __ns_ldap_freeParam(¶mVal); 2610 if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) { 2611 (void) __ns_ldap_freeCred(authp); 2612 if (modparamVal != NULL) 2613 free(modparamVal); 2614 *authp = NULL; 2615 return (NS_LDAP_INVALID_PARAM); 2616 } 2617 2618 (*authp)->cred.unix_cred.passwd = modparamVal; 2619 } 2620 if (getCertpath) { 2621 paramVal = NULL; 2622 if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P, 2623 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 2624 (void) __ns_ldap_freeCred(authp); 2625 (void) __ns_ldap_freeError(&errorp); 2626 *authp = NULL; 2627 return (rc); 2628 } 2629 2630 if (paramVal == NULL || *paramVal == NULL) { 2631 (void) __ns_ldap_freeCred(authp); 2632 *authp = NULL; 2633 return (NS_LDAP_INVALID_PARAM); 2634 } 2635 2636 (*authp)->hostcertpath = strdup((char *)*paramVal); 2637 (void) __ns_ldap_freeParam(¶mVal); 2638 if ((*authp)->hostcertpath == NULL) { 2639 (void) __ns_ldap_freeCred(authp); 2640 *authp = NULL; 2641 return (NS_LDAP_MEMORY); 2642 } 2643 } 2644 return (NS_LDAP_SUCCESS); 2645 } 2646 2647 /* 2648 * FUNCTION: __s_api_getConnection 2649 * 2650 * Bind to the specified server or one from the server 2651 * list and return the pointer. 2652 * 2653 * This function can rebind or not (NS_LDAP_HARD), it can require a 2654 * credential or bind anonymously 2655 * 2656 * This function follows the DUA configuration schema algorithm 2657 * 2658 * RETURN VALUES: 2659 * 2660 * NS_LDAP_SUCCESS A connection was made successfully. 2661 * NS_LDAP_SUCCESS_WITH_INFO 2662 * A connection was made successfully, but with 2663 * password management info in *errorp 2664 * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function. 2665 * NS_LDAP_CONFIG If there are any config errors. 2666 * NS_LDAP_MEMORY Memory errors. 2667 * NS_LDAP_INTERNAL If there was a ldap error. 2668 * 2669 * INPUT: 2670 * 2671 * server Bind to this LDAP server only 2672 * flags If NS_LDAP_HARD is set function will not return until it has 2673 * a connection unless there is a authentication problem. 2674 * If NS_LDAP_NEW_CONN is set the function must force a new 2675 * connection to be created 2676 * If NS_LDAP_KEEP_CONN is set the connection is to be kept open 2677 * auth Credentials for bind. This could be NULL in which case 2678 * a default cred built from the config module is used. 2679 * sessionId cookie that points to a previous session 2680 * fail_if_new_pwd_reqd 2681 * a flag indicating this function should fail if the passwd 2682 * in auth needs to change immediately 2683 * nopasswd_acct_mgmt 2684 * a flag indicating that makeConnection should check before 2685 * binding if server supports LDAP V3 password less 2686 * account management 2687 * 2688 * OUTPUT: 2689 * 2690 * session pointer to a session with connection information 2691 * errorp Set if there are any INTERNAL, or CONFIG error. 2692 */ 2693 int 2694 __s_api_getConnection( 2695 const char *server, 2696 const int flags, 2697 const ns_cred_t *cred, /* credentials for bind */ 2698 ConnectionID *sessionId, 2699 Connection **session, 2700 ns_ldap_error_t **errorp, 2701 int fail_if_new_pwd_reqd, 2702 int nopasswd_acct_mgmt) 2703 { 2704 char errmsg[MAXERROR]; 2705 ns_auth_t **aMethod = NULL; 2706 ns_auth_t **aNext = NULL; 2707 int **cLevel = NULL; 2708 int **cNext = NULL; 2709 int timeoutSec = NS_DEFAULT_BIND_TIMEOUT; 2710 int rc; 2711 Connection *con = NULL; 2712 int sec = 1; 2713 ns_cred_t *authp = NULL; 2714 ns_cred_t anon; 2715 int version = NS_LDAP_V2, self_gssapi_only = 0; 2716 void **paramVal = NULL; 2717 char **badSrvrs = NULL; /* List of problem hostnames */ 2718 2719 if ((session == NULL) || (sessionId == NULL)) { 2720 return (NS_LDAP_INVALID_PARAM); 2721 } 2722 *session = NULL; 2723 2724 /* if we already have a session id try to reuse connection */ 2725 if (*sessionId > 0) { 2726 rc = findConnectionById(flags, cred, *sessionId, &con); 2727 if (rc == *sessionId && con) { 2728 *session = con; 2729 return (NS_LDAP_SUCCESS); 2730 } 2731 *sessionId = 0; 2732 } 2733 2734 /* get profile version number */ 2735 if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, 2736 ¶mVal, errorp)) != NS_LDAP_SUCCESS) 2737 return (rc); 2738 if (paramVal == NULL) { 2739 (void) sprintf(errmsg, gettext("getConnection: no file " 2740 "version")); 2741 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg), 2742 NS_LDAP_CONFIG); 2743 return (NS_LDAP_CONFIG); 2744 } 2745 if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0) 2746 version = NS_LDAP_V1; 2747 (void) __ns_ldap_freeParam((void ***)¶mVal); 2748 2749 /* Get the bind timeout value */ 2750 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, ¶mVal, errorp); 2751 if (paramVal != NULL && *paramVal != NULL) { 2752 timeoutSec = **((int **)paramVal); 2753 (void) __ns_ldap_freeParam(¶mVal); 2754 } 2755 if (*errorp) 2756 (void) __ns_ldap_freeError(errorp); 2757 2758 if (cred == NULL) { 2759 /* Get the authentication method list */ 2760 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, 2761 (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS) 2762 return (rc); 2763 if (aMethod == NULL) { 2764 aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *)); 2765 if (aMethod == NULL) 2766 return (NS_LDAP_MEMORY); 2767 aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t)); 2768 if (aMethod[0] == NULL) { 2769 free(aMethod); 2770 return (NS_LDAP_MEMORY); 2771 } 2772 if (version == NS_LDAP_V1) 2773 (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE; 2774 else { 2775 (aMethod[0])->type = NS_LDAP_AUTH_SASL; 2776 (aMethod[0])->saslmech = 2777 NS_LDAP_SASL_DIGEST_MD5; 2778 (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE; 2779 } 2780 } 2781 2782 /* Get the credential level list */ 2783 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P, 2784 (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) { 2785 (void) __ns_ldap_freeParam((void ***)&aMethod); 2786 return (rc); 2787 } 2788 if (cLevel == NULL) { 2789 cLevel = (int **)calloc(2, sizeof (int *)); 2790 if (cLevel == NULL) 2791 return (NS_LDAP_MEMORY); 2792 cLevel[0] = (int *)calloc(1, sizeof (int)); 2793 if (cLevel[0] == NULL) 2794 return (NS_LDAP_MEMORY); 2795 if (version == NS_LDAP_V1) 2796 *(cLevel[0]) = NS_LDAP_CRED_PROXY; 2797 else 2798 *(cLevel[0]) = NS_LDAP_CRED_ANON; 2799 } 2800 } 2801 2802 /* setup the anon credential for anonymous connection */ 2803 (void) memset(&anon, 0, sizeof (ns_cred_t)); 2804 anon.auth.type = NS_LDAP_AUTH_NONE; 2805 2806 for (; ; ) { 2807 if (cred != NULL) { 2808 /* using specified auth method */ 2809 rc = makeConnection(&con, server, cred, 2810 sessionId, timeoutSec, errorp, 2811 fail_if_new_pwd_reqd, 2812 nopasswd_acct_mgmt, flags, &badSrvrs); 2813 /* not using bad server if credentials were supplied */ 2814 if (badSrvrs && *badSrvrs) { 2815 __s_api_free2dArray(badSrvrs); 2816 badSrvrs = NULL; 2817 } 2818 if (rc == NS_LDAP_SUCCESS || 2819 rc == NS_LDAP_SUCCESS_WITH_INFO) { 2820 *session = con; 2821 break; 2822 } 2823 } else { 2824 self_gssapi_only = __s_api_self_gssapi_only_get(); 2825 /* for every cred level */ 2826 for (cNext = cLevel; *cNext != NULL; cNext++) { 2827 if (self_gssapi_only && 2828 **cNext != NS_LDAP_CRED_SELF) 2829 continue; 2830 if (**cNext == NS_LDAP_CRED_ANON) { 2831 /* 2832 * make connection anonymously 2833 * Free the down server list before 2834 * looping through 2835 */ 2836 if (badSrvrs && *badSrvrs) { 2837 __s_api_free2dArray(badSrvrs); 2838 badSrvrs = NULL; 2839 } 2840 rc = makeConnection(&con, server, &anon, 2841 sessionId, timeoutSec, errorp, 2842 fail_if_new_pwd_reqd, 2843 nopasswd_acct_mgmt, flags, 2844 &badSrvrs); 2845 if (rc == NS_LDAP_SUCCESS || 2846 rc == 2847 NS_LDAP_SUCCESS_WITH_INFO) { 2848 *session = con; 2849 goto done; 2850 } 2851 continue; 2852 } 2853 /* for each cred level */ 2854 for (aNext = aMethod; *aNext != NULL; aNext++) { 2855 if (self_gssapi_only && 2856 (*aNext)->saslmech != 2857 NS_LDAP_SASL_GSSAPI) 2858 continue; 2859 /* 2860 * self coexists with sasl/GSSAPI only 2861 * and non-self coexists with non-gssapi 2862 * only 2863 */ 2864 if ((**cNext == NS_LDAP_CRED_SELF && 2865 (*aNext)->saslmech != 2866 NS_LDAP_SASL_GSSAPI) || 2867 (**cNext != NS_LDAP_CRED_SELF && 2868 (*aNext)->saslmech == 2869 NS_LDAP_SASL_GSSAPI)) 2870 continue; 2871 /* make connection and authenticate */ 2872 /* with default credentials */ 2873 authp = NULL; 2874 rc = __s_api_getDefaultAuth(*cNext, 2875 *aNext, &authp); 2876 if (rc != NS_LDAP_SUCCESS) { 2877 continue; 2878 } 2879 /* 2880 * Free the down server list before 2881 * looping through 2882 */ 2883 if (badSrvrs && *badSrvrs) { 2884 __s_api_free2dArray(badSrvrs); 2885 badSrvrs = NULL; 2886 } 2887 rc = makeConnection(&con, server, authp, 2888 sessionId, timeoutSec, errorp, 2889 fail_if_new_pwd_reqd, 2890 nopasswd_acct_mgmt, flags, 2891 &badSrvrs); 2892 (void) __ns_ldap_freeCred(&authp); 2893 if (rc == NS_LDAP_SUCCESS || 2894 rc == 2895 NS_LDAP_SUCCESS_WITH_INFO) { 2896 *session = con; 2897 goto done; 2898 } 2899 } 2900 } 2901 } 2902 if (flags & NS_LDAP_HARD) { 2903 if (sec < LDAPMAXHARDLOOKUPTIME) 2904 sec *= 2; 2905 _sleep(sec); 2906 } else { 2907 break; 2908 } 2909 } 2910 2911 done: 2912 /* 2913 * If unable to get a connection, and this is 2914 * the thread opening the shared connection, 2915 * unlock the session mutex and let other 2916 * threads try to get their own connection. 2917 */ 2918 if (wait4session != 0 && sessionTid == thr_self()) { 2919 wait4session = 0; 2920 sessionTid = 0; 2921 #ifdef DEBUG 2922 (void) fprintf(stderr, "tid= %d: __s_api_getConnection: " 2923 "unlocking sessionLock \n", thr_self()); 2924 fflush(stderr); 2925 #endif /* DEBUG */ 2926 (void) mutex_unlock(&sessionLock); 2927 } 2928 if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) { 2929 /* 2930 * self_gssapi_only is true but no self/sasl/gssapi is 2931 * configured 2932 */ 2933 rc = NS_LDAP_CONFIG; 2934 } 2935 2936 (void) __ns_ldap_freeParam((void ***)&aMethod); 2937 (void) __ns_ldap_freeParam((void ***)&cLevel); 2938 2939 if (badSrvrs && *badSrvrs) { 2940 /* 2941 * At this point, either we have a successful 2942 * connection or exhausted all the possible auths. 2943 * and creds. Mark the problem servers as down 2944 * so that the problem servers are not contacted 2945 * again until the refresh_ttl expires. 2946 */ 2947 (void) __s_api_removeBadServers(badSrvrs); 2948 __s_api_free2dArray(badSrvrs); 2949 } 2950 return (rc); 2951 } 2952 2953 #pragma fini(_free_sessionPool) 2954 static void 2955 _free_sessionPool() 2956 { 2957 int id; 2958 2959 (void) rw_wrlock(&sessionPoolLock); 2960 if (sessionPool != NULL) { 2961 for (id = 0; id < sessionPoolSize; id++) 2962 _DropConnection(id + CONID_OFFSET, 0, 1); 2963 free(sessionPool); 2964 sessionPool = NULL; 2965 sessionPoolSize = 0; 2966 } 2967 (void) rw_unlock(&sessionPoolLock); 2968 } 2969