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