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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 "ns_sldap.h" 43 #include "ns_internal.h" 44 #include "ns_cache_door.h" 45 #include <sys/stat.h> 46 #include <fcntl.h> 47 #include <procfs.h> 48 #include <unistd.h> 49 50 extern unsigned int _sleep(unsigned int); 51 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *, 52 LDAPControl **, LDAPControl **); 53 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip); 54 55 static int openConnection(LDAP **, const char *, const ns_cred_t *, 56 int, ns_ldap_error_t **, int, int); 57 58 static mutex_t sessionPoolLock = DEFAULTMUTEX; 59 60 static Connection **sessionPool = NULL; 61 static int sessionPoolSize = 0; 62 63 64 static mutex_t nscdLock = DEFAULTMUTEX; 65 static int nscdChecked = 0; 66 static pid_t checkedPid = -1; 67 static int isNscd = 0; 68 69 70 /* 71 * Check /proc/PID/psinfo to see if this process is nscd 72 * If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce 73 * constant reconnects for many operations. 74 * A more complete solution is to develop true connection pooling. 75 * However, this is much better than a new connection for every request. 76 */ 77 static int 78 nscd_proc() 79 { 80 pid_t my_pid; 81 psinfo_t pinfo; 82 char fname[BUFSIZ]; 83 int ret; 84 int fd; 85 86 /* Don't bother checking if this process isn't root. */ 87 /* It can't be nscd */ 88 if (getuid() != 0) 89 return (0); 90 91 my_pid = getpid(); 92 if (nscdChecked && (my_pid == checkedPid)) { 93 return (isNscd); 94 } 95 (void) mutex_lock(&nscdLock); 96 if (nscdChecked && (my_pid == checkedPid)) { 97 (void) mutex_unlock(&nscdLock); 98 return (isNscd); 99 } 100 nscdChecked = 1; 101 checkedPid = my_pid; 102 isNscd = 0; 103 if (snprintf(fname, BUFSIZ, "/proc/%d/psinfo", my_pid) != 0) { 104 if ((fd = open(fname, O_RDONLY)) > 0) { 105 ret = read(fd, &pinfo, sizeof (psinfo_t)); 106 (void) close(fd); 107 if (ret == sizeof (psinfo_t) && 108 (strcmp(pinfo.pr_fname, "nscd") == 0)) { 109 /* process runs as root and is named nscd */ 110 /* that's good enough for now */ 111 isNscd = 1; 112 } 113 } 114 } 115 (void) mutex_unlock(&nscdLock); 116 return (isNscd); 117 } 118 119 /* 120 * This function requests a server from the cache manager through 121 * the door functionality 122 */ 123 124 static int 125 __s_api_requestServer(const char *request, const char *server, 126 ns_server_info_t *ret, ns_ldap_error_t **error) 127 { 128 union { 129 ldap_data_t s_d; 130 char s_b[DOORBUFFERSIZE]; 131 } space; 132 ldap_data_t *sptr; 133 int ndata; 134 int adata; 135 char errstr[MAXERROR]; 136 const char *ireq; 137 char *rbuf, *ptr, *rest; 138 char *dptr; 139 char **mptr, **mptr1, **cptr, **cptr1; 140 int mcnt, ccnt; 141 char **servers; 142 int rc; 143 144 if (ret == NULL || error == NULL) { 145 return (NS_LDAP_OP_FAILED); 146 } 147 (void) memset(ret, 0, sizeof (ns_server_info_t)); 148 *error = NULL; 149 150 (void) memset(space.s_b, 0, DOORBUFFERSIZE); 151 152 if (request == NULL) 153 ireq = NS_CACHE_NEW; 154 else 155 ireq = request; 156 157 adata = (sizeof (ldap_call_t) + strlen(ireq) +1); 158 if (server != NULL) { 159 adata += strlen(DOORLINESEP) + 1; 160 adata += strlen(server) + 1; 161 } 162 ndata = sizeof (space); 163 space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER; 164 (void) strcpy(space.s_d.ldap_call.ldap_u.domainname, ireq); 165 if (server != NULL) { 166 (void) strcat(space.s_d.ldap_call.ldap_u.domainname, 167 DOORLINESEP); 168 (void) strcat(space.s_d.ldap_call.ldap_u.domainname, server); 169 } 170 sptr = &space.s_d; 171 172 switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) { 173 case SUCCESS: 174 break; 175 /* this case is for when the $mgr is not running, but ldapclient */ 176 /* is trying to initialize things */ 177 case NOSERVER: 178 /* get first server from config list unavailable otherwise */ 179 servers = NULL; 180 rc = __s_api_getServers(&servers, error); 181 if (rc != NS_LDAP_SUCCESS) { 182 if (servers != NULL) { 183 __s_api_free2dArray(servers); 184 servers = NULL; 185 } 186 return (rc); 187 } 188 if (servers == NULL || servers[0] == NULL) { 189 __s_api_free2dArray(servers); 190 servers = NULL; 191 (void) sprintf(errstr, 192 gettext("No server found in configuration")); 193 MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT, 194 strdup(errstr), NULL); 195 return (NS_LDAP_CONFIG); 196 } 197 ret->server = strdup(servers[0]); 198 if (ret->server == NULL) { 199 __s_api_free2dArray(servers); 200 return (NS_LDAP_MEMORY); 201 } 202 ret->saslMechanisms = NULL; 203 ret->controls = NULL; 204 __s_api_free2dArray(servers); 205 servers = NULL; 206 return (NS_LDAP_SUCCESS); 207 case NOTFOUND: 208 default: 209 return (NS_LDAP_OP_FAILED); 210 } 211 212 /* copy info from door call return structure here */ 213 rbuf = space.s_d.ldap_ret.ldap_u.config; 214 215 /* Get the host */ 216 ptr = strtok_r(rbuf, DOORLINESEP, &rest); 217 if (ptr == NULL) { 218 (void) sprintf(errstr, gettext("No server returned from " 219 "ldap_cachemgr")); 220 MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR, 221 strdup(errstr), NULL); 222 return (NS_LDAP_OP_FAILED); 223 } 224 ret->server = strdup(ptr); 225 if (ret->server == NULL) { 226 return (NS_LDAP_MEMORY); 227 } 228 229 /* get the Supported Controls/SASL mechs */ 230 mptr = NULL; 231 mcnt = 0; 232 cptr = NULL; 233 ccnt = 0; 234 for (; ; ) { 235 ptr = strtok_r(NULL, DOORLINESEP, &rest); 236 if (ptr == NULL) 237 break; 238 if (strncasecmp(ptr, _SASLMECHANISM, 239 _SASLMECHANISM_LEN) == 0) { 240 dptr = strchr(ptr, '='); 241 if (dptr == NULL) 242 continue; 243 dptr++; 244 mptr1 = (char **)realloc((void *)mptr, 245 sizeof (char *) * (mcnt+2)); 246 if (mptr1 == NULL) { 247 __s_api_free2dArray(mptr); 248 if (sptr != &space.s_d) { 249 (void) munmap((char *)sptr, ndata); 250 } 251 __s_api_free2dArray(cptr); 252 free(ret->server); 253 ret->server = NULL; 254 return (NS_LDAP_MEMORY); 255 } 256 mptr = mptr1; 257 mptr[mcnt] = strdup(dptr); 258 if (mptr[mcnt] == NULL) { 259 if (sptr != &space.s_d) { 260 (void) munmap((char *)sptr, ndata); 261 } 262 __s_api_free2dArray(cptr); 263 cptr = NULL; 264 __s_api_free2dArray(mptr); 265 mptr = NULL; 266 free(ret->server); 267 ret->server = NULL; 268 return (NS_LDAP_MEMORY); 269 } 270 mcnt++; 271 mptr[mcnt] = NULL; 272 } 273 if (strncasecmp(ptr, _SUPPORTEDCONTROL, 274 _SUPPORTEDCONTROL_LEN) == 0) { 275 dptr = strchr(ptr, '='); 276 if (dptr == NULL) 277 continue; 278 dptr++; 279 cptr1 = (char **)realloc((void *)cptr, 280 sizeof (char *) * (ccnt+2)); 281 if (cptr1 == NULL) { 282 if (sptr != &space.s_d) { 283 (void) munmap((char *)sptr, ndata); 284 } 285 __s_api_free2dArray(cptr); 286 __s_api_free2dArray(mptr); 287 mptr = NULL; 288 free(ret->server); 289 ret->server = NULL; 290 return (NS_LDAP_MEMORY); 291 } 292 cptr = cptr1; 293 cptr[ccnt] = strdup(dptr); 294 if (cptr[ccnt] == NULL) { 295 if (sptr != &space.s_d) { 296 (void) munmap((char *)sptr, ndata); 297 } 298 __s_api_free2dArray(cptr); 299 cptr = NULL; 300 __s_api_free2dArray(mptr); 301 mptr = NULL; 302 free(ret->server); 303 ret->server = NULL; 304 return (NS_LDAP_MEMORY); 305 } 306 ccnt++; 307 cptr[ccnt] = NULL; 308 } 309 } 310 if (mptr != NULL) { 311 ret->saslMechanisms = mptr; 312 } 313 if (cptr != NULL) { 314 ret->controls = cptr; 315 } 316 317 318 /* clean up door call */ 319 if (sptr != &space.s_d) { 320 (void) munmap((char *)sptr, ndata); 321 } 322 *error = NULL; 323 324 return (NS_LDAP_SUCCESS); 325 } 326 327 #ifdef DEBUG 328 329 /* 330 * printCred(): prints the credential structure 331 */ 332 static void 333 printCred(FILE *fp, const ns_cred_t *cred) 334 { 335 if (fp == NULL) { 336 (void) fprintf(fp, "printCred: fp is NULL\n"); 337 return; 338 } 339 if (cred == NULL) { 340 (void) fprintf(fp, "printCred: cred is NULL\n"); 341 return; 342 } 343 344 (void) fprintf(fp, "AuthType=%d\n", cred->auth.type); 345 (void) fprintf(fp, "TlsType=%d\n", cred->auth.tlstype); 346 (void) fprintf(fp, "SaslMech=%d\n", cred->auth.saslmech); 347 (void) fprintf(fp, "SaslOpt=%d\n", cred->auth.saslopt); 348 if (cred->hostcertpath) 349 (void) fprintf(fp, "hostCertPath=%s\n", cred->hostcertpath); 350 if (cred->cred.unix_cred.userID) 351 (void) fprintf(fp, "userID=%s\n", cred->cred.unix_cred.userID); 352 if (cred->cred.unix_cred.passwd) 353 (void) fprintf(fp, "passwd=%s\n", cred->cred.unix_cred.passwd); 354 } 355 356 /* 357 * printConnection(): prints the connection structure 358 */ 359 static void 360 printConnection(FILE *fp, Connection *con) 361 { 362 if (fp == NULL || con == NULL) 363 return; 364 365 (void) fprintf(fp, "connectionID=%d\n", con->connectionId); 366 (void) fprintf(fp, "usedBit=%d\n", con->usedBit); 367 (void) fprintf(fp, "threadID=%d\n", con->threadID); 368 if (con->serverAddr) { 369 (void) fprintf(fp, "serverAddr=%s\n", con->serverAddr); 370 } 371 printCred(fp, con->auth); 372 (void) fprintf(fp, "-----------------------------------------------\n"); 373 fflush(fp); 374 } 375 376 #endif /* DEBUG */ 377 378 379 /* 380 * addConnection(): inserts a connection in the connection list. 381 * It will also sets use bit and the thread Id for the thread 382 * using the connection for the first time. 383 * Returns: -1 = failure, new Connection ID = success 384 */ 385 static int 386 addConnection(Connection *con) 387 { 388 int i; 389 390 if (!con) 391 return (-1); 392 #ifdef DEBUG 393 (void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID); 394 #endif /* DEBUG */ 395 (void) mutex_lock(&sessionPoolLock); 396 if (sessionPool == NULL) { 397 sessionPoolSize = SESSION_CACHE_INC; 398 sessionPool = calloc(sessionPoolSize, 399 sizeof (struct connection **)); 400 if (!sessionPool) { 401 (void) mutex_unlock(&sessionPoolLock); 402 return (-1); 403 } 404 #ifdef DEBUG 405 (void) fprintf(stderr, "Initialized sessionPool\n"); 406 #endif /* DEBUG */ 407 } 408 for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i) 409 ; 410 if (i == sessionPoolSize) { 411 /* run out of array, need to increase sessionPool */ 412 Connection **cl; 413 cl = (Connection **) realloc(sessionPool, 414 (sessionPoolSize + SESSION_CACHE_INC) * 415 sizeof (Connection *)); 416 if (!cl) { 417 (void) mutex_unlock(&sessionPoolLock); 418 return (-1); 419 } 420 (void) memset(cl + sessionPoolSize, 0, 421 SESSION_CACHE_INC * sizeof (struct connection *)); 422 sessionPool = cl; 423 sessionPoolSize += SESSION_CACHE_INC; 424 #ifdef DEBUG 425 (void) fprintf(stderr, "Increased sessionPoolSize to: %d\n", 426 sessionPoolSize); 427 #endif /* DEBUG */ 428 } 429 sessionPool[i] = con; 430 con->usedBit = B_TRUE; 431 (void) mutex_unlock(&sessionPoolLock); 432 con->connectionId = i + CONID_OFFSET; 433 #ifdef DEBUG 434 (void) fprintf(stderr, "Connection added [%d]\n", i); 435 printConnection(stderr, con); 436 #endif /* DEBUG */ 437 return (i + CONID_OFFSET); 438 } 439 440 /* 441 * See if the specified session matches a currently available 442 */ 443 444 static int 445 findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID, 446 Connection **conp) 447 { 448 Connection *cp; 449 int id; 450 451 if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET) 452 return (-1); 453 *conp = NULL; 454 if (sessionPool == NULL) 455 return (-1); 456 id = cID - CONID_OFFSET; 457 if (id < 0 || id >= sessionPoolSize) 458 return (-1); 459 460 (void) mutex_lock(&sessionPoolLock); 461 if (sessionPool[id] == NULL) { 462 (void) mutex_unlock(&sessionPoolLock); 463 return (-1); 464 } 465 cp = sessionPool[id]; 466 467 /* 468 * Make sure the connection has the same type of authentication method 469 */ 470 if ((cp->usedBit) || 471 (cp->auth->auth.type != auth->auth.type) || 472 (cp->auth->auth.tlstype != auth->auth.tlstype) || 473 (cp->auth->auth.saslmech != auth->auth.saslmech) || 474 (cp->auth->auth.saslopt != auth->auth.saslopt)) { 475 (void) mutex_unlock(&sessionPoolLock); 476 return (-1); 477 } 478 if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) && 479 ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) || 480 (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) || 481 (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) && 482 ((cp->auth->cred.unix_cred.userID == NULL) || 483 (strcasecmp(cp->auth->cred.unix_cred.userID, 484 auth->cred.unix_cred.userID) != 0))) { 485 (void) mutex_unlock(&sessionPoolLock); 486 return (-1); 487 } 488 /* An existing connection is found but it needs to be reset */ 489 if (flags & NS_LDAP_NEW_CONN) { 490 (void) mutex_unlock(&sessionPoolLock); 491 DropConnection(cID, 0); 492 return (-1); 493 } 494 /* found an available connection */ 495 cp->usedBit = B_TRUE; 496 (void) mutex_unlock(&sessionPoolLock); 497 cp->threadID = thr_self(); 498 *conp = cp; 499 return (cID); 500 } 501 502 /* 503 * findConnection(): find an available connection from the list 504 * that matches the criteria specified in Connection structure. 505 * If serverAddr is NULL, then find a connection to any server 506 * as long as it matches the rest of the parameters. 507 * Returns: -1 = failure, the Connection ID found = success. 508 */ 509 static int 510 findConnection(const char *serverAddr, const ns_cred_t *auth, Connection **conp) 511 { 512 Connection *cp; 513 int i; 514 515 if (auth == NULL || conp == NULL) 516 return (-1); 517 *conp = NULL; 518 519 #ifdef DEBUG 520 (void) fprintf(stderr, "Find connection\n"); 521 (void) fprintf(stderr, "Looking for ....\n"); 522 if (serverAddr && *serverAddr) 523 (void) fprintf(stderr, "serverAddr=%s\n", serverAddr); 524 else 525 (void) fprintf(stderr, "serverAddr=NULL\n"); 526 printCred(stderr, auth); 527 fflush(stderr); 528 #endif /* DEBUG */ 529 if (sessionPool == NULL) 530 return (-1); 531 (void) mutex_lock(&sessionPoolLock); 532 for (i = 0; i < sessionPoolSize; ++i) { 533 if (sessionPool[i] == NULL) 534 continue; 535 cp = sessionPool[i]; 536 #ifdef DEBUG 537 (void) fprintf(stderr, "checking connection [%d] ....\n", i); 538 printConnection(stderr, cp); 539 #endif /* DEBUG */ 540 if ((cp->usedBit) || 541 (cp->auth->auth.type != auth->auth.type) || 542 (cp->auth->auth.tlstype != auth->auth.tlstype) || 543 (cp->auth->auth.saslmech != auth->auth.saslmech) || 544 (cp->auth->auth.saslopt != auth->auth.saslopt) || 545 (serverAddr && *serverAddr && 546 (strcasecmp(serverAddr, cp->serverAddr) != 0))) 547 continue; 548 if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) && 549 ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) || 550 (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) || 551 (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) && 552 ((cp->auth->cred.unix_cred.userID == NULL) || 553 (cp->auth->cred.unix_cred.passwd == NULL) || 554 ((strcasecmp(cp->auth->cred.unix_cred.userID, 555 auth->cred.unix_cred.userID) != 0)) || 556 ((strcmp(cp->auth->cred.unix_cred.passwd, 557 auth->cred.unix_cred.passwd) != 0)))) 558 continue; 559 /* found an available connection */ 560 cp->usedBit = B_TRUE; 561 (void) mutex_unlock(&sessionPoolLock); 562 cp->threadID = thr_self(); 563 *conp = cp; 564 #ifdef DEBUG 565 (void) fprintf(stderr, "Connection found cID=%d\n", i); 566 fflush(stderr); 567 #endif /* DEBUG */ 568 return (i + CONID_OFFSET); 569 } 570 (void) mutex_unlock(&sessionPoolLock); 571 return (-1); 572 } 573 574 /* 575 * Free a Connection structure 576 */ 577 static void 578 freeConnection(Connection *con) 579 { 580 if (con == NULL) 581 return; 582 if (con->serverAddr) 583 free(con->serverAddr); 584 if (con->auth) 585 (void) __ns_ldap_freeCred(&(con->auth)); 586 if (con->saslMechanisms) { 587 __s_api_free2dArray(con->saslMechanisms); 588 } 589 if (con->controls) { 590 __s_api_free2dArray(con->controls); 591 } 592 free(con); 593 } 594 595 /* 596 * Find a connection matching the passed in criteria. If an open 597 * connection with that criteria exists use it, otherwise open a 598 * new connection. 599 * Success: returns the pointer to the Connection structure 600 * Failure: returns NULL, error code and message should be in errorp 601 */ 602 static int 603 makeConnection(Connection **conp, const char *serverAddr, 604 const ns_cred_t *auth, ConnectionID *cID, int timeoutSec, 605 ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd) 606 { 607 Connection *con = NULL; 608 ConnectionID id; 609 char errmsg[MAXERROR]; 610 int rc, exit_rc = NS_LDAP_SUCCESS; 611 ns_server_info_t sinfo; 612 char *hReq, *host = NULL; 613 LDAP *ld = NULL; 614 int passwd_mgmt = 0; 615 616 if (conp == NULL || errorp == NULL || auth == NULL) 617 return (NS_LDAP_INVALID_PARAM); 618 *errorp = NULL; 619 *conp = NULL; 620 sinfo.server = NULL; 621 sinfo.controls = NULL; 622 sinfo.saslMechanisms = NULL; 623 624 if ((id = findConnection(serverAddr, auth, &con)) != -1) { 625 /* connection found in cache */ 626 #ifdef DEBUG 627 (void) fprintf(stderr, "connection found in cache %d\n", id); 628 fflush(stderr); 629 #endif /* DEBUG */ 630 *cID = id; 631 *conp = con; 632 return (NS_LDAP_SUCCESS); 633 } 634 635 if (serverAddr) { 636 rc = openConnection(&ld, serverAddr, auth, timeoutSec, errorp, 637 fail_if_new_pwd_reqd, passwd_mgmt); 638 if (rc == NS_LDAP_SUCCESS || rc == 639 NS_LDAP_SUCCESS_WITH_INFO) { 640 exit_rc = rc; 641 rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr, 642 &sinfo, errorp); 643 if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) { 644 (void) snprintf(errmsg, sizeof (errmsg), 645 gettext("makeConnection: unable to get " 646 "server information for %s"), serverAddr); 647 syslog(LOG_ERR, "libsldap: %s", errmsg); 648 return (NS_LDAP_OP_FAILED); 649 } 650 goto create_con; 651 } else { 652 return (rc); 653 } 654 } 655 656 /* No cached connection, create one */ 657 for (; ; ) { 658 if (host == NULL) 659 hReq = NS_CACHE_NEW; 660 else 661 hReq = NS_CACHE_NEXT; 662 rc = __s_api_requestServer(hReq, host, &sinfo, errorp); 663 if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) || 664 (host && (strcasecmp(host, sinfo.server) == 0))) { 665 /* Log the error */ 666 if (*errorp) { 667 (void) snprintf(errmsg, sizeof (errmsg), 668 "%s: (%s)", gettext("makeConnection: " 669 "unable to make LDAP connection, " 670 "request for a server failed"), 671 (*errorp)->message); 672 syslog(LOG_ERR, "libsldap: %s", errmsg); 673 } 674 675 if (sinfo.server) 676 free(sinfo.server); 677 __s_api_free2dArray(sinfo.saslMechanisms); 678 __s_api_free2dArray(sinfo.controls); 679 if (host) 680 free(host); 681 return (NS_LDAP_OP_FAILED); 682 } 683 if (host) 684 free(host); 685 host = strdup(sinfo.server); 686 if (host == NULL) { 687 free(sinfo.server); 688 __s_api_free2dArray(sinfo.saslMechanisms); 689 __s_api_free2dArray(sinfo.controls); 690 return (NS_LDAP_MEMORY); 691 } 692 693 /* check if server supports password management */ 694 passwd_mgmt = __s_api_contain_passwd_control_oid( 695 sinfo.controls); 696 /* make the connection */ 697 rc = openConnection(&ld, host, auth, timeoutSec, errorp, 698 fail_if_new_pwd_reqd, passwd_mgmt); 699 /* if success, go to create connection structure */ 700 if (rc == NS_LDAP_SUCCESS || 701 rc == NS_LDAP_SUCCESS_WITH_INFO) { 702 exit_rc = rc; 703 break; 704 } 705 706 /* 707 * If not able to reach the server, inform the ldap 708 * cache manager that the server should be removed 709 * from its server list. Thus, the manager will not 710 * return this server on the next get-server request 711 * and will also reduce the server list refresh TTL, 712 * so that it will find out sooner when the server 713 * is up again. 714 */ 715 if (rc == NS_LDAP_INTERNAL && *errorp != NULL) { 716 if ((*errorp)->status == LDAP_CONNECT_ERROR || 717 (*errorp)->status == LDAP_SERVER_DOWN) 718 __s_api_removeServer(host); 719 } 720 721 /* else, cleanup and go for the next server */ 722 if (sinfo.server) { 723 free(sinfo.server); 724 sinfo.server = NULL; 725 } 726 __s_api_free2dArray(sinfo.saslMechanisms); 727 sinfo.saslMechanisms = NULL; 728 __s_api_free2dArray(sinfo.controls); 729 sinfo.controls = NULL; 730 if (*errorp) { 731 /* 732 * If openConnection() failed due to 733 * password policy, or invalid credential, 734 * keep *errorp and exit 735 */ 736 if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD || 737 (*errorp)->status == LDAP_INVALID_CREDENTIALS) { 738 free(host); 739 return (rc); 740 } else { 741 (void) __ns_ldap_freeError(errorp); 742 *errorp = NULL; 743 } 744 } 745 } 746 747 create_con: 748 /* we have created ld, setup con structure */ 749 if (host) 750 free(host); 751 if ((con = calloc(1, sizeof (Connection))) == NULL) { 752 if (sinfo.server) 753 free(sinfo.server); 754 __s_api_free2dArray(sinfo.saslMechanisms); 755 __s_api_free2dArray(sinfo.controls); 756 /* 757 * If password control attached in **errorp, 758 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 759 * free the error structure 760 */ 761 if (*errorp) { 762 (void) __ns_ldap_freeError(errorp); 763 *errorp = NULL; 764 } 765 return (NS_LDAP_MEMORY); 766 } 767 768 con->serverAddr = sinfo.server; 769 con->saslMechanisms = sinfo.saslMechanisms; 770 con->controls = sinfo.controls; 771 772 con->auth = __ns_ldap_dupAuth(auth); 773 if (con->auth == NULL) { 774 free(con); 775 /* 776 * If password control attached in **errorp, 777 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 778 * free the error structure 779 */ 780 if (*errorp) { 781 (void) __ns_ldap_freeError(errorp); 782 *errorp = NULL; 783 } 784 return (NS_LDAP_MEMORY); 785 } 786 787 con->threadID = thr_self(); 788 con->ld = ld; 789 if ((id = addConnection(con)) == -1) { 790 freeConnection(con); 791 /* 792 * If password control attached in **errorp, 793 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 794 * free the error structure 795 */ 796 if (*errorp) { 797 (void) __ns_ldap_freeError(errorp); 798 *errorp = NULL; 799 } 800 return (NS_LDAP_MEMORY); 801 } 802 #ifdef DEBUG 803 (void) fprintf(stderr, "connection added into cache %d\n", id); 804 fflush(stderr); 805 #endif /* DEBUG */ 806 *cID = id; 807 *conp = con; 808 return (exit_rc); 809 } 810 811 /* 812 * Return the specified connection to the pool. If necessary 813 * delete the connection. 814 */ 815 816 static void 817 _DropConnection(ConnectionID cID, int flag, int fini) 818 { 819 Connection *cp; 820 int id; 821 int use_mutex = !fini; 822 823 id = cID - CONID_OFFSET; 824 if (id < 0 || id >= sessionPoolSize) 825 return; 826 #ifdef DEBUG 827 (void) fprintf(stderr, 828 "Dropping connection cID=%d flag=0x%x\n", cID, flag); 829 fflush(stderr); 830 #endif /* DEBUG */ 831 if (use_mutex) 832 (void) mutex_lock(&sessionPoolLock); 833 834 cp = sessionPool[id]; 835 /* sanity check before removing */ 836 if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) { 837 if (use_mutex) 838 (void) mutex_unlock(&sessionPoolLock); 839 return; 840 } 841 842 if (!fini && 843 ((flag & NS_LDAP_NEW_CONN) == 0) && 844 ((flag & NS_LDAP_KEEP_CONN) || nscd_proc())) { 845 /* release Connection (keep alive) */ 846 cp->usedBit = B_FALSE; 847 cp->threadID = 0; /* unmark the threadID */ 848 if (use_mutex) 849 (void) mutex_unlock(&sessionPoolLock); 850 } else { 851 /* delete Connection (disconnect) */ 852 sessionPool[id] = NULL; 853 if (use_mutex) 854 (void) mutex_unlock(&sessionPoolLock); 855 (void) ldap_unbind(cp->ld); 856 freeConnection(cp); 857 } 858 } 859 860 void 861 DropConnection(ConnectionID cID, int flag) 862 { 863 _DropConnection(cID, flag, 0); 864 } 865 866 /* 867 * This routine is called after a bind operation is 868 * done in openConnection() to process the password 869 * management information, if any. 870 * 871 * Input: 872 * bind_type: "simple" or "sasl/DIGEST-MD5" 873 * ldaprc : ldap rc from the ldap bind operation 874 * controls : controls returned by the server 875 * errmsg : error message from the server 876 * fail_if_new_pwd_reqd: 877 * flag indicating if connection should be open 878 * when password needs to change immediately 879 * passwd_mgmt: 880 * flag indicating if server supports password 881 * policy/management 882 * 883 * Output : ns_ldap_error structure, which may contain 884 * password status and number of seconds until 885 * expired 886 * 887 * return rc: 888 * NS_LDAP_EXTERNAL: error, connection should not open 889 * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached 890 * NS_LDAP_SUCCESS: OK to open connection 891 * 892 */ 893 894 static int 895 process_pwd_mgmt(char *bind_type, int ldaprc, 896 LDAPControl **controls, 897 char *errmsg, ns_ldap_error_t **errorp, 898 int fail_if_new_pwd_reqd, 899 int passwd_mgmt) 900 { 901 char errstr[MAXERROR]; 902 LDAPControl **ctrl = NULL; 903 int exit_rc; 904 ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD; 905 int sec_until_exp = 0; 906 907 /* 908 * errmsg may be an empty string, 909 * even if ldaprc is LDAP_SUCCESS, 910 * free the empty string if that's the case 911 */ 912 if (errmsg && 913 (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) { 914 ldap_memfree(errmsg); 915 errmsg = NULL; 916 } 917 918 if (ldaprc != LDAP_SUCCESS) { 919 /* 920 * try to map ldap rc and error message to 921 * a password status 922 */ 923 if (errmsg) { 924 if (passwd_mgmt) 925 pwd_status = 926 __s_api_set_passwd_status( 927 ldaprc, errmsg); 928 ldap_memfree(errmsg); 929 } 930 931 (void) snprintf(errstr, sizeof (errstr), 932 gettext("openConnection: " 933 "%s bind failed " 934 "- %s"), bind_type, ldap_err2string(ldaprc)); 935 936 if (pwd_status != NS_PASSWD_GOOD) { 937 MKERROR_PWD_MGMT(*errorp, 938 ldaprc, strdup(errstr), 939 pwd_status, 0, NULL); 940 } else { 941 MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr), 942 NULL); 943 } 944 if (controls) 945 ldap_controls_free(controls); 946 947 return (NS_LDAP_INTERNAL); 948 } 949 950 /* 951 * ldaprc is LDAP_SUCCESS, 952 * process the password management controls, if any 953 */ 954 exit_rc = NS_LDAP_SUCCESS; 955 if (controls && passwd_mgmt) { 956 /* 957 * The control with the OID 958 * 2.16.840.1.113730.3.4.4 (or 959 * LDAP_CONTROL_PWEXPIRED, as defined 960 * in the ldap.h header file) is the 961 * expired password control. 962 * 963 * This control is used if the server 964 * is configured to require users to 965 * change their passwords when first 966 * logging in and whenever the 967 * passwords are reset. 968 * 969 * If the user is logging in for the 970 * first time or if the user's 971 * password has been reset, the 972 * server sends this control to 973 * indicate that the client needs to 974 * change the password immediately. 975 * 976 * At this point, the only operation 977 * that the client can perform is to 978 * change the user's password. If the 979 * client requests any other LDAP 980 * operation, the server sends back 981 * an LDAP_UNWILLING_TO_PERFORM 982 * result code with an expired 983 * password control. 984 * 985 * The control with the OID 986 * 2.16.840.1.113730.3.4.5 (or 987 * LDAP_CONTROL_PWEXPIRING, as 988 * defined in the ldap.h header file) 989 * is the password expiration warning 990 * control. 991 * 992 * This control is used if the server 993 * is configured to expire user 994 * passwords after a certain amount 995 * of time. 996 * 997 * The server sends this control back 998 * to the client if the client binds 999 * using a password that will soon 1000 * expire. The ldctl_value field of 1001 * the LDAPControl structure 1002 * specifies the number of seconds 1003 * before the password will expire. 1004 */ 1005 for (ctrl = controls; *ctrl; ctrl++) { 1006 1007 if (strcmp((*ctrl)->ldctl_oid, 1008 LDAP_CONTROL_PWEXPIRED) == 0) { 1009 /* 1010 * if the caller wants this bind 1011 * to fail, set up the error info. 1012 * If call to this function is 1013 * for searching the LDAP directory, 1014 * e.g., __ns_ldap_list(), 1015 * there's really no sense to 1016 * let a connection open and 1017 * then fail immediately afterward 1018 * on the LDAP search operation with 1019 * the LDAP_UNWILLING_TO_PERFORM rc 1020 */ 1021 pwd_status = 1022 NS_PASSWD_CHANGE_NEEDED; 1023 if (fail_if_new_pwd_reqd) { 1024 (void) snprintf(errstr, 1025 sizeof (errstr), 1026 gettext( 1027 "openConnection: " 1028 "%s bind " 1029 "failed " 1030 "- password " 1031 "expired. It " 1032 " needs to change " 1033 "immediately!"), 1034 bind_type); 1035 MKERROR_PWD_MGMT(*errorp, 1036 LDAP_SUCCESS, 1037 strdup(errstr), 1038 pwd_status, 1039 0, 1040 NULL); 1041 exit_rc = NS_LDAP_INTERNAL; 1042 } else { 1043 MKERROR_PWD_MGMT(*errorp, 1044 LDAP_SUCCESS, 1045 NULL, 1046 pwd_status, 1047 0, 1048 NULL); 1049 exit_rc = 1050 NS_LDAP_SUCCESS_WITH_INFO; 1051 } 1052 break; 1053 } else if (strcmp((*ctrl)->ldctl_oid, 1054 LDAP_CONTROL_PWEXPIRING) == 0) { 1055 pwd_status = 1056 NS_PASSWD_ABOUT_TO_EXPIRE; 1057 if ((*ctrl)-> 1058 ldctl_value.bv_len > 0 && 1059 (*ctrl)-> 1060 ldctl_value.bv_val) 1061 sec_until_exp = 1062 atoi((*ctrl)-> 1063 ldctl_value.bv_val); 1064 MKERROR_PWD_MGMT(*errorp, 1065 LDAP_SUCCESS, 1066 NULL, 1067 pwd_status, 1068 sec_until_exp, 1069 NULL); 1070 exit_rc = 1071 NS_LDAP_SUCCESS_WITH_INFO; 1072 break; 1073 } 1074 } 1075 } 1076 1077 if (controls) 1078 ldap_controls_free(controls); 1079 1080 return (exit_rc); 1081 } 1082 1083 static int 1084 ldap_in_hosts_switch() 1085 { 1086 enum __nsw_parse_err pserr; 1087 struct __nsw_switchconfig *conf; 1088 struct __nsw_lookup *lkp; 1089 const char *name; 1090 int found = 0; 1091 1092 conf = __nsw_getconfig("hosts", &pserr); 1093 if (conf == NULL) { 1094 return (-1); 1095 } 1096 1097 /* check for skip and count other backends */ 1098 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { 1099 name = lkp->service_name; 1100 if (strcmp(name, "ldap") == 0) { 1101 found = 1; 1102 break; 1103 } 1104 } 1105 __nsw_freeconfig(conf); 1106 return (found); 1107 } 1108 1109 static int 1110 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, 1111 int timeoutSec, ns_ldap_error_t **errorp, 1112 int fail_if_new_pwd_reqd, int passwd_mgmt) 1113 { 1114 LDAP *ld = NULL; 1115 char *binddn, *passwd; 1116 char *digest_md5_name; 1117 const char *s; 1118 int ldapVersion = LDAP_VERSION3; 1119 int derefOption = LDAP_DEREF_ALWAYS; 1120 int zero = 0; 1121 int rc; 1122 char errstr[MAXERROR]; 1123 int errnum = 0; 1124 LDAPMessage *resultMsg; 1125 int msgId; 1126 int useSSL = 0; 1127 struct timeval tv; 1128 AuthType_t bindType; 1129 int timeoutMilliSec = timeoutSec * 1000; 1130 struct berval cred; 1131 char *sslServerAddr; 1132 char *s1; 1133 char *errmsg; 1134 LDAPControl **controls; 1135 int pwd_rc; 1136 1137 *errorp = NULL; 1138 *ldp = NULL; 1139 1140 switch (auth->auth.type) { 1141 case NS_LDAP_AUTH_NONE: 1142 case NS_LDAP_AUTH_SIMPLE: 1143 case NS_LDAP_AUTH_SASL: 1144 bindType = auth->auth.type; 1145 break; 1146 case NS_LDAP_AUTH_TLS: 1147 useSSL = 1; 1148 switch (auth->auth.tlstype) { 1149 case NS_LDAP_TLS_NONE: 1150 bindType = NS_LDAP_AUTH_NONE; 1151 break; 1152 case NS_LDAP_TLS_SIMPLE: 1153 bindType = NS_LDAP_AUTH_SIMPLE; 1154 break; 1155 case NS_LDAP_TLS_SASL: 1156 bindType = NS_LDAP_AUTH_SASL; 1157 break; 1158 default: 1159 (void) sprintf(errstr, 1160 gettext("openConnection: unsupported " 1161 "TLS authentication method " 1162 "(%d)"), auth->auth.tlstype); 1163 MKERROR(LOG_WARNING, *errorp, 1164 LDAP_AUTH_METHOD_NOT_SUPPORTED, 1165 strdup(errstr), NULL); 1166 return (NS_LDAP_INTERNAL); 1167 } 1168 break; 1169 default: 1170 (void) sprintf(errstr, 1171 gettext("openConnection: unsupported " 1172 "authentication method (%d)"), auth->auth.type); 1173 MKERROR(LOG_WARNING, *errorp, 1174 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 1175 NULL); 1176 return (NS_LDAP_INTERNAL); 1177 } 1178 1179 if (useSSL) { 1180 const char *hostcertpath; 1181 char *alloc_hcp = NULL; 1182 #ifdef DEBUG 1183 (void) fprintf(stderr, "+++TLS transport\n"); 1184 #endif /* DEBUG */ 1185 hostcertpath = auth->hostcertpath; 1186 if (hostcertpath == NULL) { 1187 alloc_hcp = __s_get_hostcertpath(); 1188 hostcertpath = alloc_hcp; 1189 } 1190 1191 if (hostcertpath == NULL) 1192 return (NS_LDAP_MEMORY); 1193 1194 if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) { 1195 if (alloc_hcp) 1196 free(alloc_hcp); 1197 (void) snprintf(errstr, sizeof (errstr), 1198 gettext("openConnection: failed to initialize " 1199 "TLS security (%s)"), 1200 ldapssl_err2string(rc)); 1201 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1202 strdup(errstr), NULL); 1203 return (NS_LDAP_INTERNAL); 1204 } 1205 if (alloc_hcp) 1206 free(alloc_hcp); 1207 1208 /* determine if the host name contains a port number */ 1209 s = strchr(serverAddr, ']'); /* skip over ipv6 addr */ 1210 if (s == NULL) 1211 s = serverAddr; 1212 s = strchr(s, ':'); 1213 if (s != NULL) { 1214 /* 1215 * If we do get a port number, we will try stripping 1216 * it. At present, referrals will always have a 1217 * port number. 1218 */ 1219 sslServerAddr = strdup(serverAddr); 1220 if (sslServerAddr == NULL) 1221 return (NS_LDAP_MEMORY); 1222 s1 = strrchr(sslServerAddr, ':'); 1223 if (s1 != NULL) 1224 *s1 = '\0'; 1225 (void) snprintf(errstr, sizeof (errstr), 1226 gettext("openConnection: cannot use tls with %s. " 1227 "Trying %s"), 1228 serverAddr, sslServerAddr); 1229 syslog(LOG_ERR, "libsldap: %s", errstr); 1230 } else 1231 sslServerAddr = (char *)serverAddr; 1232 1233 ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1); 1234 1235 if (sslServerAddr != serverAddr) 1236 free(sslServerAddr); 1237 1238 if (ld == NULL || 1239 ldapssl_install_gethostbyaddr(ld, "ldap") != 0) { 1240 (void) snprintf(errstr, sizeof (errstr), 1241 gettext("openConnection: failed to connect " 1242 "using TLS (%s)"), strerror(errno)); 1243 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1244 strdup(errstr), NULL); 1245 return (NS_LDAP_INTERNAL); 1246 } 1247 } else { 1248 #ifdef DEBUG 1249 (void) fprintf(stderr, "+++Unsecure transport\n"); 1250 #endif /* DEBUG */ 1251 /* Warning message IF cannot connect to host(s) */ 1252 if ((ld = ldap_init((char *)serverAddr, LDAP_PORT)) == NULL) { 1253 char *p = strerror(errno); 1254 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1255 strdup(p), NULL); 1256 return (NS_LDAP_INTERNAL); 1257 } else { 1258 /* check and avoid gethostname recursion */ 1259 if (ldap_in_hosts_switch() > 0 && 1260 ! __s_api_isipv4((char *)serverAddr) && 1261 ! __s_api_isipv6((char *)serverAddr)) { 1262 /* host: ldap - found, attempt to recover */ 1263 if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB, 1264 "ldap") != 0) { 1265 (void) snprintf(errstr, sizeof (errstr), 1266 gettext("openConnection: " 1267 "unrecoverable gethostname " 1268 "recursion detected " 1269 "in /etc/nsswitch.conf")); 1270 MKERROR(LOG_WARNING, *errorp, 1271 LDAP_CONNECT_ERROR, 1272 strdup(errstr), NULL); 1273 (void) ldap_unbind(ld); 1274 return (NS_LDAP_INTERNAL); 1275 } 1276 } 1277 } 1278 } 1279 1280 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); 1281 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 1282 /* 1283 * set LDAP_OPT_REFERRALS to OFF. 1284 * This library will handle the referral itself 1285 * based on API flags or configuration file 1286 * specification. If this option is not set 1287 * to OFF, libldap will never pass the 1288 * referral info up to this library 1289 */ 1290 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1291 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 1292 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 1293 /* setup TCP/IP connect timeout */ 1294 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 1295 &timeoutMilliSec); 1296 /* retry if LDAP I/O was interrupted */ 1297 (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1298 1299 switch (bindType) { 1300 case NS_LDAP_AUTH_NONE: 1301 #ifdef DEBUG 1302 (void) fprintf(stderr, "+++Anonymous bind\n"); 1303 #endif /* DEBUG */ 1304 break; 1305 case NS_LDAP_AUTH_SIMPLE: 1306 binddn = auth->cred.unix_cred.userID; 1307 passwd = auth->cred.unix_cred.passwd; 1308 if (passwd == NULL || *passwd == '\0' || 1309 binddn == NULL || *binddn == '\0') { 1310 (void) sprintf(errstr, gettext("openConnection: " 1311 "missing credentials for Simple bind")); 1312 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 1313 strdup(errstr), NULL); 1314 (void) ldap_unbind(ld); 1315 return (NS_LDAP_INTERNAL); 1316 } 1317 1318 #ifdef DEBUG 1319 (void) fprintf(stderr, "+++Simple bind\n"); 1320 #endif /* DEBUG */ 1321 msgId = ldap_simple_bind(ld, binddn, passwd); 1322 1323 if (msgId == -1) { 1324 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 1325 (void *)&errnum); 1326 (void) snprintf(errstr, sizeof (errstr), 1327 gettext("openConnection: simple bind failed " 1328 "- %s"), ldap_err2string(errnum)); 1329 (void) ldap_unbind(ld); 1330 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 1331 NULL); 1332 return (NS_LDAP_INTERNAL); 1333 } 1334 1335 tv.tv_sec = timeoutSec; 1336 tv.tv_usec = 0; 1337 rc = ldap_result(ld, msgId, 0, &tv, &resultMsg); 1338 1339 if ((rc == -1) || (rc == 0)) { 1340 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 1341 (void *)&errnum); 1342 (void) snprintf(errstr, sizeof (errstr), 1343 gettext("openConnection: simple bind failed " 1344 "- %s"), ldap_err2string(errnum)); 1345 (void) ldap_msgfree(resultMsg); 1346 (void) ldap_unbind(ld); 1347 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 1348 NULL); 1349 return (NS_LDAP_INTERNAL); 1350 } 1351 1352 /* 1353 * get ldaprc, controls, and error msg 1354 */ 1355 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 1356 &errmsg, NULL, &controls, 1); 1357 1358 if (rc != LDAP_SUCCESS) { 1359 (void) snprintf(errstr, sizeof (errstr), 1360 gettext("openConnection: simple bind failed " 1361 "- unable to parse result")); 1362 (void) ldap_unbind(ld); 1363 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 1364 strdup(errstr), NULL); 1365 return (NS_LDAP_INTERNAL); 1366 } 1367 1368 /* process the password management info, if any */ 1369 pwd_rc = process_pwd_mgmt("simple", 1370 errnum, controls, errmsg, 1371 errorp, 1372 fail_if_new_pwd_reqd, 1373 passwd_mgmt); 1374 1375 if (pwd_rc == NS_LDAP_INTERNAL) { 1376 (void) ldap_unbind(ld); 1377 return (pwd_rc); 1378 } 1379 1380 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 1381 *ldp = ld; 1382 return (pwd_rc); 1383 } 1384 1385 break; 1386 case NS_LDAP_AUTH_SASL: 1387 /* We don't support any sasl options yet */ 1388 if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE) { 1389 (void) sprintf(errstr, 1390 gettext("openConnection: SASL options are " 1391 "not supported (%d)"), auth->auth.saslopt); 1392 MKERROR(LOG_WARNING, *errorp, 1393 LDAP_AUTH_METHOD_NOT_SUPPORTED, 1394 strdup(errstr), NULL); 1395 (void) ldap_unbind(ld); 1396 return (NS_LDAP_INTERNAL); 1397 } 1398 binddn = auth->cred.unix_cred.userID; 1399 passwd = auth->cred.unix_cred.passwd; 1400 if (passwd == NULL || *passwd == '\0' || 1401 binddn == NULL || *binddn == '\0') { 1402 (void) sprintf(errstr, 1403 gettext("openConnection: missing credentials " 1404 "for SASL bind")); 1405 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 1406 strdup(errstr), NULL); 1407 (void) ldap_unbind(ld); 1408 return (NS_LDAP_INTERNAL); 1409 } 1410 cred.bv_val = passwd; 1411 cred.bv_len = strlen(passwd); 1412 1413 switch (auth->auth.saslmech) { 1414 case NS_LDAP_SASL_CRAM_MD5: 1415 /* 1416 * NOTE: if iDS changes to support cram_md5, 1417 * please add password management code here. 1418 * Since ldap_sasl_cram_md5_bind_s does not 1419 * return anything that could be used to 1420 * extract the ldap rc/errmsg/control to 1421 * determine if bind failed due to password 1422 * policy, a new cram_md5_bind API will need 1423 * to be introduced. See 1424 * ldap_x_sasl_digest_md5_bind() and case 1425 * NS_LDAP_SASL_DIGEST_MD5 below for details. 1426 */ 1427 if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn, 1428 &cred, NULL, NULL)) != LDAP_SUCCESS) { 1429 (void) ldap_get_option(ld, 1430 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 1431 (void) snprintf(errstr, sizeof (errstr), 1432 gettext("openConnection: " 1433 "sasl/CRAM-MD5 bind failed - %s"), 1434 ldap_err2string(errnum)); 1435 MKERROR(LOG_WARNING, *errorp, errnum, 1436 strdup(errstr), NULL); 1437 (void) ldap_unbind(ld); 1438 return (NS_LDAP_INTERNAL); 1439 } 1440 break; 1441 case NS_LDAP_SASL_DIGEST_MD5: 1442 digest_md5_name = malloc(strlen(binddn) + 5); 1443 /* 5 = strlen("dn: ") + 1 */ 1444 if (digest_md5_name == NULL) { 1445 (void) ldap_unbind(ld); 1446 return (NS_LDAP_MEMORY); 1447 } 1448 (void) strcpy(digest_md5_name, "dn: "); 1449 (void) strcat(digest_md5_name, binddn); 1450 1451 tv.tv_sec = timeoutSec; 1452 tv.tv_usec = 0; 1453 rc = ldap_x_sasl_digest_md5_bind(ld, 1454 digest_md5_name, &cred, NULL, NULL, 1455 &tv, &resultMsg); 1456 1457 if (resultMsg == NULL) { 1458 free(digest_md5_name); 1459 (void) ldap_get_option(ld, 1460 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 1461 (void) snprintf(errstr, sizeof (errstr), 1462 gettext("openConnection: " 1463 "DIGEST-MD5 bind failed - %s"), 1464 ldap_err2string(errnum)); 1465 (void) ldap_unbind(ld); 1466 MKERROR(LOG_WARNING, *errorp, errnum, 1467 strdup(errstr), NULL); 1468 return (NS_LDAP_INTERNAL); 1469 } 1470 1471 /* 1472 * get ldaprc, controls, and error msg 1473 */ 1474 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 1475 &errmsg, NULL, &controls, 1); 1476 1477 if (rc != LDAP_SUCCESS) { 1478 free(digest_md5_name); 1479 (void) snprintf(errstr, sizeof (errstr), 1480 gettext("openConnection: " 1481 "DIGEST-MD5 bind failed " 1482 "- unable to parse result")); 1483 (void) ldap_unbind(ld); 1484 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 1485 strdup(errstr), NULL); 1486 return (NS_LDAP_INTERNAL); 1487 } 1488 1489 /* process the password management info, if any */ 1490 pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5", 1491 errnum, controls, errmsg, 1492 errorp, 1493 fail_if_new_pwd_reqd, 1494 passwd_mgmt); 1495 1496 if (pwd_rc == NS_LDAP_INTERNAL) { 1497 free(digest_md5_name); 1498 (void) ldap_unbind(ld); 1499 return (pwd_rc); 1500 } 1501 1502 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 1503 *ldp = ld; 1504 return (pwd_rc); 1505 } 1506 1507 free(digest_md5_name); 1508 break; 1509 default: 1510 (void) ldap_unbind(ld); 1511 (void) sprintf(errstr, 1512 gettext("openConnection: unsupported SASL " 1513 "mechanism (%d)"), auth->auth.saslmech); 1514 MKERROR(LOG_WARNING, *errorp, 1515 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 1516 NULL); 1517 return (NS_LDAP_INTERNAL); 1518 } 1519 } 1520 1521 *ldp = ld; 1522 return (NS_LDAP_SUCCESS); 1523 } 1524 1525 /* 1526 * FUNCTION: __s_api_getDefaultAuth 1527 * 1528 * Constructs a credential for authentication using the config module. 1529 * 1530 * RETURN VALUES: 1531 * 1532 * NS_LDAP_SUCCESS If successful 1533 * NS_LDAP_CONFIG If there are any config errors. 1534 * NS_LDAP_MEMORY Memory errors. 1535 * NS_LDAP_OP_FAILED If there are no more authentication methods so can 1536 * not build a new authp. 1537 * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the 1538 * necessary fields of a cred for a given auth method 1539 * are not provided. 1540 * INPUT: 1541 * 1542 * cLevel Currently requested credential level to be tried 1543 * 1544 * aMethod Currently requested authentication method to be tried 1545 * 1546 * OUTPUT: 1547 * 1548 * authp authentication method to use. 1549 */ 1550 static int 1551 __s_api_getDefaultAuth( 1552 int *cLevel, 1553 ns_auth_t *aMethod, 1554 ns_cred_t **authp) 1555 { 1556 void **paramVal = NULL; 1557 char *modparamVal = NULL; 1558 int getUid = 0; 1559 int getPasswd = 0; 1560 int getCertpath = 0; 1561 int rc = 0; 1562 ns_ldap_error_t *errorp = NULL; 1563 1564 #ifdef DEBUG 1565 (void) fprintf(stderr, "__s_api_getDefaultAuth START\n"); 1566 #endif 1567 1568 if (aMethod == NULL) { 1569 /* Require an Auth */ 1570 return (NS_LDAP_INVALID_PARAM); 1571 1572 } 1573 1574 /* 1575 * Do not differentiate between (proxy/self) at this time, but 1576 * reject self credential levels at this time 1577 */ 1578 if (cLevel && *cLevel == NS_LDAP_CRED_SELF) 1579 return (NS_LDAP_INVALID_PARAM); 1580 1581 *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); 1582 if ((*authp) == NULL) 1583 return (NS_LDAP_MEMORY); 1584 1585 (*authp)->auth = *aMethod; 1586 1587 switch (aMethod->type) { 1588 case NS_LDAP_AUTH_NONE: 1589 return (NS_LDAP_SUCCESS); 1590 case NS_LDAP_AUTH_SIMPLE: 1591 getUid++; 1592 getPasswd++; 1593 break; 1594 case NS_LDAP_AUTH_SASL: 1595 if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 1596 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) { 1597 getUid++; 1598 getPasswd++; 1599 } else { 1600 (void) __ns_ldap_freeCred(authp); 1601 *authp = NULL; 1602 return (NS_LDAP_INVALID_PARAM); 1603 } 1604 break; 1605 case NS_LDAP_AUTH_TLS: 1606 if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) || 1607 ((aMethod->tlstype == NS_LDAP_TLS_SASL) && 1608 ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 1609 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) { 1610 getUid++; 1611 getPasswd++; 1612 getCertpath++; 1613 } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) { 1614 getCertpath++; 1615 } else { 1616 (void) __ns_ldap_freeCred(authp); 1617 *authp = NULL; 1618 return (NS_LDAP_INVALID_PARAM); 1619 } 1620 break; 1621 } 1622 1623 if (getUid) { 1624 paramVal = NULL; 1625 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P, 1626 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1627 (void) __ns_ldap_freeCred(authp); 1628 (void) __ns_ldap_freeError(&errorp); 1629 *authp = NULL; 1630 return (rc); 1631 } 1632 1633 if (paramVal == NULL || *paramVal == NULL) { 1634 (void) __ns_ldap_freeCred(authp); 1635 *authp = NULL; 1636 return (NS_LDAP_INVALID_PARAM); 1637 } 1638 1639 (*authp)->cred.unix_cred.userID = strdup((char *)*paramVal); 1640 (void) __ns_ldap_freeParam(¶mVal); 1641 if ((*authp)->cred.unix_cred.userID == NULL) { 1642 (void) __ns_ldap_freeCred(authp); 1643 *authp = NULL; 1644 return (NS_LDAP_MEMORY); 1645 } 1646 } 1647 if (getPasswd) { 1648 paramVal = NULL; 1649 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P, 1650 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1651 (void) __ns_ldap_freeCred(authp); 1652 (void) __ns_ldap_freeError(&errorp); 1653 *authp = NULL; 1654 return (rc); 1655 } 1656 1657 if (paramVal == NULL || *paramVal == NULL) { 1658 (void) __ns_ldap_freeCred(authp); 1659 *authp = NULL; 1660 return (NS_LDAP_INVALID_PARAM); 1661 } 1662 1663 modparamVal = dvalue((char *)*paramVal); 1664 (void) __ns_ldap_freeParam(¶mVal); 1665 if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) { 1666 (void) __ns_ldap_freeCred(authp); 1667 if (modparamVal != NULL) 1668 free(modparamVal); 1669 *authp = NULL; 1670 return (NS_LDAP_INVALID_PARAM); 1671 } 1672 1673 (*authp)->cred.unix_cred.passwd = modparamVal; 1674 } 1675 if (getCertpath) { 1676 paramVal = NULL; 1677 if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P, 1678 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1679 (void) __ns_ldap_freeCred(authp); 1680 (void) __ns_ldap_freeError(&errorp); 1681 *authp = NULL; 1682 return (rc); 1683 } 1684 1685 if (paramVal == NULL || *paramVal == NULL) { 1686 (void) __ns_ldap_freeCred(authp); 1687 *authp = NULL; 1688 return (NS_LDAP_INVALID_PARAM); 1689 } 1690 1691 (*authp)->hostcertpath = strdup((char *)*paramVal); 1692 (void) __ns_ldap_freeParam(¶mVal); 1693 if ((*authp)->hostcertpath == NULL) { 1694 (void) __ns_ldap_freeCred(authp); 1695 *authp = NULL; 1696 return (NS_LDAP_MEMORY); 1697 } 1698 } 1699 return (NS_LDAP_SUCCESS); 1700 } 1701 1702 /* 1703 * FUNCTION: __s_api_getConnection 1704 * 1705 * Bind to the specified server or one from the server 1706 * list and return the pointer. 1707 * 1708 * This function can rebind or not (NS_LDAP_HARD), it can require a 1709 * credential or bind anonymously 1710 * 1711 * This function follows the DUA configuration schema algorithm 1712 * 1713 * RETURN VALUES: 1714 * 1715 * NS_LDAP_SUCCESS A connection was made successfully. 1716 * NS_LDAP_SUCCESS_WITH_INFO 1717 * A connection was made successfully, but with 1718 * password management info in *errorp 1719 * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function. 1720 * NS_LDAP_CONFIG If there are any config errors. 1721 * NS_LDAP_MEMORY Memory errors. 1722 * NS_LDAP_INTERNAL If there was a ldap error. 1723 * 1724 * INPUT: 1725 * 1726 * server Bind to this LDAP server only 1727 * flags If NS_LDAP_HARD is set function will not return until it has 1728 * a connection unless there is a authentication problem. 1729 * If NS_LDAP_NEW_CONN is set the function must force a new 1730 * connection to be created 1731 * If NS_LDAP_KEEP_CONN is set the connection is to be kept open 1732 * auth Credentials for bind. This could be NULL in which case 1733 * a default cred built from the config module is used. 1734 * sessionId cookie that points to a previous session 1735 * fail_if_new_pwd_reqd 1736 * a flag indicating this function should fail if the passwd 1737 * in auth needs to change immediately 1738 * 1739 * OUTPUT: 1740 * 1741 * session pointer to a session with connection information 1742 * errorp Set if there are any INTERNAL, or CONFIG error. 1743 */ 1744 int 1745 __s_api_getConnection( 1746 const char *server, 1747 const int flags, 1748 const ns_cred_t *cred, /* credentials for bind */ 1749 ConnectionID *sessionId, 1750 Connection **session, 1751 ns_ldap_error_t **errorp, 1752 int fail_if_new_pwd_reqd) 1753 { 1754 char errmsg[MAXERROR]; 1755 ns_auth_t **aMethod = NULL; 1756 ns_auth_t **aNext = NULL; 1757 int **cLevel = NULL; 1758 int **cNext = NULL; 1759 int timeoutSec = NS_DEFAULT_BIND_TIMEOUT; 1760 int rc; 1761 Connection *con = NULL; 1762 int sec = 1; 1763 ns_cred_t *authp = NULL; 1764 ns_cred_t anon; 1765 int version = NS_LDAP_V2; 1766 void **paramVal = NULL; 1767 1768 if ((session == NULL) || (sessionId == NULL)) { 1769 return (NS_LDAP_INVALID_PARAM); 1770 } 1771 *session = NULL; 1772 1773 /* if we already have a session id try to reuse connection */ 1774 if (*sessionId > 0) { 1775 rc = findConnectionById(flags, cred, *sessionId, &con); 1776 if (rc == *sessionId && con) { 1777 *session = con; 1778 return (NS_LDAP_SUCCESS); 1779 } 1780 *sessionId = 0; 1781 } 1782 1783 /* get profile version number */ 1784 if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, 1785 ¶mVal, errorp)) != NS_LDAP_SUCCESS) 1786 return (rc); 1787 if (paramVal == NULL) { 1788 (void) sprintf(errmsg, gettext("getConnection: no file " 1789 "version")); 1790 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg), 1791 NS_LDAP_CONFIG); 1792 return (NS_LDAP_CONFIG); 1793 } 1794 if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0) 1795 version = NS_LDAP_V1; 1796 (void) __ns_ldap_freeParam((void ***)¶mVal); 1797 1798 /* Get the bind timeout value */ 1799 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, ¶mVal, errorp); 1800 if (paramVal != NULL && *paramVal != NULL) { 1801 timeoutSec = **((int **)paramVal); 1802 (void) __ns_ldap_freeParam(¶mVal); 1803 } 1804 if (*errorp) 1805 (void) __ns_ldap_freeError(errorp); 1806 1807 if (cred == NULL) { 1808 /* Get the authentication method list */ 1809 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, 1810 (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS) 1811 return (rc); 1812 if (aMethod == NULL) { 1813 aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *)); 1814 if (aMethod == NULL) 1815 return (NS_LDAP_MEMORY); 1816 aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t)); 1817 if (aMethod[0] == NULL) { 1818 free(aMethod); 1819 return (NS_LDAP_MEMORY); 1820 } 1821 if (version == NS_LDAP_V1) 1822 (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE; 1823 else { 1824 (aMethod[0])->type = NS_LDAP_AUTH_SASL; 1825 (aMethod[0])->saslmech = 1826 NS_LDAP_SASL_DIGEST_MD5; 1827 (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE; 1828 } 1829 } 1830 1831 /* Get the credential level list */ 1832 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P, 1833 (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) { 1834 (void) __ns_ldap_freeParam((void ***)&aMethod); 1835 return (rc); 1836 } 1837 if (cLevel == NULL) { 1838 cLevel = (int **)calloc(2, sizeof (int *)); 1839 if (cLevel == NULL) 1840 return (NS_LDAP_MEMORY); 1841 cLevel[0] = (int *)calloc(1, sizeof (int)); 1842 if (cLevel[0] == NULL) 1843 return (NS_LDAP_MEMORY); 1844 if (version == NS_LDAP_V1) 1845 *(cLevel[0]) = NS_LDAP_CRED_PROXY; 1846 else 1847 *(cLevel[0]) = NS_LDAP_CRED_ANON; 1848 } 1849 } 1850 1851 /* setup the anon credential for anonymous connection */ 1852 (void) memset(&anon, 0, sizeof (ns_cred_t)); 1853 anon.auth.type = NS_LDAP_AUTH_NONE; 1854 1855 for (; ; ) { 1856 if (cred != NULL) { 1857 /* using specified auth method */ 1858 rc = makeConnection(&con, server, cred, 1859 sessionId, timeoutSec, errorp, 1860 fail_if_new_pwd_reqd); 1861 if (rc == NS_LDAP_SUCCESS || 1862 rc == NS_LDAP_SUCCESS_WITH_INFO) { 1863 *session = con; 1864 break; 1865 } 1866 } else { 1867 /* for every cred level */ 1868 for (cNext = cLevel; *cNext != NULL; cNext++) { 1869 if (**cNext == NS_LDAP_CRED_ANON) { 1870 /* make connection anonymously */ 1871 rc = makeConnection(&con, server, &anon, 1872 sessionId, timeoutSec, errorp, 1873 fail_if_new_pwd_reqd); 1874 if (rc == NS_LDAP_SUCCESS || 1875 rc == 1876 NS_LDAP_SUCCESS_WITH_INFO) { 1877 *session = con; 1878 goto done; 1879 } 1880 continue; 1881 } 1882 /* for each cred level */ 1883 for (aNext = aMethod; *aNext != NULL; aNext++) { 1884 /* make connection and authenticate */ 1885 /* with default credentials */ 1886 authp = NULL; 1887 rc = __s_api_getDefaultAuth(*cNext, 1888 *aNext, &authp); 1889 if (rc != NS_LDAP_SUCCESS) { 1890 continue; 1891 } 1892 rc = makeConnection(&con, server, authp, 1893 sessionId, timeoutSec, errorp, 1894 fail_if_new_pwd_reqd); 1895 (void) __ns_ldap_freeCred(&authp); 1896 if (rc == NS_LDAP_SUCCESS || 1897 rc == 1898 NS_LDAP_SUCCESS_WITH_INFO) { 1899 *session = con; 1900 goto done; 1901 } 1902 } 1903 } 1904 } 1905 if (flags & NS_LDAP_HARD) { 1906 if (sec < LDAPMAXHARDLOOKUPTIME) 1907 sec *= 2; 1908 _sleep(sec); 1909 } else { 1910 break; 1911 } 1912 } 1913 1914 done: 1915 (void) __ns_ldap_freeParam((void ***)&aMethod); 1916 (void) __ns_ldap_freeParam((void ***)&cLevel); 1917 return (rc); 1918 } 1919 1920 #pragma fini(_free_sessionPool) 1921 static void 1922 _free_sessionPool() 1923 { 1924 int id; 1925 1926 (void) mutex_lock(&sessionPoolLock); 1927 if (sessionPool != NULL) { 1928 for (id = 0; id < sessionPoolSize; id++) 1929 _DropConnection(id + CONID_OFFSET, 0, 1); 1930 free(sessionPool); 1931 sessionPool = NULL; 1932 sessionPoolSize = 0; 1933 } 1934 (void) mutex_unlock(&sessionPoolLock); 1935 } 1936