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