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