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 if (__s_api_removeServer(host) < 0) { 719 /* 720 * Couldn't remove server from 721 * server list. Log a warning. 722 */ 723 syslog(LOG_WARNING, "libsldap: could " 724 "not remove %s from servers list", 725 host); 726 } 727 } 728 } 729 730 /* else, cleanup and go for the next server */ 731 if (sinfo.server) { 732 free(sinfo.server); 733 sinfo.server = NULL; 734 } 735 __s_api_free2dArray(sinfo.saslMechanisms); 736 sinfo.saslMechanisms = NULL; 737 __s_api_free2dArray(sinfo.controls); 738 sinfo.controls = NULL; 739 if (*errorp) { 740 /* 741 * If openConnection() failed due to 742 * password policy, or invalid credential, 743 * keep *errorp and exit 744 */ 745 if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD || 746 (*errorp)->status == LDAP_INVALID_CREDENTIALS) { 747 free(host); 748 return (rc); 749 } else { 750 (void) __ns_ldap_freeError(errorp); 751 *errorp = NULL; 752 } 753 } 754 } 755 756 create_con: 757 /* we have created ld, setup con structure */ 758 if (host) 759 free(host); 760 if ((con = calloc(1, sizeof (Connection))) == NULL) { 761 if (sinfo.server) 762 free(sinfo.server); 763 __s_api_free2dArray(sinfo.saslMechanisms); 764 __s_api_free2dArray(sinfo.controls); 765 /* 766 * If password control attached in **errorp, 767 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 768 * free the error structure 769 */ 770 if (*errorp) { 771 (void) __ns_ldap_freeError(errorp); 772 *errorp = NULL; 773 } 774 return (NS_LDAP_MEMORY); 775 } 776 777 con->serverAddr = sinfo.server; 778 con->saslMechanisms = sinfo.saslMechanisms; 779 con->controls = sinfo.controls; 780 781 con->auth = __ns_ldap_dupAuth(auth); 782 if (con->auth == NULL) { 783 free(con); 784 /* 785 * If password control attached in **errorp, 786 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 787 * free the error structure 788 */ 789 if (*errorp) { 790 (void) __ns_ldap_freeError(errorp); 791 *errorp = NULL; 792 } 793 return (NS_LDAP_MEMORY); 794 } 795 796 con->threadID = thr_self(); 797 con->ld = ld; 798 if ((id = addConnection(con)) == -1) { 799 freeConnection(con); 800 /* 801 * If password control attached in **errorp, 802 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 803 * free the error structure 804 */ 805 if (*errorp) { 806 (void) __ns_ldap_freeError(errorp); 807 *errorp = NULL; 808 } 809 return (NS_LDAP_MEMORY); 810 } 811 #ifdef DEBUG 812 (void) fprintf(stderr, "connection added into cache %d\n", id); 813 fflush(stderr); 814 #endif /* DEBUG */ 815 *cID = id; 816 *conp = con; 817 return (exit_rc); 818 } 819 820 /* 821 * Return the specified connection to the pool. If necessary 822 * delete the connection. 823 */ 824 825 static void 826 _DropConnection(ConnectionID cID, int flag, int fini) 827 { 828 Connection *cp; 829 int id; 830 int use_mutex = !fini; 831 832 id = cID - CONID_OFFSET; 833 if (id < 0 || id >= sessionPoolSize) 834 return; 835 #ifdef DEBUG 836 (void) fprintf(stderr, 837 "Dropping connection cID=%d flag=0x%x\n", cID, flag); 838 fflush(stderr); 839 #endif /* DEBUG */ 840 if (use_mutex) 841 (void) mutex_lock(&sessionPoolLock); 842 843 cp = sessionPool[id]; 844 /* sanity check before removing */ 845 if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) { 846 if (use_mutex) 847 (void) mutex_unlock(&sessionPoolLock); 848 return; 849 } 850 851 if (!fini && 852 ((flag & NS_LDAP_NEW_CONN) == 0) && 853 ((flag & NS_LDAP_KEEP_CONN) || nscd_proc())) { 854 /* release Connection (keep alive) */ 855 cp->usedBit = B_FALSE; 856 cp->threadID = 0; /* unmark the threadID */ 857 if (use_mutex) 858 (void) mutex_unlock(&sessionPoolLock); 859 } else { 860 /* delete Connection (disconnect) */ 861 sessionPool[id] = NULL; 862 if (use_mutex) 863 (void) mutex_unlock(&sessionPoolLock); 864 (void) ldap_unbind(cp->ld); 865 freeConnection(cp); 866 } 867 } 868 869 void 870 DropConnection(ConnectionID cID, int flag) 871 { 872 _DropConnection(cID, flag, 0); 873 } 874 875 /* 876 * This routine is called after a bind operation is 877 * done in openConnection() to process the password 878 * management information, if any. 879 * 880 * Input: 881 * bind_type: "simple" or "sasl/DIGEST-MD5" 882 * ldaprc : ldap rc from the ldap bind operation 883 * controls : controls returned by the server 884 * errmsg : error message from the server 885 * fail_if_new_pwd_reqd: 886 * flag indicating if connection should be open 887 * when password needs to change immediately 888 * passwd_mgmt: 889 * flag indicating if server supports password 890 * policy/management 891 * 892 * Output : ns_ldap_error structure, which may contain 893 * password status and number of seconds until 894 * expired 895 * 896 * return rc: 897 * NS_LDAP_EXTERNAL: error, connection should not open 898 * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached 899 * NS_LDAP_SUCCESS: OK to open connection 900 * 901 */ 902 903 static int 904 process_pwd_mgmt(char *bind_type, int ldaprc, 905 LDAPControl **controls, 906 char *errmsg, ns_ldap_error_t **errorp, 907 int fail_if_new_pwd_reqd, 908 int passwd_mgmt) 909 { 910 char errstr[MAXERROR]; 911 LDAPControl **ctrl = NULL; 912 int exit_rc; 913 ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD; 914 int sec_until_exp = 0; 915 916 /* 917 * errmsg may be an empty string, 918 * even if ldaprc is LDAP_SUCCESS, 919 * free the empty string if that's the case 920 */ 921 if (errmsg && 922 (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) { 923 ldap_memfree(errmsg); 924 errmsg = NULL; 925 } 926 927 if (ldaprc != LDAP_SUCCESS) { 928 /* 929 * try to map ldap rc and error message to 930 * a password status 931 */ 932 if (errmsg) { 933 if (passwd_mgmt) 934 pwd_status = 935 __s_api_set_passwd_status( 936 ldaprc, errmsg); 937 ldap_memfree(errmsg); 938 } 939 940 (void) snprintf(errstr, sizeof (errstr), 941 gettext("openConnection: " 942 "%s bind failed " 943 "- %s"), bind_type, ldap_err2string(ldaprc)); 944 945 if (pwd_status != NS_PASSWD_GOOD) { 946 MKERROR_PWD_MGMT(*errorp, 947 ldaprc, strdup(errstr), 948 pwd_status, 0, NULL); 949 } else { 950 MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr), 951 NULL); 952 } 953 if (controls) 954 ldap_controls_free(controls); 955 956 return (NS_LDAP_INTERNAL); 957 } 958 959 /* 960 * ldaprc is LDAP_SUCCESS, 961 * process the password management controls, if any 962 */ 963 exit_rc = NS_LDAP_SUCCESS; 964 if (controls && passwd_mgmt) { 965 /* 966 * The control with the OID 967 * 2.16.840.1.113730.3.4.4 (or 968 * LDAP_CONTROL_PWEXPIRED, as defined 969 * in the ldap.h header file) is the 970 * expired password control. 971 * 972 * This control is used if the server 973 * is configured to require users to 974 * change their passwords when first 975 * logging in and whenever the 976 * passwords are reset. 977 * 978 * If the user is logging in for the 979 * first time or if the user's 980 * password has been reset, the 981 * server sends this control to 982 * indicate that the client needs to 983 * change the password immediately. 984 * 985 * At this point, the only operation 986 * that the client can perform is to 987 * change the user's password. If the 988 * client requests any other LDAP 989 * operation, the server sends back 990 * an LDAP_UNWILLING_TO_PERFORM 991 * result code with an expired 992 * password control. 993 * 994 * The control with the OID 995 * 2.16.840.1.113730.3.4.5 (or 996 * LDAP_CONTROL_PWEXPIRING, as 997 * defined in the ldap.h header file) 998 * is the password expiration warning 999 * control. 1000 * 1001 * This control is used if the server 1002 * is configured to expire user 1003 * passwords after a certain amount 1004 * of time. 1005 * 1006 * The server sends this control back 1007 * to the client if the client binds 1008 * using a password that will soon 1009 * expire. The ldctl_value field of 1010 * the LDAPControl structure 1011 * specifies the number of seconds 1012 * before the password will expire. 1013 */ 1014 for (ctrl = controls; *ctrl; ctrl++) { 1015 1016 if (strcmp((*ctrl)->ldctl_oid, 1017 LDAP_CONTROL_PWEXPIRED) == 0) { 1018 /* 1019 * if the caller wants this bind 1020 * to fail, set up the error info. 1021 * If call to this function is 1022 * for searching the LDAP directory, 1023 * e.g., __ns_ldap_list(), 1024 * there's really no sense to 1025 * let a connection open and 1026 * then fail immediately afterward 1027 * on the LDAP search operation with 1028 * the LDAP_UNWILLING_TO_PERFORM rc 1029 */ 1030 pwd_status = 1031 NS_PASSWD_CHANGE_NEEDED; 1032 if (fail_if_new_pwd_reqd) { 1033 (void) snprintf(errstr, 1034 sizeof (errstr), 1035 gettext( 1036 "openConnection: " 1037 "%s bind " 1038 "failed " 1039 "- password " 1040 "expired. It " 1041 " needs to change " 1042 "immediately!"), 1043 bind_type); 1044 MKERROR_PWD_MGMT(*errorp, 1045 LDAP_SUCCESS, 1046 strdup(errstr), 1047 pwd_status, 1048 0, 1049 NULL); 1050 exit_rc = NS_LDAP_INTERNAL; 1051 } else { 1052 MKERROR_PWD_MGMT(*errorp, 1053 LDAP_SUCCESS, 1054 NULL, 1055 pwd_status, 1056 0, 1057 NULL); 1058 exit_rc = 1059 NS_LDAP_SUCCESS_WITH_INFO; 1060 } 1061 break; 1062 } else if (strcmp((*ctrl)->ldctl_oid, 1063 LDAP_CONTROL_PWEXPIRING) == 0) { 1064 pwd_status = 1065 NS_PASSWD_ABOUT_TO_EXPIRE; 1066 if ((*ctrl)-> 1067 ldctl_value.bv_len > 0 && 1068 (*ctrl)-> 1069 ldctl_value.bv_val) 1070 sec_until_exp = 1071 atoi((*ctrl)-> 1072 ldctl_value.bv_val); 1073 MKERROR_PWD_MGMT(*errorp, 1074 LDAP_SUCCESS, 1075 NULL, 1076 pwd_status, 1077 sec_until_exp, 1078 NULL); 1079 exit_rc = 1080 NS_LDAP_SUCCESS_WITH_INFO; 1081 break; 1082 } 1083 } 1084 } 1085 1086 if (controls) 1087 ldap_controls_free(controls); 1088 1089 return (exit_rc); 1090 } 1091 1092 static int 1093 ldap_in_hosts_switch() 1094 { 1095 enum __nsw_parse_err pserr; 1096 struct __nsw_switchconfig *conf; 1097 struct __nsw_lookup *lkp; 1098 const char *name; 1099 int found = 0; 1100 1101 conf = __nsw_getconfig("hosts", &pserr); 1102 if (conf == NULL) { 1103 return (-1); 1104 } 1105 1106 /* check for skip and count other backends */ 1107 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { 1108 name = lkp->service_name; 1109 if (strcmp(name, "ldap") == 0) { 1110 found = 1; 1111 break; 1112 } 1113 } 1114 __nsw_freeconfig(conf); 1115 return (found); 1116 } 1117 1118 static int 1119 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, 1120 int timeoutSec, ns_ldap_error_t **errorp, 1121 int fail_if_new_pwd_reqd, int passwd_mgmt) 1122 { 1123 LDAP *ld = NULL; 1124 char *binddn, *passwd; 1125 char *digest_md5_name; 1126 const char *s; 1127 int ldapVersion = LDAP_VERSION3; 1128 int derefOption = LDAP_DEREF_ALWAYS; 1129 int zero = 0; 1130 int rc; 1131 char errstr[MAXERROR]; 1132 int errnum = 0; 1133 LDAPMessage *resultMsg; 1134 int msgId; 1135 int useSSL = 0; 1136 struct timeval tv; 1137 AuthType_t bindType; 1138 int timeoutMilliSec = timeoutSec * 1000; 1139 struct berval cred; 1140 char *sslServerAddr; 1141 char *s1; 1142 char *errmsg; 1143 LDAPControl **controls; 1144 int pwd_rc; 1145 1146 *errorp = NULL; 1147 *ldp = NULL; 1148 1149 switch (auth->auth.type) { 1150 case NS_LDAP_AUTH_NONE: 1151 case NS_LDAP_AUTH_SIMPLE: 1152 case NS_LDAP_AUTH_SASL: 1153 bindType = auth->auth.type; 1154 break; 1155 case NS_LDAP_AUTH_TLS: 1156 useSSL = 1; 1157 switch (auth->auth.tlstype) { 1158 case NS_LDAP_TLS_NONE: 1159 bindType = NS_LDAP_AUTH_NONE; 1160 break; 1161 case NS_LDAP_TLS_SIMPLE: 1162 bindType = NS_LDAP_AUTH_SIMPLE; 1163 break; 1164 case NS_LDAP_TLS_SASL: 1165 bindType = NS_LDAP_AUTH_SASL; 1166 break; 1167 default: 1168 (void) sprintf(errstr, 1169 gettext("openConnection: unsupported " 1170 "TLS authentication method " 1171 "(%d)"), auth->auth.tlstype); 1172 MKERROR(LOG_WARNING, *errorp, 1173 LDAP_AUTH_METHOD_NOT_SUPPORTED, 1174 strdup(errstr), NULL); 1175 return (NS_LDAP_INTERNAL); 1176 } 1177 break; 1178 default: 1179 (void) sprintf(errstr, 1180 gettext("openConnection: unsupported " 1181 "authentication method (%d)"), auth->auth.type); 1182 MKERROR(LOG_WARNING, *errorp, 1183 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 1184 NULL); 1185 return (NS_LDAP_INTERNAL); 1186 } 1187 1188 if (useSSL) { 1189 const char *hostcertpath; 1190 char *alloc_hcp = NULL; 1191 #ifdef DEBUG 1192 (void) fprintf(stderr, "+++TLS transport\n"); 1193 #endif /* DEBUG */ 1194 hostcertpath = auth->hostcertpath; 1195 if (hostcertpath == NULL) { 1196 alloc_hcp = __s_get_hostcertpath(); 1197 hostcertpath = alloc_hcp; 1198 } 1199 1200 if (hostcertpath == NULL) 1201 return (NS_LDAP_MEMORY); 1202 1203 if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) { 1204 if (alloc_hcp) 1205 free(alloc_hcp); 1206 (void) snprintf(errstr, sizeof (errstr), 1207 gettext("openConnection: failed to initialize " 1208 "TLS security (%s)"), 1209 ldapssl_err2string(rc)); 1210 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1211 strdup(errstr), NULL); 1212 return (NS_LDAP_INTERNAL); 1213 } 1214 if (alloc_hcp) 1215 free(alloc_hcp); 1216 1217 /* determine if the host name contains a port number */ 1218 s = strchr(serverAddr, ']'); /* skip over ipv6 addr */ 1219 if (s == NULL) 1220 s = serverAddr; 1221 s = strchr(s, ':'); 1222 if (s != NULL) { 1223 /* 1224 * If we do get a port number, we will try stripping 1225 * it. At present, referrals will always have a 1226 * port number. 1227 */ 1228 sslServerAddr = strdup(serverAddr); 1229 if (sslServerAddr == NULL) 1230 return (NS_LDAP_MEMORY); 1231 s1 = strrchr(sslServerAddr, ':'); 1232 if (s1 != NULL) 1233 *s1 = '\0'; 1234 (void) snprintf(errstr, sizeof (errstr), 1235 gettext("openConnection: cannot use tls with %s. " 1236 "Trying %s"), 1237 serverAddr, sslServerAddr); 1238 syslog(LOG_ERR, "libsldap: %s", errstr); 1239 } else 1240 sslServerAddr = (char *)serverAddr; 1241 1242 ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1); 1243 1244 if (sslServerAddr != serverAddr) 1245 free(sslServerAddr); 1246 1247 if (ld == NULL || 1248 ldapssl_install_gethostbyaddr(ld, "ldap") != 0) { 1249 (void) snprintf(errstr, sizeof (errstr), 1250 gettext("openConnection: failed to connect " 1251 "using TLS (%s)"), strerror(errno)); 1252 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1253 strdup(errstr), NULL); 1254 return (NS_LDAP_INTERNAL); 1255 } 1256 } else { 1257 #ifdef DEBUG 1258 (void) fprintf(stderr, "+++Unsecure transport\n"); 1259 #endif /* DEBUG */ 1260 /* Warning message IF cannot connect to host(s) */ 1261 if ((ld = ldap_init((char *)serverAddr, LDAP_PORT)) == NULL) { 1262 char *p = strerror(errno); 1263 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1264 strdup(p), NULL); 1265 return (NS_LDAP_INTERNAL); 1266 } else { 1267 /* check and avoid gethostname recursion */ 1268 if (ldap_in_hosts_switch() > 0 && 1269 ! __s_api_isipv4((char *)serverAddr) && 1270 ! __s_api_isipv6((char *)serverAddr)) { 1271 /* host: ldap - found, attempt to recover */ 1272 if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB, 1273 "ldap") != 0) { 1274 (void) snprintf(errstr, sizeof (errstr), 1275 gettext("openConnection: " 1276 "unrecoverable gethostname " 1277 "recursion detected " 1278 "in /etc/nsswitch.conf")); 1279 MKERROR(LOG_WARNING, *errorp, 1280 LDAP_CONNECT_ERROR, 1281 strdup(errstr), NULL); 1282 (void) ldap_unbind(ld); 1283 return (NS_LDAP_INTERNAL); 1284 } 1285 } 1286 } 1287 } 1288 1289 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); 1290 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 1291 /* 1292 * set LDAP_OPT_REFERRALS to OFF. 1293 * This library will handle the referral itself 1294 * based on API flags or configuration file 1295 * specification. If this option is not set 1296 * to OFF, libldap will never pass the 1297 * referral info up to this library 1298 */ 1299 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1300 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 1301 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 1302 /* setup TCP/IP connect timeout */ 1303 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 1304 &timeoutMilliSec); 1305 /* retry if LDAP I/O was interrupted */ 1306 (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1307 1308 switch (bindType) { 1309 case NS_LDAP_AUTH_NONE: 1310 #ifdef DEBUG 1311 (void) fprintf(stderr, "+++Anonymous bind\n"); 1312 #endif /* DEBUG */ 1313 break; 1314 case NS_LDAP_AUTH_SIMPLE: 1315 binddn = auth->cred.unix_cred.userID; 1316 passwd = auth->cred.unix_cred.passwd; 1317 if (passwd == NULL || *passwd == '\0' || 1318 binddn == NULL || *binddn == '\0') { 1319 (void) sprintf(errstr, gettext("openConnection: " 1320 "missing credentials for Simple bind")); 1321 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 1322 strdup(errstr), NULL); 1323 (void) ldap_unbind(ld); 1324 return (NS_LDAP_INTERNAL); 1325 } 1326 1327 #ifdef DEBUG 1328 (void) fprintf(stderr, "+++Simple bind\n"); 1329 #endif /* DEBUG */ 1330 msgId = ldap_simple_bind(ld, binddn, passwd); 1331 1332 if (msgId == -1) { 1333 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 1334 (void *)&errnum); 1335 (void) snprintf(errstr, sizeof (errstr), 1336 gettext("openConnection: simple bind failed " 1337 "- %s"), ldap_err2string(errnum)); 1338 (void) ldap_unbind(ld); 1339 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 1340 NULL); 1341 return (NS_LDAP_INTERNAL); 1342 } 1343 1344 tv.tv_sec = timeoutSec; 1345 tv.tv_usec = 0; 1346 rc = ldap_result(ld, msgId, 0, &tv, &resultMsg); 1347 1348 if ((rc == -1) || (rc == 0)) { 1349 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 1350 (void *)&errnum); 1351 (void) snprintf(errstr, sizeof (errstr), 1352 gettext("openConnection: simple bind failed " 1353 "- %s"), ldap_err2string(errnum)); 1354 (void) ldap_msgfree(resultMsg); 1355 (void) ldap_unbind(ld); 1356 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 1357 NULL); 1358 return (NS_LDAP_INTERNAL); 1359 } 1360 1361 /* 1362 * get ldaprc, controls, and error msg 1363 */ 1364 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 1365 &errmsg, NULL, &controls, 1); 1366 1367 if (rc != LDAP_SUCCESS) { 1368 (void) snprintf(errstr, sizeof (errstr), 1369 gettext("openConnection: simple bind failed " 1370 "- unable to parse result")); 1371 (void) ldap_unbind(ld); 1372 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 1373 strdup(errstr), NULL); 1374 return (NS_LDAP_INTERNAL); 1375 } 1376 1377 /* process the password management info, if any */ 1378 pwd_rc = process_pwd_mgmt("simple", 1379 errnum, controls, errmsg, 1380 errorp, 1381 fail_if_new_pwd_reqd, 1382 passwd_mgmt); 1383 1384 if (pwd_rc == NS_LDAP_INTERNAL) { 1385 (void) ldap_unbind(ld); 1386 return (pwd_rc); 1387 } 1388 1389 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 1390 *ldp = ld; 1391 return (pwd_rc); 1392 } 1393 1394 break; 1395 case NS_LDAP_AUTH_SASL: 1396 /* We don't support any sasl options yet */ 1397 if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE) { 1398 (void) sprintf(errstr, 1399 gettext("openConnection: SASL options are " 1400 "not supported (%d)"), auth->auth.saslopt); 1401 MKERROR(LOG_WARNING, *errorp, 1402 LDAP_AUTH_METHOD_NOT_SUPPORTED, 1403 strdup(errstr), NULL); 1404 (void) ldap_unbind(ld); 1405 return (NS_LDAP_INTERNAL); 1406 } 1407 binddn = auth->cred.unix_cred.userID; 1408 passwd = auth->cred.unix_cred.passwd; 1409 if (passwd == NULL || *passwd == '\0' || 1410 binddn == NULL || *binddn == '\0') { 1411 (void) sprintf(errstr, 1412 gettext("openConnection: missing credentials " 1413 "for SASL bind")); 1414 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 1415 strdup(errstr), NULL); 1416 (void) ldap_unbind(ld); 1417 return (NS_LDAP_INTERNAL); 1418 } 1419 cred.bv_val = passwd; 1420 cred.bv_len = strlen(passwd); 1421 1422 switch (auth->auth.saslmech) { 1423 case NS_LDAP_SASL_CRAM_MD5: 1424 /* 1425 * NOTE: if iDS changes to support cram_md5, 1426 * please add password management code here. 1427 * Since ldap_sasl_cram_md5_bind_s does not 1428 * return anything that could be used to 1429 * extract the ldap rc/errmsg/control to 1430 * determine if bind failed due to password 1431 * policy, a new cram_md5_bind API will need 1432 * to be introduced. See 1433 * ldap_x_sasl_digest_md5_bind() and case 1434 * NS_LDAP_SASL_DIGEST_MD5 below for details. 1435 */ 1436 if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn, 1437 &cred, NULL, NULL)) != LDAP_SUCCESS) { 1438 (void) ldap_get_option(ld, 1439 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 1440 (void) snprintf(errstr, sizeof (errstr), 1441 gettext("openConnection: " 1442 "sasl/CRAM-MD5 bind failed - %s"), 1443 ldap_err2string(errnum)); 1444 MKERROR(LOG_WARNING, *errorp, errnum, 1445 strdup(errstr), NULL); 1446 (void) ldap_unbind(ld); 1447 return (NS_LDAP_INTERNAL); 1448 } 1449 break; 1450 case NS_LDAP_SASL_DIGEST_MD5: 1451 digest_md5_name = malloc(strlen(binddn) + 5); 1452 /* 5 = strlen("dn: ") + 1 */ 1453 if (digest_md5_name == NULL) { 1454 (void) ldap_unbind(ld); 1455 return (NS_LDAP_MEMORY); 1456 } 1457 (void) strcpy(digest_md5_name, "dn: "); 1458 (void) strcat(digest_md5_name, binddn); 1459 1460 tv.tv_sec = timeoutSec; 1461 tv.tv_usec = 0; 1462 rc = ldap_x_sasl_digest_md5_bind(ld, 1463 digest_md5_name, &cred, NULL, NULL, 1464 &tv, &resultMsg); 1465 1466 if (resultMsg == NULL) { 1467 free(digest_md5_name); 1468 (void) ldap_get_option(ld, 1469 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 1470 (void) snprintf(errstr, sizeof (errstr), 1471 gettext("openConnection: " 1472 "DIGEST-MD5 bind failed - %s"), 1473 ldap_err2string(errnum)); 1474 (void) ldap_unbind(ld); 1475 MKERROR(LOG_WARNING, *errorp, errnum, 1476 strdup(errstr), NULL); 1477 return (NS_LDAP_INTERNAL); 1478 } 1479 1480 /* 1481 * get ldaprc, controls, and error msg 1482 */ 1483 rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 1484 &errmsg, NULL, &controls, 1); 1485 1486 if (rc != LDAP_SUCCESS) { 1487 free(digest_md5_name); 1488 (void) snprintf(errstr, sizeof (errstr), 1489 gettext("openConnection: " 1490 "DIGEST-MD5 bind failed " 1491 "- unable to parse result")); 1492 (void) ldap_unbind(ld); 1493 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 1494 strdup(errstr), NULL); 1495 return (NS_LDAP_INTERNAL); 1496 } 1497 1498 /* process the password management info, if any */ 1499 pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5", 1500 errnum, controls, errmsg, 1501 errorp, 1502 fail_if_new_pwd_reqd, 1503 passwd_mgmt); 1504 1505 if (pwd_rc == NS_LDAP_INTERNAL) { 1506 free(digest_md5_name); 1507 (void) ldap_unbind(ld); 1508 return (pwd_rc); 1509 } 1510 1511 if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) { 1512 *ldp = ld; 1513 return (pwd_rc); 1514 } 1515 1516 free(digest_md5_name); 1517 break; 1518 default: 1519 (void) ldap_unbind(ld); 1520 (void) sprintf(errstr, 1521 gettext("openConnection: unsupported SASL " 1522 "mechanism (%d)"), auth->auth.saslmech); 1523 MKERROR(LOG_WARNING, *errorp, 1524 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 1525 NULL); 1526 return (NS_LDAP_INTERNAL); 1527 } 1528 } 1529 1530 *ldp = ld; 1531 return (NS_LDAP_SUCCESS); 1532 } 1533 1534 /* 1535 * FUNCTION: __s_api_getDefaultAuth 1536 * 1537 * Constructs a credential for authentication using the config module. 1538 * 1539 * RETURN VALUES: 1540 * 1541 * NS_LDAP_SUCCESS If successful 1542 * NS_LDAP_CONFIG If there are any config errors. 1543 * NS_LDAP_MEMORY Memory errors. 1544 * NS_LDAP_OP_FAILED If there are no more authentication methods so can 1545 * not build a new authp. 1546 * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the 1547 * necessary fields of a cred for a given auth method 1548 * are not provided. 1549 * INPUT: 1550 * 1551 * cLevel Currently requested credential level to be tried 1552 * 1553 * aMethod Currently requested authentication method to be tried 1554 * 1555 * OUTPUT: 1556 * 1557 * authp authentication method to use. 1558 */ 1559 static int 1560 __s_api_getDefaultAuth( 1561 int *cLevel, 1562 ns_auth_t *aMethod, 1563 ns_cred_t **authp) 1564 { 1565 void **paramVal = NULL; 1566 char *modparamVal = NULL; 1567 int getUid = 0; 1568 int getPasswd = 0; 1569 int getCertpath = 0; 1570 int rc = 0; 1571 ns_ldap_error_t *errorp = NULL; 1572 1573 #ifdef DEBUG 1574 (void) fprintf(stderr, "__s_api_getDefaultAuth START\n"); 1575 #endif 1576 1577 if (aMethod == NULL) { 1578 /* Require an Auth */ 1579 return (NS_LDAP_INVALID_PARAM); 1580 1581 } 1582 1583 /* 1584 * Do not differentiate between (proxy/self) at this time, but 1585 * reject self credential levels at this time 1586 */ 1587 if (cLevel && *cLevel == NS_LDAP_CRED_SELF) 1588 return (NS_LDAP_INVALID_PARAM); 1589 1590 *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); 1591 if ((*authp) == NULL) 1592 return (NS_LDAP_MEMORY); 1593 1594 (*authp)->auth = *aMethod; 1595 1596 switch (aMethod->type) { 1597 case NS_LDAP_AUTH_NONE: 1598 return (NS_LDAP_SUCCESS); 1599 case NS_LDAP_AUTH_SIMPLE: 1600 getUid++; 1601 getPasswd++; 1602 break; 1603 case NS_LDAP_AUTH_SASL: 1604 if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 1605 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) { 1606 getUid++; 1607 getPasswd++; 1608 } else { 1609 (void) __ns_ldap_freeCred(authp); 1610 *authp = NULL; 1611 return (NS_LDAP_INVALID_PARAM); 1612 } 1613 break; 1614 case NS_LDAP_AUTH_TLS: 1615 if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) || 1616 ((aMethod->tlstype == NS_LDAP_TLS_SASL) && 1617 ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 1618 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) { 1619 getUid++; 1620 getPasswd++; 1621 getCertpath++; 1622 } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) { 1623 getCertpath++; 1624 } else { 1625 (void) __ns_ldap_freeCred(authp); 1626 *authp = NULL; 1627 return (NS_LDAP_INVALID_PARAM); 1628 } 1629 break; 1630 } 1631 1632 if (getUid) { 1633 paramVal = NULL; 1634 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P, 1635 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1636 (void) __ns_ldap_freeCred(authp); 1637 (void) __ns_ldap_freeError(&errorp); 1638 *authp = NULL; 1639 return (rc); 1640 } 1641 1642 if (paramVal == NULL || *paramVal == NULL) { 1643 (void) __ns_ldap_freeCred(authp); 1644 *authp = NULL; 1645 return (NS_LDAP_INVALID_PARAM); 1646 } 1647 1648 (*authp)->cred.unix_cred.userID = strdup((char *)*paramVal); 1649 (void) __ns_ldap_freeParam(¶mVal); 1650 if ((*authp)->cred.unix_cred.userID == NULL) { 1651 (void) __ns_ldap_freeCred(authp); 1652 *authp = NULL; 1653 return (NS_LDAP_MEMORY); 1654 } 1655 } 1656 if (getPasswd) { 1657 paramVal = NULL; 1658 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P, 1659 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1660 (void) __ns_ldap_freeCred(authp); 1661 (void) __ns_ldap_freeError(&errorp); 1662 *authp = NULL; 1663 return (rc); 1664 } 1665 1666 if (paramVal == NULL || *paramVal == NULL) { 1667 (void) __ns_ldap_freeCred(authp); 1668 *authp = NULL; 1669 return (NS_LDAP_INVALID_PARAM); 1670 } 1671 1672 modparamVal = dvalue((char *)*paramVal); 1673 (void) __ns_ldap_freeParam(¶mVal); 1674 if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) { 1675 (void) __ns_ldap_freeCred(authp); 1676 if (modparamVal != NULL) 1677 free(modparamVal); 1678 *authp = NULL; 1679 return (NS_LDAP_INVALID_PARAM); 1680 } 1681 1682 (*authp)->cred.unix_cred.passwd = modparamVal; 1683 } 1684 if (getCertpath) { 1685 paramVal = NULL; 1686 if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P, 1687 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1688 (void) __ns_ldap_freeCred(authp); 1689 (void) __ns_ldap_freeError(&errorp); 1690 *authp = NULL; 1691 return (rc); 1692 } 1693 1694 if (paramVal == NULL || *paramVal == NULL) { 1695 (void) __ns_ldap_freeCred(authp); 1696 *authp = NULL; 1697 return (NS_LDAP_INVALID_PARAM); 1698 } 1699 1700 (*authp)->hostcertpath = strdup((char *)*paramVal); 1701 (void) __ns_ldap_freeParam(¶mVal); 1702 if ((*authp)->hostcertpath == NULL) { 1703 (void) __ns_ldap_freeCred(authp); 1704 *authp = NULL; 1705 return (NS_LDAP_MEMORY); 1706 } 1707 } 1708 return (NS_LDAP_SUCCESS); 1709 } 1710 1711 /* 1712 * FUNCTION: __s_api_getConnection 1713 * 1714 * Bind to the specified server or one from the server 1715 * list and return the pointer. 1716 * 1717 * This function can rebind or not (NS_LDAP_HARD), it can require a 1718 * credential or bind anonymously 1719 * 1720 * This function follows the DUA configuration schema algorithm 1721 * 1722 * RETURN VALUES: 1723 * 1724 * NS_LDAP_SUCCESS A connection was made successfully. 1725 * NS_LDAP_SUCCESS_WITH_INFO 1726 * A connection was made successfully, but with 1727 * password management info in *errorp 1728 * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function. 1729 * NS_LDAP_CONFIG If there are any config errors. 1730 * NS_LDAP_MEMORY Memory errors. 1731 * NS_LDAP_INTERNAL If there was a ldap error. 1732 * 1733 * INPUT: 1734 * 1735 * server Bind to this LDAP server only 1736 * flags If NS_LDAP_HARD is set function will not return until it has 1737 * a connection unless there is a authentication problem. 1738 * If NS_LDAP_NEW_CONN is set the function must force a new 1739 * connection to be created 1740 * If NS_LDAP_KEEP_CONN is set the connection is to be kept open 1741 * auth Credentials for bind. This could be NULL in which case 1742 * a default cred built from the config module is used. 1743 * sessionId cookie that points to a previous session 1744 * fail_if_new_pwd_reqd 1745 * a flag indicating this function should fail if the passwd 1746 * in auth needs to change immediately 1747 * 1748 * OUTPUT: 1749 * 1750 * session pointer to a session with connection information 1751 * errorp Set if there are any INTERNAL, or CONFIG error. 1752 */ 1753 int 1754 __s_api_getConnection( 1755 const char *server, 1756 const int flags, 1757 const ns_cred_t *cred, /* credentials for bind */ 1758 ConnectionID *sessionId, 1759 Connection **session, 1760 ns_ldap_error_t **errorp, 1761 int fail_if_new_pwd_reqd) 1762 { 1763 char errmsg[MAXERROR]; 1764 ns_auth_t **aMethod = NULL; 1765 ns_auth_t **aNext = NULL; 1766 int **cLevel = NULL; 1767 int **cNext = NULL; 1768 int timeoutSec = NS_DEFAULT_BIND_TIMEOUT; 1769 int rc; 1770 Connection *con = NULL; 1771 int sec = 1; 1772 ns_cred_t *authp = NULL; 1773 ns_cred_t anon; 1774 int version = NS_LDAP_V2; 1775 void **paramVal = NULL; 1776 1777 if ((session == NULL) || (sessionId == NULL)) { 1778 return (NS_LDAP_INVALID_PARAM); 1779 } 1780 *session = NULL; 1781 1782 /* if we already have a session id try to reuse connection */ 1783 if (*sessionId > 0) { 1784 rc = findConnectionById(flags, cred, *sessionId, &con); 1785 if (rc == *sessionId && con) { 1786 *session = con; 1787 return (NS_LDAP_SUCCESS); 1788 } 1789 *sessionId = 0; 1790 } 1791 1792 /* get profile version number */ 1793 if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, 1794 ¶mVal, errorp)) != NS_LDAP_SUCCESS) 1795 return (rc); 1796 if (paramVal == NULL) { 1797 (void) sprintf(errmsg, gettext("getConnection: no file " 1798 "version")); 1799 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg), 1800 NS_LDAP_CONFIG); 1801 return (NS_LDAP_CONFIG); 1802 } 1803 if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0) 1804 version = NS_LDAP_V1; 1805 (void) __ns_ldap_freeParam((void ***)¶mVal); 1806 1807 /* Get the bind timeout value */ 1808 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, ¶mVal, errorp); 1809 if (paramVal != NULL && *paramVal != NULL) { 1810 timeoutSec = **((int **)paramVal); 1811 (void) __ns_ldap_freeParam(¶mVal); 1812 } 1813 if (*errorp) 1814 (void) __ns_ldap_freeError(errorp); 1815 1816 if (cred == NULL) { 1817 /* Get the authentication method list */ 1818 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, 1819 (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS) 1820 return (rc); 1821 if (aMethod == NULL) { 1822 aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *)); 1823 if (aMethod == NULL) 1824 return (NS_LDAP_MEMORY); 1825 aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t)); 1826 if (aMethod[0] == NULL) { 1827 free(aMethod); 1828 return (NS_LDAP_MEMORY); 1829 } 1830 if (version == NS_LDAP_V1) 1831 (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE; 1832 else { 1833 (aMethod[0])->type = NS_LDAP_AUTH_SASL; 1834 (aMethod[0])->saslmech = 1835 NS_LDAP_SASL_DIGEST_MD5; 1836 (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE; 1837 } 1838 } 1839 1840 /* Get the credential level list */ 1841 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P, 1842 (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) { 1843 (void) __ns_ldap_freeParam((void ***)&aMethod); 1844 return (rc); 1845 } 1846 if (cLevel == NULL) { 1847 cLevel = (int **)calloc(2, sizeof (int *)); 1848 if (cLevel == NULL) 1849 return (NS_LDAP_MEMORY); 1850 cLevel[0] = (int *)calloc(1, sizeof (int)); 1851 if (cLevel[0] == NULL) 1852 return (NS_LDAP_MEMORY); 1853 if (version == NS_LDAP_V1) 1854 *(cLevel[0]) = NS_LDAP_CRED_PROXY; 1855 else 1856 *(cLevel[0]) = NS_LDAP_CRED_ANON; 1857 } 1858 } 1859 1860 /* setup the anon credential for anonymous connection */ 1861 (void) memset(&anon, 0, sizeof (ns_cred_t)); 1862 anon.auth.type = NS_LDAP_AUTH_NONE; 1863 1864 for (; ; ) { 1865 if (cred != NULL) { 1866 /* using specified auth method */ 1867 rc = makeConnection(&con, server, cred, 1868 sessionId, timeoutSec, errorp, 1869 fail_if_new_pwd_reqd); 1870 if (rc == NS_LDAP_SUCCESS || 1871 rc == NS_LDAP_SUCCESS_WITH_INFO) { 1872 *session = con; 1873 break; 1874 } 1875 } else { 1876 /* for every cred level */ 1877 for (cNext = cLevel; *cNext != NULL; cNext++) { 1878 if (**cNext == NS_LDAP_CRED_ANON) { 1879 /* make connection anonymously */ 1880 rc = makeConnection(&con, server, &anon, 1881 sessionId, timeoutSec, errorp, 1882 fail_if_new_pwd_reqd); 1883 if (rc == NS_LDAP_SUCCESS || 1884 rc == 1885 NS_LDAP_SUCCESS_WITH_INFO) { 1886 *session = con; 1887 goto done; 1888 } 1889 continue; 1890 } 1891 /* for each cred level */ 1892 for (aNext = aMethod; *aNext != NULL; aNext++) { 1893 /* make connection and authenticate */ 1894 /* with default credentials */ 1895 authp = NULL; 1896 rc = __s_api_getDefaultAuth(*cNext, 1897 *aNext, &authp); 1898 if (rc != NS_LDAP_SUCCESS) { 1899 continue; 1900 } 1901 rc = makeConnection(&con, server, authp, 1902 sessionId, timeoutSec, errorp, 1903 fail_if_new_pwd_reqd); 1904 (void) __ns_ldap_freeCred(&authp); 1905 if (rc == NS_LDAP_SUCCESS || 1906 rc == 1907 NS_LDAP_SUCCESS_WITH_INFO) { 1908 *session = con; 1909 goto done; 1910 } 1911 } 1912 } 1913 } 1914 if (flags & NS_LDAP_HARD) { 1915 if (sec < LDAPMAXHARDLOOKUPTIME) 1916 sec *= 2; 1917 _sleep(sec); 1918 } else { 1919 break; 1920 } 1921 } 1922 1923 done: 1924 (void) __ns_ldap_freeParam((void ***)&aMethod); 1925 (void) __ns_ldap_freeParam((void ***)&cLevel); 1926 return (rc); 1927 } 1928 1929 #pragma fini(_free_sessionPool) 1930 static void 1931 _free_sessionPool() 1932 { 1933 int id; 1934 1935 (void) mutex_lock(&sessionPoolLock); 1936 if (sessionPool != NULL) { 1937 for (id = 0; id < sessionPoolSize; id++) 1938 _DropConnection(id + CONID_OFFSET, 0, 1); 1939 free(sessionPool); 1940 sessionPool = NULL; 1941 sessionPoolSize = 0; 1942 } 1943 (void) mutex_unlock(&sessionPoolLock); 1944 } 1945