1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <synch.h> 33 #include <time.h> 34 #include <libintl.h> 35 #include <thread.h> 36 #include <syslog.h> 37 #include <sys/mman.h> 38 #include <nsswitch.h> 39 #include <nss_dbdefs.h> 40 #include "solaris-priv.h" 41 #include "solaris-int.h" 42 #include "ns_sldap.h" 43 #include "ns_internal.h" 44 #include "ns_cache_door.h" 45 #include "ns_connmgmt.h" 46 #include "ldappr.h" 47 #include <sys/stat.h> 48 #include <fcntl.h> 49 #include <procfs.h> 50 #include <unistd.h> 51 52 #define USE_DEFAULT_PORT 0 53 54 static ns_ldap_return_code performBind(const ns_cred_t *, 55 LDAP *, 56 int, 57 ns_ldap_error_t **, 58 int, 59 int); 60 static ns_ldap_return_code createSession(const ns_cred_t *, 61 const char *, 62 uint16_t, 63 int, 64 LDAP **, 65 ns_ldap_error_t **); 66 67 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *, 68 LDAPControl **, LDAPControl **); 69 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip); 70 71 static int openConnection(LDAP **, const char *, const ns_cred_t *, 72 int, ns_ldap_error_t **, int, int, ns_conn_user_t *); 73 static void 74 _DropConnection(ConnectionID cID, int flag, int fini); 75 76 static mutex_t sessionPoolLock = DEFAULTMUTEX; 77 78 static Connection **sessionPool = NULL; 79 static int sessionPoolSize = 0; 80 81 /* 82 * SSF values are for SASL integrity & privacy. 83 * JES DS5.2 does not support this feature but DS6 does. 84 * The values between 0 and 65535 can work with both server versions. 85 */ 86 #define MAX_SASL_SSF 65535 87 #define MIN_SASL_SSF 0 88 89 /* Number of hostnames to allocate memory for */ 90 #define NUMTOMALLOC 32 91 92 /* 93 * This function get the servers from the lists and returns 94 * the first server with the empty lists of server controls and 95 * SASL mechanisms. It is invoked if it is not possible to obtain a server 96 * from ldap_cachemgr or the local list. 97 */ 98 static 99 ns_ldap_return_code 100 getFirstFromConfig(ns_server_info_t *ret, ns_ldap_error_t **error) 101 { 102 char **servers = NULL; 103 ns_ldap_return_code ret_code; 104 char errstr[MAXERROR]; 105 106 /* get first server from config list unavailable otherwise */ 107 ret_code = __s_api_getServers(&servers, error); 108 if (ret_code != NS_LDAP_SUCCESS) { 109 if (servers != NULL) { 110 __s_api_free2dArray(servers); 111 } 112 return (ret_code); 113 } 114 115 if (servers == NULL || servers[0] == NULL) { 116 __s_api_free2dArray(servers); 117 (void) sprintf(errstr, 118 gettext("No server found in configuration")); 119 MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT, 120 strdup(errstr), NS_LDAP_MEMORY); 121 return (NS_LDAP_CONFIG); 122 } 123 124 ret->server = strdup(servers[0]); 125 if (ret->server == NULL) { 126 __s_api_free2dArray(servers); 127 return (NS_LDAP_MEMORY); 128 } 129 130 ret->saslMechanisms = NULL; 131 ret->controls = NULL; 132 133 __s_api_free2dArray(servers); 134 135 return (NS_LDAP_SUCCESS); 136 } 137 138 /* 139 * This function requests a server from the cache manager through 140 * the door functionality 141 */ 142 143 int 144 __s_api_requestServer(const char *request, const char *server, 145 ns_server_info_t *ret, ns_ldap_error_t **error, const char *addrType) 146 { 147 union { 148 ldap_data_t s_d; 149 char s_b[DOORBUFFERSIZE]; 150 } space; 151 ldap_data_t *sptr; 152 int ndata; 153 int adata; 154 char errstr[MAXERROR]; 155 const char *ireq; 156 char *rbuf, *ptr, *rest; 157 char *dptr; 158 char **mptr, **mptr1, **cptr, **cptr1; 159 int mcnt, ccnt; 160 int len; 161 ns_ldap_return_code ret_code; 162 163 if (ret == NULL || error == NULL) { 164 return (NS_LDAP_OP_FAILED); 165 } 166 (void) memset(ret, 0, sizeof (ns_server_info_t)); 167 *error = NULL; 168 169 if (request == NULL) 170 ireq = NS_CACHE_NEW; 171 else 172 ireq = request; 173 174 /* 175 * In the 'Standalone' mode a server will be obtained 176 * from the local libsldap's list 177 */ 178 if (__s_api_isStandalone()) { 179 if ((ret_code = __s_api_findRootDSE(ireq, 180 server, 181 addrType, 182 ret, 183 error)) != NS_LDAP_SUCCESS) { 184 /* 185 * get first server from local list only once 186 * to prevent looping 187 */ 188 if (strcmp(ireq, NS_CACHE_NEW) != 0) 189 return (ret_code); 190 191 syslog(LOG_WARNING, 192 "libsldap (\"standalone\" mode): " 193 "can not find any available server. " 194 "Return the first one from the lists"); 195 if (*error != NULL) { 196 (void) __ns_ldap_freeError(error); 197 } 198 199 ret_code = getFirstFromConfig(ret, error); 200 if (ret_code != NS_LDAP_SUCCESS) { 201 return (ret_code); 202 } 203 204 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) { 205 ret_code = __s_api_ip2hostname(ret->server, 206 &ret->serverFQDN); 207 if (ret_code != NS_LDAP_SUCCESS) { 208 (void) snprintf(errstr, 209 sizeof (errstr), 210 gettext("The %s address " 211 "can not be resolved into " 212 "a host name. Returning " 213 "the address as it is."), 214 ret->server); 215 MKERROR(LOG_ERR, 216 *error, 217 NS_CONFIG_NOTLOADED, 218 strdup(errstr), 219 NS_LDAP_MEMORY); 220 free(ret->server); 221 ret->server = NULL; 222 return (NS_LDAP_INTERNAL); 223 } 224 } 225 } 226 227 return (NS_LDAP_SUCCESS); 228 } 229 230 (void) memset(space.s_b, 0, DOORBUFFERSIZE); 231 232 adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1); 233 if (server != NULL) { 234 adata += strlen(DOORLINESEP) + 1; 235 adata += strlen(server) + 1; 236 } 237 ndata = sizeof (space); 238 len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber); 239 space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER; 240 if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len) 241 return (NS_LDAP_MEMORY); 242 if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >= 243 len) 244 return (NS_LDAP_MEMORY); 245 if (server != NULL) { 246 if (strlcat(space.s_d.ldap_call.ldap_u.domainname, 247 DOORLINESEP, len) >= len) 248 return (NS_LDAP_MEMORY); 249 if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, 250 len) >= len) 251 return (NS_LDAP_MEMORY); 252 } 253 sptr = &space.s_d; 254 255 switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) { 256 case NS_CACHE_SUCCESS: 257 break; 258 /* this case is for when the $mgr is not running, but ldapclient */ 259 /* is trying to initialize things */ 260 case NS_CACHE_NOSERVER: 261 ret_code = getFirstFromConfig(ret, error); 262 if (ret_code != NS_LDAP_SUCCESS) { 263 return (ret_code); 264 } 265 266 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) { 267 ret_code = __s_api_ip2hostname(ret->server, 268 &ret->serverFQDN); 269 if (ret_code != NS_LDAP_SUCCESS) { 270 (void) snprintf(errstr, 271 sizeof (errstr), 272 gettext("The %s address " 273 "can not be resolved into " 274 "a host name. Returning " 275 "the address as it is."), 276 ret->server); 277 MKERROR(LOG_ERR, 278 *error, 279 NS_CONFIG_NOTLOADED, 280 strdup(errstr), 281 NS_LDAP_MEMORY); 282 free(ret->server); 283 ret->server = NULL; 284 return (NS_LDAP_INTERNAL); 285 } 286 } 287 return (NS_LDAP_SUCCESS); 288 case NS_CACHE_NOTFOUND: 289 default: 290 return (NS_LDAP_OP_FAILED); 291 } 292 293 /* copy info from door call return structure here */ 294 rbuf = space.s_d.ldap_ret.ldap_u.config; 295 296 /* Get the host */ 297 ptr = strtok_r(rbuf, DOORLINESEP, &rest); 298 if (ptr == NULL) { 299 (void) sprintf(errstr, gettext("No server returned from " 300 "ldap_cachemgr")); 301 MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR, 302 strdup(errstr), NS_LDAP_MEMORY); 303 return (NS_LDAP_OP_FAILED); 304 } 305 ret->server = strdup(ptr); 306 if (ret->server == NULL) { 307 return (NS_LDAP_MEMORY); 308 } 309 /* Get the host FQDN format */ 310 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) { 311 ptr = strtok_r(NULL, DOORLINESEP, &rest); 312 if (ptr == NULL) { 313 (void) sprintf(errstr, gettext("No server FQDN format " 314 "returned from ldap_cachemgr")); 315 MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR, 316 strdup(errstr), NULL); 317 free(ret->server); 318 ret->server = NULL; 319 return (NS_LDAP_OP_FAILED); 320 } 321 ret->serverFQDN = strdup(ptr); 322 if (ret->serverFQDN == NULL) { 323 free(ret->server); 324 ret->server = NULL; 325 return (NS_LDAP_MEMORY); 326 } 327 } 328 329 /* get the Supported Controls/SASL mechs */ 330 mptr = NULL; 331 mcnt = 0; 332 cptr = NULL; 333 ccnt = 0; 334 for (;;) { 335 ptr = strtok_r(NULL, DOORLINESEP, &rest); 336 if (ptr == NULL) 337 break; 338 if (strncasecmp(ptr, _SASLMECHANISM, 339 _SASLMECHANISM_LEN) == 0) { 340 dptr = strchr(ptr, '='); 341 if (dptr == NULL) 342 continue; 343 dptr++; 344 mptr1 = (char **)realloc((void *)mptr, 345 sizeof (char *) * (mcnt+2)); 346 if (mptr1 == NULL) { 347 __s_api_free2dArray(mptr); 348 if (sptr != &space.s_d) { 349 (void) munmap((char *)sptr, ndata); 350 } 351 __s_api_free2dArray(cptr); 352 __s_api_free_server_info(ret); 353 return (NS_LDAP_MEMORY); 354 } 355 mptr = mptr1; 356 mptr[mcnt] = strdup(dptr); 357 if (mptr[mcnt] == NULL) { 358 if (sptr != &space.s_d) { 359 (void) munmap((char *)sptr, ndata); 360 } 361 __s_api_free2dArray(cptr); 362 cptr = NULL; 363 __s_api_free2dArray(mptr); 364 mptr = NULL; 365 __s_api_free_server_info(ret); 366 return (NS_LDAP_MEMORY); 367 } 368 mcnt++; 369 mptr[mcnt] = NULL; 370 } 371 if (strncasecmp(ptr, _SUPPORTEDCONTROL, 372 _SUPPORTEDCONTROL_LEN) == 0) { 373 dptr = strchr(ptr, '='); 374 if (dptr == NULL) 375 continue; 376 dptr++; 377 cptr1 = (char **)realloc((void *)cptr, 378 sizeof (char *) * (ccnt+2)); 379 if (cptr1 == NULL) { 380 if (sptr != &space.s_d) { 381 (void) munmap((char *)sptr, ndata); 382 } 383 __s_api_free2dArray(cptr); 384 __s_api_free2dArray(mptr); 385 mptr = NULL; 386 __s_api_free_server_info(ret); 387 return (NS_LDAP_MEMORY); 388 } 389 cptr = cptr1; 390 cptr[ccnt] = strdup(dptr); 391 if (cptr[ccnt] == NULL) { 392 if (sptr != &space.s_d) { 393 (void) munmap((char *)sptr, ndata); 394 } 395 __s_api_free2dArray(cptr); 396 cptr = NULL; 397 __s_api_free2dArray(mptr); 398 mptr = NULL; 399 __s_api_free_server_info(ret); 400 return (NS_LDAP_MEMORY); 401 } 402 ccnt++; 403 cptr[ccnt] = NULL; 404 } 405 } 406 if (mptr != NULL) { 407 ret->saslMechanisms = mptr; 408 } 409 if (cptr != NULL) { 410 ret->controls = cptr; 411 } 412 413 414 /* clean up door call */ 415 if (sptr != &space.s_d) { 416 (void) munmap((char *)sptr, ndata); 417 } 418 *error = NULL; 419 420 return (NS_LDAP_SUCCESS); 421 } 422 423 424 #ifdef DEBUG 425 /* 426 * printCred(): prints the credential structure 427 */ 428 static void 429 printCred(FILE *fp, const ns_cred_t *cred) 430 { 431 thread_t t = thr_self(); 432 433 if (cred == NULL) { 434 (void) fprintf(fp, "tid= %d: printCred: cred is NULL\n", t); 435 return; 436 } 437 438 (void) fprintf(fp, "tid= %d: AuthType=%d", t, cred->auth.type); 439 (void) fprintf(fp, "tid= %d: TlsType=%d", t, cred->auth.tlstype); 440 (void) fprintf(fp, "tid= %d: SaslMech=%d", t, cred->auth.saslmech); 441 (void) fprintf(fp, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt); 442 if (cred->hostcertpath) 443 (void) fprintf(fp, "tid= %d: hostCertPath=%s\n", 444 t, cred->hostcertpath); 445 if (cred->cred.unix_cred.userID) 446 (void) fprintf(fp, "tid= %d: userID=%s\n", 447 t, cred->cred.unix_cred.userID); 448 if (cred->cred.unix_cred.passwd) 449 (void) fprintf(fp, "tid= %d: passwd=%s\n", 450 t, cred->cred.unix_cred.passwd); 451 } 452 453 /* 454 * printConnection(): prints the connection structure 455 */ 456 static void 457 printConnection(FILE *fp, Connection *con) 458 { 459 thread_t t = thr_self(); 460 461 if (con == NULL) 462 return; 463 464 (void) fprintf(fp, "tid= %d: connectionID=%d\n", t, con->connectionId); 465 (void) fprintf(fp, "tid= %d: usedBit=%d\n", t, con->usedBit); 466 (void) fprintf(fp, "tid= %d: threadID=%d\n", t, con->threadID); 467 if (con->serverAddr) { 468 (void) fprintf(fp, "tid= %d: serverAddr=%s\n", 469 t, con->serverAddr); 470 } 471 printCred(fp, con->auth); 472 } 473 #endif 474 475 /* 476 * addConnection(): inserts a connection in the connection list. 477 * It will also sets use bit and the thread Id for the thread 478 * using the connection for the first time. 479 * Returns: -1 = failure, new Connection ID = success 480 */ 481 static int 482 addConnection(Connection *con) 483 { 484 int i; 485 486 if (!con) 487 return (-1); 488 #ifdef DEBUG 489 (void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID); 490 #endif /* DEBUG */ 491 (void) mutex_lock(&sessionPoolLock); 492 if (sessionPool == NULL) { 493 sessionPoolSize = SESSION_CACHE_INC; 494 sessionPool = calloc(sessionPoolSize, 495 sizeof (Connection *)); 496 if (!sessionPool) { 497 (void) mutex_unlock(&sessionPoolLock); 498 return (-1); 499 } 500 #ifdef DEBUG 501 (void) fprintf(stderr, "Initialized sessionPool\n"); 502 #endif /* DEBUG */ 503 } 504 for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i) 505 ; 506 if (i == sessionPoolSize) { 507 /* run out of array, need to increase sessionPool */ 508 Connection **cl; 509 cl = (Connection **) realloc(sessionPool, 510 (sessionPoolSize + SESSION_CACHE_INC) * 511 sizeof (Connection *)); 512 if (!cl) { 513 (void) mutex_unlock(&sessionPoolLock); 514 return (-1); 515 } 516 (void) memset(cl + sessionPoolSize, 0, 517 SESSION_CACHE_INC * sizeof (Connection *)); 518 sessionPool = cl; 519 sessionPoolSize += SESSION_CACHE_INC; 520 #ifdef DEBUG 521 (void) fprintf(stderr, "Increased sessionPoolSize to: %d\n", 522 sessionPoolSize); 523 #endif /* DEBUG */ 524 } 525 sessionPool[i] = con; 526 con->usedBit = B_TRUE; 527 (void) mutex_unlock(&sessionPoolLock); 528 con->connectionId = i + CONID_OFFSET; 529 #ifdef DEBUG 530 (void) fprintf(stderr, "Connection added [%d]\n", i); 531 printConnection(stderr, con); 532 #endif /* DEBUG */ 533 return (i + CONID_OFFSET); 534 } 535 536 /* 537 * findConnection(): find an available connection from the list 538 * that matches the criteria specified in Connection structure. 539 * If serverAddr is NULL, then find a connection to any server 540 * as long as it matches the rest of the parameters. 541 * Returns: -1 = failure, the Connection ID found = success. 542 */ 543 static int 544 findConnection(int flags, const char *serverAddr, 545 const ns_cred_t *auth, Connection **conp) 546 { 547 Connection *cp; 548 int i; 549 #ifdef DEBUG 550 thread_t t; 551 #endif /* DEBUG */ 552 553 if (auth == NULL || conp == NULL) 554 return (-1); 555 *conp = NULL; 556 557 /* 558 * If a new connection is requested, no need to continue. 559 * If the process is not nscd and is not requesting keep 560 * connections alive, no need to continue. 561 */ 562 if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() && 563 !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN))) 564 return (-1); 565 566 #ifdef DEBUG 567 t = thr_self(); 568 (void) fprintf(stderr, "tid= %d: Find connection\n", t); 569 (void) fprintf(stderr, "tid= %d: Looking for ....\n", t); 570 if (serverAddr && *serverAddr) 571 (void) fprintf(stderr, "tid= %d: serverAddr=%s\n", 572 t, serverAddr); 573 else 574 (void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t); 575 printCred(stderr, auth); 576 fflush(stderr); 577 #endif /* DEBUG */ 578 if (sessionPool == NULL) 579 return (-1); 580 (void) mutex_lock(&sessionPoolLock); 581 for (i = 0; i < sessionPoolSize; ++i) { 582 if (sessionPool[i] == NULL) 583 continue; 584 cp = sessionPool[i]; 585 #ifdef DEBUG 586 (void) fprintf(stderr, 587 "tid: %d: checking connection [%d] ....\n", t, i); 588 printConnection(stderr, cp); 589 #endif /* DEBUG */ 590 if ((cp->usedBit) || (serverAddr && *serverAddr && 591 (strcasecmp(serverAddr, cp->serverAddr) != 0))) 592 continue; 593 594 if (__s_api_is_auth_matched(cp->auth, auth) == B_FALSE) 595 continue; 596 597 /* found an available connection */ 598 cp->usedBit = B_TRUE; 599 (void) mutex_unlock(&sessionPoolLock); 600 cp->threadID = thr_self(); 601 *conp = cp; 602 #ifdef DEBUG 603 (void) fprintf(stderr, 604 "tid %d: Connection found cID=%d\n", t, i); 605 fflush(stderr); 606 #endif /* DEBUG */ 607 return (i + CONID_OFFSET); 608 } 609 (void) mutex_unlock(&sessionPoolLock); 610 return (-1); 611 } 612 613 /* 614 * Free a Connection structure 615 */ 616 void 617 __s_api_freeConnection(Connection *con) 618 { 619 if (con == NULL) 620 return; 621 if (con->serverAddr) 622 free(con->serverAddr); 623 if (con->auth) 624 (void) __ns_ldap_freeCred(&(con->auth)); 625 if (con->saslMechanisms) { 626 __s_api_free2dArray(con->saslMechanisms); 627 } 628 if (con->controls) { 629 __s_api_free2dArray(con->controls); 630 } 631 free(con); 632 } 633 634 /* 635 * Find a connection matching the passed in criteria. If an open 636 * connection with that criteria exists use it, otherwise open a 637 * new connection. 638 * Success: returns the pointer to the Connection structure 639 * Failure: returns NULL, error code and message should be in errorp 640 */ 641 642 static int 643 makeConnection(Connection **conp, const char *serverAddr, 644 const ns_cred_t *auth, ConnectionID *cID, int timeoutSec, 645 ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd, 646 int nopasswd_acct_mgmt, int flags, char ***badsrvrs, 647 ns_conn_user_t *conn_user) 648 { 649 Connection *con = NULL; 650 ConnectionID id; 651 char errmsg[MAXERROR]; 652 int rc, exit_rc = NS_LDAP_SUCCESS; 653 ns_server_info_t sinfo; 654 char *hReq, *host = NULL; 655 LDAP *ld = NULL; 656 int passwd_mgmt = 0; 657 int totalbad = 0; /* Number of servers contacted unsuccessfully */ 658 short memerr = 0; /* Variable for tracking memory allocation */ 659 char *serverAddrType = NULL, **bindHost = NULL; 660 661 662 if (conp == NULL || errorp == NULL || auth == NULL) 663 return (NS_LDAP_INVALID_PARAM); 664 *errorp = NULL; 665 *conp = NULL; 666 (void) memset(&sinfo, 0, sizeof (sinfo)); 667 668 if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) { 669 /* connection found in cache */ 670 #ifdef DEBUG 671 (void) fprintf(stderr, "tid= %d: connection found in " 672 "cache %d\n", thr_self(), id); 673 fflush(stderr); 674 #endif /* DEBUG */ 675 *cID = id; 676 *conp = con; 677 return (NS_LDAP_SUCCESS); 678 } 679 680 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) { 681 serverAddrType = NS_CACHE_ADDR_HOSTNAME; 682 bindHost = &sinfo.serverFQDN; 683 } else { 684 serverAddrType = NS_CACHE_ADDR_IP; 685 bindHost = &sinfo.server; 686 } 687 688 if (serverAddr) { 689 if (__s_api_isInitializing()) { 690 /* 691 * When obtaining the root DSE, connect to the server 692 * passed here through the serverAddr parameter 693 */ 694 sinfo.server = strdup(serverAddr); 695 if (sinfo.server == NULL) 696 return (NS_LDAP_MEMORY); 697 if (strcmp(serverAddrType, 698 NS_CACHE_ADDR_HOSTNAME) == 0) { 699 rc = __s_api_ip2hostname(sinfo.server, 700 &sinfo.serverFQDN); 701 if (rc != NS_LDAP_SUCCESS) { 702 (void) snprintf(errmsg, 703 sizeof (errmsg), 704 gettext("The %s address " 705 "can not be resolved into " 706 "a host name. Returning " 707 "the address as it is."), 708 serverAddr); 709 MKERROR(LOG_ERR, 710 *errorp, 711 NS_CONFIG_NOTLOADED, 712 strdup(errmsg), 713 NS_LDAP_MEMORY); 714 __s_api_free_server_info(&sinfo); 715 return (NS_LDAP_INTERNAL); 716 } 717 } 718 } else { 719 /* 720 * We're given the server address, just use it. 721 * In case of sasl/GSSAPI, serverAddr would need 722 * to be a FQDN. We assume this is the case for now. 723 * 724 * Only the server address fields of sinfo structure 725 * are filled in since these are the only relevant 726 * data that we have. Other fields of this structure 727 * (controls, saslMechanisms) are kept to NULL. 728 */ 729 sinfo.server = strdup(serverAddr); 730 if (sinfo.server == NULL) { 731 return (NS_LDAP_MEMORY); 732 } 733 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) { 734 sinfo.serverFQDN = strdup(serverAddr); 735 if (sinfo.serverFQDN == NULL) { 736 free(sinfo.server); 737 return (NS_LDAP_MEMORY); 738 } 739 } 740 } 741 rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp, 742 fail_if_new_pwd_reqd, passwd_mgmt, conn_user); 743 if (rc == NS_LDAP_SUCCESS || rc == 744 NS_LDAP_SUCCESS_WITH_INFO) { 745 exit_rc = rc; 746 goto create_con; 747 } else { 748 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) { 749 (void) snprintf(errmsg, sizeof (errmsg), 750 "%s %s", gettext("makeConnection: " 751 "failed to open connection using " 752 "sasl/GSSAPI to"), *bindHost); 753 } else { 754 (void) snprintf(errmsg, sizeof (errmsg), 755 "%s %s", gettext("makeConnection: " 756 "failed to open connection to"), 757 *bindHost); 758 } 759 syslog(LOG_ERR, "libsldap: %s", errmsg); 760 __s_api_free_server_info(&sinfo); 761 return (rc); 762 } 763 } 764 765 /* No cached connection, create one */ 766 for (; ; ) { 767 if (host == NULL) 768 hReq = NS_CACHE_NEW; 769 else 770 hReq = NS_CACHE_NEXT; 771 rc = __s_api_requestServer(hReq, host, &sinfo, errorp, 772 serverAddrType); 773 if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) || 774 (host && (strcasecmp(host, sinfo.server) == 0))) { 775 /* Log the error */ 776 if (*errorp) { 777 (void) snprintf(errmsg, sizeof (errmsg), 778 "%s: (%s)", gettext("makeConnection: " 779 "unable to make LDAP connection, " 780 "request for a server failed"), 781 (*errorp)->message); 782 syslog(LOG_ERR, "libsldap: %s", errmsg); 783 } 784 785 __s_api_free_server_info(&sinfo); 786 if (host) 787 free(host); 788 return (NS_LDAP_OP_FAILED); 789 } 790 if (host) 791 free(host); 792 host = strdup(sinfo.server); 793 if (host == NULL) { 794 __s_api_free_server_info(&sinfo); 795 return (NS_LDAP_MEMORY); 796 } 797 798 /* check if server supports password management */ 799 passwd_mgmt = __s_api_contain_passwd_control_oid( 800 sinfo.controls); 801 /* check if server supports password less account mgmt */ 802 if (nopasswd_acct_mgmt && 803 !__s_api_contain_account_usable_control_oid( 804 sinfo.controls)) { 805 syslog(LOG_WARNING, "libsldap: server %s does not " 806 "provide account information without password", 807 host); 808 free(host); 809 __s_api_free_server_info(&sinfo); 810 return (NS_LDAP_OP_FAILED); 811 } 812 /* make the connection */ 813 rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp, 814 fail_if_new_pwd_reqd, passwd_mgmt, conn_user); 815 /* if success, go to create connection structure */ 816 if (rc == NS_LDAP_SUCCESS || 817 rc == NS_LDAP_SUCCESS_WITH_INFO) { 818 exit_rc = rc; 819 break; 820 } 821 822 /* 823 * If not able to reach the server, inform the ldap 824 * cache manager that the server should be removed 825 * from its server list. Thus, the manager will not 826 * return this server on the next get-server request 827 * and will also reduce the server list refresh TTL, 828 * so that it will find out sooner when the server 829 * is up again. 830 */ 831 if (rc == NS_LDAP_INTERNAL && *errorp != NULL) { 832 if ((*errorp)->status == LDAP_CONNECT_ERROR || 833 (*errorp)->status == LDAP_SERVER_DOWN) { 834 /* Reset memory allocation error */ 835 memerr = 0; 836 /* 837 * We contacted a server that we could 838 * not either authenticate to or contact. 839 * If it is due to authentication, then 840 * we need to try the server again. So, 841 * do not remove the server yet, but 842 * add it to the bad server list. 843 * The caller routine will remove 844 * the servers if: 845 * a). A good server is found or 846 * b). All the possible methods 847 * are tried without finding 848 * a good server 849 */ 850 if (*badsrvrs == NULL) { 851 if (!(*badsrvrs = (char **)malloc 852 (sizeof (char *) * NUMTOMALLOC))) { 853 memerr = 1; 854 } 855 /* Allocate memory in chunks of NUMTOMALLOC */ 856 } else if ((totalbad % NUMTOMALLOC) == 857 NUMTOMALLOC - 1) { 858 char **tmpptr; 859 if (!(tmpptr = (char **)realloc( 860 *badsrvrs, 861 (sizeof (char *) * NUMTOMALLOC * 862 ((totalbad/NUMTOMALLOC) + 2))))) { 863 memerr = 1; 864 } else { 865 *badsrvrs = tmpptr; 866 } 867 } 868 /* 869 * Store host only if there were no unsuccessful 870 * memory allocations above 871 */ 872 if (!memerr && 873 !((*badsrvrs)[totalbad++] = strdup(host))) { 874 memerr = 1; 875 totalbad--; 876 } 877 (*badsrvrs)[totalbad] = NULL; 878 } 879 } 880 881 /* else, cleanup and go for the next server */ 882 __s_api_free_server_info(&sinfo); 883 884 /* Return if we had memory allocation errors */ 885 if (memerr) 886 return (NS_LDAP_MEMORY); 887 if (*errorp) { 888 /* 889 * If openConnection() failed due to 890 * password policy, or invalid credential, 891 * keep *errorp and exit 892 */ 893 if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD || 894 (*errorp)->status == LDAP_INVALID_CREDENTIALS) { 895 free(host); 896 return (rc); 897 } else { 898 (void) __ns_ldap_freeError(errorp); 899 *errorp = NULL; 900 } 901 } 902 } 903 904 create_con: 905 /* we have created ld, setup con structure */ 906 if (host) 907 free(host); 908 if ((con = calloc(1, sizeof (Connection))) == NULL) { 909 __s_api_free_server_info(&sinfo); 910 /* 911 * If password control attached in **errorp, 912 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 913 * free the error structure 914 */ 915 if (*errorp) { 916 (void) __ns_ldap_freeError(errorp); 917 *errorp = NULL; 918 } 919 (void) ldap_unbind(ld); 920 return (NS_LDAP_MEMORY); 921 } 922 923 con->serverAddr = sinfo.server; /* Store original format */ 924 if (sinfo.serverFQDN != NULL) { 925 free(sinfo.serverFQDN); 926 sinfo.serverFQDN = NULL; 927 } 928 con->saslMechanisms = sinfo.saslMechanisms; 929 con->controls = sinfo.controls; 930 931 con->auth = __ns_ldap_dupAuth(auth); 932 if (con->auth == NULL) { 933 (void) ldap_unbind(ld); 934 __s_api_freeConnection(con); 935 /* 936 * If password control attached in **errorp, 937 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 938 * free the error structure 939 */ 940 if (*errorp) { 941 (void) __ns_ldap_freeError(errorp); 942 *errorp = NULL; 943 } 944 return (NS_LDAP_MEMORY); 945 } 946 947 con->threadID = thr_self(); 948 con->pid = getpid(); 949 950 con->ld = ld; 951 /* add MT connection to the MT connection pool */ 952 if (conn_user != NULL && conn_user->conn_mt != NULL) { 953 if (__s_api_conn_mt_add(con, conn_user, errorp) == 954 NS_LDAP_SUCCESS) { 955 *conp = con; 956 return (exit_rc); 957 } else { 958 (void) ldap_unbind(ld); 959 __s_api_freeConnection(con); 960 return ((*errorp)->status); 961 } 962 } 963 964 /* MT connection not supported or not required case */ 965 if ((id = addConnection(con)) == -1) { 966 (void) ldap_unbind(ld); 967 __s_api_freeConnection(con); 968 /* 969 * If password control attached in **errorp, 970 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO, 971 * free the error structure 972 */ 973 if (*errorp) { 974 (void) __ns_ldap_freeError(errorp); 975 *errorp = NULL; 976 } 977 return (NS_LDAP_MEMORY); 978 } 979 #ifdef DEBUG 980 (void) fprintf(stderr, "tid= %d: connection added into " 981 "cache %d\n", thr_self(), id); 982 fflush(stderr); 983 #endif /* DEBUG */ 984 *cID = id; 985 *conp = con; 986 return (exit_rc); 987 } 988 989 /* 990 * Return the specified connection to the pool. If necessary 991 * delete the connection. 992 */ 993 994 static void 995 _DropConnection(ConnectionID cID, int flag, int fini) 996 { 997 Connection *cp; 998 int id; 999 int use_mutex = !fini; 1000 struct timeval zerotime; 1001 LDAPMessage *res; 1002 1003 zerotime.tv_sec = zerotime.tv_usec = 0L; 1004 1005 id = cID - CONID_OFFSET; 1006 if (id < 0 || id >= sessionPoolSize) 1007 return; 1008 #ifdef DEBUG 1009 (void) fprintf(stderr, 1010 "tid %d: Dropping connection cID=%d flag=0x%x\n", 1011 thr_self(), cID, flag); 1012 fflush(stderr); 1013 #endif /* DEBUG */ 1014 if (use_mutex) 1015 (void) mutex_lock(&sessionPoolLock); 1016 1017 cp = sessionPool[id]; 1018 /* sanity check before removing */ 1019 if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) { 1020 if (use_mutex) 1021 (void) mutex_unlock(&sessionPoolLock); 1022 return; 1023 } 1024 1025 if (!fini && 1026 ((flag & NS_LDAP_NEW_CONN) == 0) && 1027 ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc() || 1028 __s_api_peruser_proc())) { 1029 /* release Connection (keep alive) */ 1030 cp->usedBit = B_FALSE; 1031 cp->threadID = 0; /* unmark the threadID */ 1032 /* 1033 * Do sanity cleanup of remaining results. 1034 */ 1035 while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL, 1036 &zerotime, &res) > 0) { 1037 if (res != NULL) 1038 (void) ldap_msgfree(res); 1039 } 1040 if (use_mutex) 1041 (void) mutex_unlock(&sessionPoolLock); 1042 } else { 1043 /* delete Connection (disconnect) */ 1044 sessionPool[id] = NULL; 1045 if (use_mutex) 1046 (void) mutex_unlock(&sessionPoolLock); 1047 (void) ldap_unbind(cp->ld); 1048 __s_api_freeConnection(cp); 1049 } 1050 } 1051 1052 void 1053 DropConnection(ConnectionID cID, int flag) 1054 { 1055 _DropConnection(cID, flag, 0); 1056 } 1057 1058 /* 1059 * This routine is called after a bind operation is 1060 * done in openConnection() to process the password 1061 * management information, if any. 1062 * 1063 * Input: 1064 * bind_type: "simple" or "sasl/DIGEST-MD5" 1065 * ldaprc : ldap rc from the ldap bind operation 1066 * controls : controls returned by the server 1067 * errmsg : error message from the server 1068 * fail_if_new_pwd_reqd: 1069 * flag indicating if connection should be open 1070 * when password needs to change immediately 1071 * passwd_mgmt: 1072 * flag indicating if server supports password 1073 * policy/management 1074 * 1075 * Output : ns_ldap_error structure, which may contain 1076 * password status and number of seconds until 1077 * expired 1078 * 1079 * return rc: 1080 * NS_LDAP_EXTERNAL: error, connection should not open 1081 * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached 1082 * NS_LDAP_SUCCESS: OK to open connection 1083 * 1084 */ 1085 1086 static int 1087 process_pwd_mgmt(char *bind_type, int ldaprc, 1088 LDAPControl **controls, 1089 char *errmsg, ns_ldap_error_t **errorp, 1090 int fail_if_new_pwd_reqd, 1091 int passwd_mgmt) 1092 { 1093 char errstr[MAXERROR]; 1094 LDAPControl **ctrl = NULL; 1095 int exit_rc; 1096 ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD; 1097 int sec_until_exp = 0; 1098 1099 /* 1100 * errmsg may be an empty string, 1101 * even if ldaprc is LDAP_SUCCESS, 1102 * free the empty string if that's the case 1103 */ 1104 if (errmsg && 1105 (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) { 1106 ldap_memfree(errmsg); 1107 errmsg = NULL; 1108 } 1109 1110 if (ldaprc != LDAP_SUCCESS) { 1111 /* 1112 * try to map ldap rc and error message to 1113 * a password status 1114 */ 1115 if (errmsg) { 1116 if (passwd_mgmt) 1117 pwd_status = 1118 __s_api_set_passwd_status( 1119 ldaprc, errmsg); 1120 ldap_memfree(errmsg); 1121 } 1122 1123 (void) snprintf(errstr, sizeof (errstr), 1124 gettext("openConnection: " 1125 "%s bind failed " 1126 "- %s"), bind_type, ldap_err2string(ldaprc)); 1127 1128 if (pwd_status != NS_PASSWD_GOOD) { 1129 MKERROR_PWD_MGMT(*errorp, 1130 ldaprc, strdup(errstr), 1131 pwd_status, 0, NULL); 1132 } else { 1133 MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr), 1134 NS_LDAP_MEMORY); 1135 } 1136 if (controls) 1137 ldap_controls_free(controls); 1138 1139 return (NS_LDAP_INTERNAL); 1140 } 1141 1142 /* 1143 * ldaprc is LDAP_SUCCESS, 1144 * process the password management controls, if any 1145 */ 1146 exit_rc = NS_LDAP_SUCCESS; 1147 if (controls && passwd_mgmt) { 1148 /* 1149 * The control with the OID 1150 * 2.16.840.1.113730.3.4.4 (or 1151 * LDAP_CONTROL_PWEXPIRED, as defined 1152 * in the ldap.h header file) is the 1153 * expired password control. 1154 * 1155 * This control is used if the server 1156 * is configured to require users to 1157 * change their passwords when first 1158 * logging in and whenever the 1159 * passwords are reset. 1160 * 1161 * If the user is logging in for the 1162 * first time or if the user's 1163 * password has been reset, the 1164 * server sends this control to 1165 * indicate that the client needs to 1166 * change the password immediately. 1167 * 1168 * At this point, the only operation 1169 * that the client can perform is to 1170 * change the user's password. If the 1171 * client requests any other LDAP 1172 * operation, the server sends back 1173 * an LDAP_UNWILLING_TO_PERFORM 1174 * result code with an expired 1175 * password control. 1176 * 1177 * The control with the OID 1178 * 2.16.840.1.113730.3.4.5 (or 1179 * LDAP_CONTROL_PWEXPIRING, as 1180 * defined in the ldap.h header file) 1181 * is the password expiration warning 1182 * control. 1183 * 1184 * This control is used if the server 1185 * is configured to expire user 1186 * passwords after a certain amount 1187 * of time. 1188 * 1189 * The server sends this control back 1190 * to the client if the client binds 1191 * using a password that will soon 1192 * expire. The ldctl_value field of 1193 * the LDAPControl structure 1194 * specifies the number of seconds 1195 * before the password will expire. 1196 */ 1197 for (ctrl = controls; *ctrl; ctrl++) { 1198 1199 if (strcmp((*ctrl)->ldctl_oid, 1200 LDAP_CONTROL_PWEXPIRED) == 0) { 1201 /* 1202 * if the caller wants this bind 1203 * to fail, set up the error info. 1204 * If call to this function is 1205 * for searching the LDAP directory, 1206 * e.g., __ns_ldap_list(), 1207 * there's really no sense to 1208 * let a connection open and 1209 * then fail immediately afterward 1210 * on the LDAP search operation with 1211 * the LDAP_UNWILLING_TO_PERFORM rc 1212 */ 1213 pwd_status = 1214 NS_PASSWD_CHANGE_NEEDED; 1215 if (fail_if_new_pwd_reqd) { 1216 (void) snprintf(errstr, 1217 sizeof (errstr), 1218 gettext( 1219 "openConnection: " 1220 "%s bind " 1221 "failed " 1222 "- password " 1223 "expired. It " 1224 " needs to change " 1225 "immediately!"), 1226 bind_type); 1227 MKERROR_PWD_MGMT(*errorp, 1228 LDAP_SUCCESS, 1229 strdup(errstr), 1230 pwd_status, 1231 0, 1232 NULL); 1233 exit_rc = NS_LDAP_INTERNAL; 1234 } else { 1235 MKERROR_PWD_MGMT(*errorp, 1236 LDAP_SUCCESS, 1237 NULL, 1238 pwd_status, 1239 0, 1240 NULL); 1241 exit_rc = 1242 NS_LDAP_SUCCESS_WITH_INFO; 1243 } 1244 break; 1245 } else if (strcmp((*ctrl)->ldctl_oid, 1246 LDAP_CONTROL_PWEXPIRING) == 0) { 1247 pwd_status = 1248 NS_PASSWD_ABOUT_TO_EXPIRE; 1249 if ((*ctrl)-> 1250 ldctl_value.bv_len > 0 && 1251 (*ctrl)-> 1252 ldctl_value.bv_val) 1253 sec_until_exp = 1254 atoi((*ctrl)-> 1255 ldctl_value.bv_val); 1256 MKERROR_PWD_MGMT(*errorp, 1257 LDAP_SUCCESS, 1258 NULL, 1259 pwd_status, 1260 sec_until_exp, 1261 NULL); 1262 exit_rc = 1263 NS_LDAP_SUCCESS_WITH_INFO; 1264 break; 1265 } 1266 } 1267 } 1268 1269 if (controls) 1270 ldap_controls_free(controls); 1271 1272 return (exit_rc); 1273 } 1274 1275 static int 1276 ldap_in_nss_switch(char *db) 1277 { 1278 enum __nsw_parse_err pserr; 1279 struct __nsw_switchconfig *conf; 1280 struct __nsw_lookup *lkp; 1281 const char *name; 1282 int found = 0; 1283 1284 conf = __nsw_getconfig(db, &pserr); 1285 if (conf == NULL) { 1286 return (-1); 1287 } 1288 1289 /* check for skip and count other backends */ 1290 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { 1291 name = lkp->service_name; 1292 if (strcmp(name, "ldap") == 0) { 1293 found = 1; 1294 break; 1295 } 1296 } 1297 __nsw_freeconfig(conf); 1298 return (found); 1299 } 1300 1301 static int 1302 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth, 1303 int timeoutSec, ns_ldap_error_t **errorp, 1304 int fail_if_new_pwd_reqd, int passwd_mgmt, 1305 ns_conn_user_t *conn_user) 1306 { 1307 LDAP *ld = NULL; 1308 int ldapVersion = LDAP_VERSION3; 1309 int derefOption = LDAP_DEREF_ALWAYS; 1310 int zero = 0; 1311 int timeoutMilliSec = timeoutSec * 1000; 1312 uint16_t port = USE_DEFAULT_PORT; 1313 char *s; 1314 char errstr[MAXERROR]; 1315 1316 ns_ldap_return_code ret_code = NS_LDAP_SUCCESS; 1317 1318 *errorp = NULL; 1319 *ldp = NULL; 1320 1321 /* determine if the host name contains a port number */ 1322 s = strchr(serverAddr, ']'); /* skip over ipv6 addr */ 1323 s = strchr(s != NULL ? s : serverAddr, ':'); 1324 if (s != NULL) { 1325 if (sscanf(s + 1, "%hu", &port) != 1) { 1326 (void) snprintf(errstr, 1327 sizeof (errstr), 1328 gettext("openConnection: cannot " 1329 "convert %s into a valid " 1330 "port number for the " 1331 "%s server. A default value " 1332 "will be used."), 1333 s, 1334 serverAddr); 1335 syslog(LOG_ERR, "libsldap: %s", errstr); 1336 } else { 1337 *s = '\0'; 1338 } 1339 } 1340 1341 ret_code = createSession(auth, 1342 serverAddr, 1343 port, 1344 timeoutMilliSec, 1345 &ld, 1346 errorp); 1347 if (s != NULL) { 1348 *s = ':'; 1349 } 1350 if (ret_code != NS_LDAP_SUCCESS) { 1351 return (ret_code); 1352 } 1353 1354 /* check to see if the underlying libsldap supports MT connection */ 1355 if (conn_user != NULL) { 1356 int rc; 1357 1358 rc = __s_api_check_libldap_MT_conn_support(conn_user, ld, 1359 errorp); 1360 if (rc != NS_LDAP_SUCCESS) { 1361 (void) ldap_unbind(ld); 1362 return (rc); 1363 } 1364 } 1365 1366 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); 1367 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 1368 /* 1369 * set LDAP_OPT_REFERRALS to OFF. 1370 * This library will handle the referral itself 1371 * based on API flags or configuration file 1372 * specification. If this option is not set 1373 * to OFF, libldap will never pass the 1374 * referral info up to this library 1375 */ 1376 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1377 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 1378 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 1379 /* setup TCP/IP connect timeout */ 1380 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 1381 &timeoutMilliSec); 1382 /* retry if LDAP I/O was interrupted */ 1383 (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 1384 1385 ret_code = performBind(auth, 1386 ld, 1387 timeoutSec, 1388 errorp, 1389 fail_if_new_pwd_reqd, 1390 passwd_mgmt); 1391 1392 if (ret_code == NS_LDAP_SUCCESS || 1393 ret_code == NS_LDAP_SUCCESS_WITH_INFO) { 1394 *ldp = ld; 1395 } 1396 1397 return (ret_code); 1398 } 1399 1400 /* 1401 * FUNCTION: __s_api_getDefaultAuth 1402 * 1403 * Constructs a credential for authentication using the config module. 1404 * 1405 * RETURN VALUES: 1406 * 1407 * NS_LDAP_SUCCESS If successful 1408 * NS_LDAP_CONFIG If there are any config errors. 1409 * NS_LDAP_MEMORY Memory errors. 1410 * NS_LDAP_OP_FAILED If there are no more authentication methods so can 1411 * not build a new authp. 1412 * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the 1413 * necessary fields of a cred for a given auth method 1414 * are not provided. 1415 * INPUT: 1416 * 1417 * cLevel Currently requested credential level to be tried 1418 * 1419 * aMethod Currently requested authentication method to be tried 1420 * 1421 * OUTPUT: 1422 * 1423 * authp authentication method to use. 1424 */ 1425 static int 1426 __s_api_getDefaultAuth( 1427 int *cLevel, 1428 ns_auth_t *aMethod, 1429 ns_cred_t **authp) 1430 { 1431 void **paramVal = NULL; 1432 char *modparamVal = NULL; 1433 int getUid = 0; 1434 int getPasswd = 0; 1435 int getCertpath = 0; 1436 int rc = 0; 1437 ns_ldap_error_t *errorp = NULL; 1438 1439 #ifdef DEBUG 1440 (void) fprintf(stderr, "__s_api_getDefaultAuth START\n"); 1441 #endif 1442 1443 if (aMethod == NULL) { 1444 /* Require an Auth */ 1445 return (NS_LDAP_INVALID_PARAM); 1446 1447 } 1448 /* 1449 * credential level "self" can work with auth method sasl/GSSAPI only 1450 */ 1451 if (cLevel && *cLevel == NS_LDAP_CRED_SELF && 1452 aMethod->saslmech != NS_LDAP_SASL_GSSAPI) 1453 return (NS_LDAP_INVALID_PARAM); 1454 1455 *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); 1456 if ((*authp) == NULL) 1457 return (NS_LDAP_MEMORY); 1458 1459 (*authp)->auth = *aMethod; 1460 1461 switch (aMethod->type) { 1462 case NS_LDAP_AUTH_NONE: 1463 return (NS_LDAP_SUCCESS); 1464 case NS_LDAP_AUTH_SIMPLE: 1465 getUid++; 1466 getPasswd++; 1467 break; 1468 case NS_LDAP_AUTH_SASL: 1469 if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 1470 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) { 1471 getUid++; 1472 getPasswd++; 1473 } else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) { 1474 (void) __ns_ldap_freeCred(authp); 1475 *authp = NULL; 1476 return (NS_LDAP_INVALID_PARAM); 1477 } 1478 break; 1479 case NS_LDAP_AUTH_TLS: 1480 if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) || 1481 ((aMethod->tlstype == NS_LDAP_TLS_SASL) && 1482 ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) || 1483 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) { 1484 getUid++; 1485 getPasswd++; 1486 getCertpath++; 1487 } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) { 1488 getCertpath++; 1489 } else { 1490 (void) __ns_ldap_freeCred(authp); 1491 *authp = NULL; 1492 return (NS_LDAP_INVALID_PARAM); 1493 } 1494 break; 1495 } 1496 1497 if (getUid) { 1498 paramVal = NULL; 1499 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P, 1500 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1501 (void) __ns_ldap_freeCred(authp); 1502 (void) __ns_ldap_freeError(&errorp); 1503 *authp = NULL; 1504 return (rc); 1505 } 1506 1507 if (paramVal == NULL || *paramVal == NULL) { 1508 (void) __ns_ldap_freeCred(authp); 1509 *authp = NULL; 1510 return (NS_LDAP_INVALID_PARAM); 1511 } 1512 1513 (*authp)->cred.unix_cred.userID = strdup((char *)*paramVal); 1514 (void) __ns_ldap_freeParam(¶mVal); 1515 if ((*authp)->cred.unix_cred.userID == NULL) { 1516 (void) __ns_ldap_freeCred(authp); 1517 *authp = NULL; 1518 return (NS_LDAP_MEMORY); 1519 } 1520 } 1521 if (getPasswd) { 1522 paramVal = NULL; 1523 if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P, 1524 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1525 (void) __ns_ldap_freeCred(authp); 1526 (void) __ns_ldap_freeError(&errorp); 1527 *authp = NULL; 1528 return (rc); 1529 } 1530 1531 if (paramVal == NULL || *paramVal == NULL) { 1532 (void) __ns_ldap_freeCred(authp); 1533 *authp = NULL; 1534 return (NS_LDAP_INVALID_PARAM); 1535 } 1536 1537 modparamVal = dvalue((char *)*paramVal); 1538 (void) __ns_ldap_freeParam(¶mVal); 1539 if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) { 1540 (void) __ns_ldap_freeCred(authp); 1541 if (modparamVal != NULL) 1542 free(modparamVal); 1543 *authp = NULL; 1544 return (NS_LDAP_INVALID_PARAM); 1545 } 1546 1547 (*authp)->cred.unix_cred.passwd = modparamVal; 1548 } 1549 if (getCertpath) { 1550 paramVal = NULL; 1551 if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P, 1552 ¶mVal, &errorp)) != NS_LDAP_SUCCESS) { 1553 (void) __ns_ldap_freeCred(authp); 1554 (void) __ns_ldap_freeError(&errorp); 1555 *authp = NULL; 1556 return (rc); 1557 } 1558 1559 if (paramVal == NULL || *paramVal == NULL) { 1560 (void) __ns_ldap_freeCred(authp); 1561 *authp = NULL; 1562 return (NS_LDAP_INVALID_PARAM); 1563 } 1564 1565 (*authp)->hostcertpath = strdup((char *)*paramVal); 1566 (void) __ns_ldap_freeParam(¶mVal); 1567 if ((*authp)->hostcertpath == NULL) { 1568 (void) __ns_ldap_freeCred(authp); 1569 *authp = NULL; 1570 return (NS_LDAP_MEMORY); 1571 } 1572 } 1573 return (NS_LDAP_SUCCESS); 1574 } 1575 1576 /* 1577 * FUNCTION: getConnection 1578 * 1579 * internal version of __s_api_getConnection() 1580 */ 1581 static int 1582 getConnection( 1583 const char *server, 1584 const int flags, 1585 const ns_cred_t *cred, /* credentials for bind */ 1586 ConnectionID *sessionId, 1587 Connection **session, 1588 ns_ldap_error_t **errorp, 1589 int fail_if_new_pwd_reqd, 1590 int nopasswd_acct_mgmt, 1591 ns_conn_user_t *conn_user) 1592 { 1593 char errmsg[MAXERROR]; 1594 ns_auth_t **aMethod = NULL; 1595 ns_auth_t **aNext = NULL; 1596 int **cLevel = NULL; 1597 int **cNext = NULL; 1598 int timeoutSec = NS_DEFAULT_BIND_TIMEOUT; 1599 int rc; 1600 Connection *con = NULL; 1601 int sec = 1; 1602 ns_cred_t *authp = NULL; 1603 ns_cred_t anon; 1604 int version = NS_LDAP_V2, self_gssapi_only = 0; 1605 void **paramVal = NULL; 1606 char **badSrvrs = NULL; /* List of problem hostnames */ 1607 1608 if ((session == NULL) || (sessionId == NULL)) { 1609 return (NS_LDAP_INVALID_PARAM); 1610 } 1611 *session = NULL; 1612 1613 /* reuse MT connection if needed and if available */ 1614 if (conn_user != NULL) { 1615 rc = __s_api_conn_mt_get(server, flags, cred, session, errorp, 1616 conn_user); 1617 if (rc != NS_LDAP_NOTFOUND) 1618 return (rc); 1619 } 1620 1621 /* get profile version number */ 1622 if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, 1623 ¶mVal, errorp)) != NS_LDAP_SUCCESS) 1624 return (rc); 1625 if (paramVal == NULL) { 1626 (void) sprintf(errmsg, gettext("getConnection: no file " 1627 "version")); 1628 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg), 1629 NS_LDAP_CONFIG); 1630 return (NS_LDAP_CONFIG); 1631 } 1632 if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0) 1633 version = NS_LDAP_V1; 1634 (void) __ns_ldap_freeParam((void ***)¶mVal); 1635 1636 /* Get the bind timeout value */ 1637 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, ¶mVal, errorp); 1638 if (paramVal != NULL && *paramVal != NULL) { 1639 timeoutSec = **((int **)paramVal); 1640 (void) __ns_ldap_freeParam(¶mVal); 1641 } 1642 if (*errorp) 1643 (void) __ns_ldap_freeError(errorp); 1644 1645 if (cred == NULL) { 1646 /* Get the authentication method list */ 1647 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, 1648 (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS) 1649 return (rc); 1650 if (aMethod == NULL) { 1651 aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *)); 1652 if (aMethod == NULL) 1653 return (NS_LDAP_MEMORY); 1654 aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t)); 1655 if (aMethod[0] == NULL) { 1656 free(aMethod); 1657 return (NS_LDAP_MEMORY); 1658 } 1659 if (version == NS_LDAP_V1) 1660 (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE; 1661 else { 1662 (aMethod[0])->type = NS_LDAP_AUTH_SASL; 1663 (aMethod[0])->saslmech = 1664 NS_LDAP_SASL_DIGEST_MD5; 1665 (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE; 1666 } 1667 } 1668 1669 /* Get the credential level list */ 1670 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P, 1671 (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) { 1672 (void) __ns_ldap_freeParam((void ***)&aMethod); 1673 return (rc); 1674 } 1675 if (cLevel == NULL) { 1676 cLevel = (int **)calloc(2, sizeof (int *)); 1677 if (cLevel == NULL) 1678 return (NS_LDAP_MEMORY); 1679 cLevel[0] = (int *)calloc(1, sizeof (int)); 1680 if (cLevel[0] == NULL) 1681 return (NS_LDAP_MEMORY); 1682 if (version == NS_LDAP_V1) 1683 *(cLevel[0]) = NS_LDAP_CRED_PROXY; 1684 else 1685 *(cLevel[0]) = NS_LDAP_CRED_ANON; 1686 } 1687 } 1688 1689 /* setup the anon credential for anonymous connection */ 1690 (void) memset(&anon, 0, sizeof (ns_cred_t)); 1691 anon.auth.type = NS_LDAP_AUTH_NONE; 1692 1693 for (;;) { 1694 if (cred != NULL) { 1695 /* using specified auth method */ 1696 rc = makeConnection(&con, server, cred, 1697 sessionId, timeoutSec, errorp, 1698 fail_if_new_pwd_reqd, 1699 nopasswd_acct_mgmt, flags, &badSrvrs, conn_user); 1700 /* not using bad server if credentials were supplied */ 1701 if (badSrvrs && *badSrvrs) { 1702 __s_api_free2dArray(badSrvrs); 1703 badSrvrs = NULL; 1704 } 1705 if (rc == NS_LDAP_SUCCESS || 1706 rc == NS_LDAP_SUCCESS_WITH_INFO) { 1707 *session = con; 1708 break; 1709 } 1710 } else { 1711 self_gssapi_only = __s_api_self_gssapi_only_get(); 1712 /* for every cred level */ 1713 for (cNext = cLevel; *cNext != NULL; cNext++) { 1714 if (self_gssapi_only && 1715 **cNext != NS_LDAP_CRED_SELF) 1716 continue; 1717 if (**cNext == NS_LDAP_CRED_ANON) { 1718 /* 1719 * make connection anonymously 1720 * Free the down server list before 1721 * looping through 1722 */ 1723 if (badSrvrs && *badSrvrs) { 1724 __s_api_free2dArray(badSrvrs); 1725 badSrvrs = NULL; 1726 } 1727 rc = makeConnection(&con, server, &anon, 1728 sessionId, timeoutSec, errorp, 1729 fail_if_new_pwd_reqd, 1730 nopasswd_acct_mgmt, flags, 1731 &badSrvrs, conn_user); 1732 if (rc == NS_LDAP_SUCCESS || 1733 rc == 1734 NS_LDAP_SUCCESS_WITH_INFO) { 1735 *session = con; 1736 goto done; 1737 } 1738 continue; 1739 } 1740 /* for each cred level */ 1741 for (aNext = aMethod; *aNext != NULL; aNext++) { 1742 if (self_gssapi_only && 1743 (*aNext)->saslmech != 1744 NS_LDAP_SASL_GSSAPI) 1745 continue; 1746 /* 1747 * self coexists with sasl/GSSAPI only 1748 * and non-self coexists with non-gssapi 1749 * only 1750 */ 1751 if ((**cNext == NS_LDAP_CRED_SELF && 1752 (*aNext)->saslmech != 1753 NS_LDAP_SASL_GSSAPI) || 1754 (**cNext != NS_LDAP_CRED_SELF && 1755 (*aNext)->saslmech == 1756 NS_LDAP_SASL_GSSAPI)) 1757 continue; 1758 /* make connection and authenticate */ 1759 /* with default credentials */ 1760 authp = NULL; 1761 rc = __s_api_getDefaultAuth(*cNext, 1762 *aNext, &authp); 1763 if (rc != NS_LDAP_SUCCESS) { 1764 continue; 1765 } 1766 /* 1767 * Free the down server list before 1768 * looping through 1769 */ 1770 if (badSrvrs && *badSrvrs) { 1771 __s_api_free2dArray(badSrvrs); 1772 badSrvrs = NULL; 1773 } 1774 rc = makeConnection(&con, server, authp, 1775 sessionId, timeoutSec, errorp, 1776 fail_if_new_pwd_reqd, 1777 nopasswd_acct_mgmt, flags, 1778 &badSrvrs, conn_user); 1779 (void) __ns_ldap_freeCred(&authp); 1780 if (rc == NS_LDAP_SUCCESS || 1781 rc == 1782 NS_LDAP_SUCCESS_WITH_INFO) { 1783 *session = con; 1784 goto done; 1785 } 1786 } 1787 } 1788 } 1789 if (flags & NS_LDAP_HARD) { 1790 if (sec < LDAPMAXHARDLOOKUPTIME) 1791 sec *= 2; 1792 (void) sleep(sec); 1793 } else { 1794 break; 1795 } 1796 } 1797 1798 done: 1799 if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) { 1800 /* 1801 * self_gssapi_only is true but no self/sasl/gssapi is 1802 * configured 1803 */ 1804 rc = NS_LDAP_CONFIG; 1805 } 1806 1807 (void) __ns_ldap_freeParam((void ***)&aMethod); 1808 (void) __ns_ldap_freeParam((void ***)&cLevel); 1809 1810 if (badSrvrs && *badSrvrs) { 1811 /* 1812 * At this point, either we have a successful 1813 * connection or exhausted all the possible auths. 1814 * and creds. Mark the problem servers as down 1815 * so that the problem servers are not contacted 1816 * again until the refresh_ttl expires. 1817 */ 1818 (void) __s_api_removeBadServers(badSrvrs); 1819 __s_api_free2dArray(badSrvrs); 1820 } 1821 return (rc); 1822 } 1823 1824 /* 1825 * FUNCTION: __s_api_getConnection 1826 * 1827 * Bind to the specified server or one from the server 1828 * list and return the pointer. 1829 * 1830 * This function can rebind or not (NS_LDAP_HARD), it can require a 1831 * credential or bind anonymously 1832 * 1833 * This function follows the DUA configuration schema algorithm 1834 * 1835 * RETURN VALUES: 1836 * 1837 * NS_LDAP_SUCCESS A connection was made successfully. 1838 * NS_LDAP_SUCCESS_WITH_INFO 1839 * A connection was made successfully, but with 1840 * password management info in *errorp 1841 * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function. 1842 * NS_LDAP_CONFIG If there are any config errors. 1843 * NS_LDAP_MEMORY Memory errors. 1844 * NS_LDAP_INTERNAL If there was a ldap error. 1845 * 1846 * INPUT: 1847 * 1848 * server Bind to this LDAP server only 1849 * flags If NS_LDAP_HARD is set function will not return until it has 1850 * a connection unless there is a authentication problem. 1851 * If NS_LDAP_NEW_CONN is set the function must force a new 1852 * connection to be created 1853 * If NS_LDAP_KEEP_CONN is set the connection is to be kept open 1854 * auth Credentials for bind. This could be NULL in which case 1855 * a default cred built from the config module is used. 1856 * sessionId cookie that points to a previous session 1857 * fail_if_new_pwd_reqd 1858 * a flag indicating this function should fail if the passwd 1859 * in auth needs to change immediately 1860 * nopasswd_acct_mgmt 1861 * a flag indicating that makeConnection should check before 1862 * binding if server supports LDAP V3 password less 1863 * account management 1864 * 1865 * OUTPUT: 1866 * 1867 * session pointer to a session with connection information 1868 * errorp Set if there are any INTERNAL, or CONFIG error. 1869 */ 1870 int 1871 __s_api_getConnection( 1872 const char *server, 1873 const int flags, 1874 const ns_cred_t *cred, /* credentials for bind */ 1875 ConnectionID *sessionId, 1876 Connection **session, 1877 ns_ldap_error_t **errorp, 1878 int fail_if_new_pwd_reqd, 1879 int nopasswd_acct_mgmt, 1880 ns_conn_user_t *conn_user) 1881 { 1882 int rc; 1883 1884 rc = getConnection(server, flags, cred, sessionId, session, 1885 errorp, fail_if_new_pwd_reqd, nopasswd_acct_mgmt, 1886 conn_user); 1887 1888 if (rc != NS_LDAP_SUCCESS && rc != NS_LDAP_SUCCESS_WITH_INFO) { 1889 if (conn_user != NULL && conn_user->conn_mt != NULL) 1890 __s_api_conn_mt_remove(conn_user, rc, errorp); 1891 } 1892 1893 return (rc); 1894 } 1895 1896 void 1897 __s_api_free_sessionPool() 1898 { 1899 int id; 1900 1901 (void) mutex_lock(&sessionPoolLock); 1902 1903 if (sessionPool != NULL) { 1904 for (id = 0; id < sessionPoolSize; id++) 1905 _DropConnection(id + CONID_OFFSET, 0, 1); 1906 free(sessionPool); 1907 sessionPool = NULL; 1908 sessionPoolSize = 0; 1909 } 1910 (void) mutex_unlock(&sessionPoolLock); 1911 } 1912 1913 /* 1914 * This function initializes a TLS LDAP session. On success LDAP* is returned 1915 * (pointed by *ldp). Otherwise, the function returns an NS error code and 1916 * provide an additional info pointed by *errorp. 1917 */ 1918 static 1919 ns_ldap_return_code 1920 createTLSSession(const ns_cred_t *auth, const char *serverAddr, 1921 uint16_t port, int timeoutMilliSec, 1922 LDAP **ldp, ns_ldap_error_t **errorp) 1923 { 1924 const char *hostcertpath; 1925 char *alloc_hcp = NULL, errstr[MAXERROR]; 1926 int ldap_rc; 1927 1928 #ifdef DEBUG 1929 (void) fprintf(stderr, "tid= %d: +++TLS transport\n", 1930 thr_self()); 1931 #endif /* DEBUG */ 1932 1933 if (prldap_set_session_option(NULL, NULL, 1934 PRLDAP_OPT_IO_MAX_TIMEOUT, 1935 timeoutMilliSec) != LDAP_SUCCESS) { 1936 (void) snprintf(errstr, sizeof (errstr), 1937 gettext("createTLSSession: failed to initialize " 1938 "TLS security")); 1939 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1940 strdup(errstr), NS_LDAP_MEMORY); 1941 return (NS_LDAP_INTERNAL); 1942 } 1943 1944 hostcertpath = auth->hostcertpath; 1945 if (hostcertpath == NULL) { 1946 alloc_hcp = __s_get_hostcertpath(); 1947 hostcertpath = alloc_hcp; 1948 } 1949 1950 if (hostcertpath == NULL) 1951 return (NS_LDAP_MEMORY); 1952 1953 if ((ldap_rc = ldapssl_client_init(hostcertpath, NULL)) < 0) { 1954 if (alloc_hcp != NULL) { 1955 free(alloc_hcp); 1956 } 1957 (void) snprintf(errstr, sizeof (errstr), 1958 gettext("createTLSSession: failed to initialize " 1959 "TLS security (%s)"), 1960 ldapssl_err2string(ldap_rc)); 1961 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1962 strdup(errstr), NS_LDAP_MEMORY); 1963 return (NS_LDAP_INTERNAL); 1964 } 1965 if (alloc_hcp) 1966 free(alloc_hcp); 1967 1968 *ldp = ldapssl_init(serverAddr, port, 1); 1969 1970 if (*ldp == NULL || 1971 ldapssl_install_gethostbyaddr(*ldp, "ldap") != 0) { 1972 (void) snprintf(errstr, sizeof (errstr), 1973 gettext("createTLSSession: failed to connect " 1974 "using TLS (%s)"), strerror(errno)); 1975 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 1976 strdup(errstr), NS_LDAP_MEMORY); 1977 return (NS_LDAP_INTERNAL); 1978 } 1979 1980 return (NS_LDAP_SUCCESS); 1981 } 1982 1983 /* 1984 * Convert (resolve) hostname to IP address. 1985 * 1986 * INPUT: 1987 * 1988 * server - \[IPv6_address\][:port] 1989 * - IPv4_address[:port] 1990 * - hostname[:port] 1991 * 1992 * newaddr - Buffer to which this function writes resulting address, 1993 * including the port number, if specified in server argument. 1994 * 1995 * newaddr_size - Size of the newaddr buffer. 1996 * 1997 * errstr - Buffer to which error string is written if error occurs. 1998 * 1999 * errstr_size - Size of the errstr buffer. 2000 * 2001 * OUTPUT: 2002 * 2003 * Returns 1 for success, 0 in case of error. 2004 * 2005 * newaddr - See above (INPUT section). 2006 * 2007 * errstr - See above (INPUT section). 2008 */ 2009 static int 2010 cvt_hostname2ip(char *server, char *newaddr, int newaddr_size, 2011 char *errstr, int errstr_size) 2012 { 2013 char *s; 2014 unsigned short port = 0; 2015 int err; 2016 char buffer[NSS_BUFLEN_HOSTS]; 2017 struct hostent result; 2018 2019 /* Determine if the host name contains a port number. */ 2020 2021 /* Skip over IPv6 address. */ 2022 s = strchr(server, ']'); 2023 s = strchr(s != NULL ? s : server, ':'); 2024 if (s != NULL) { 2025 if (sscanf(s + 1, "%hu", &port) != 1) { 2026 /* Address misformatted. No port number after : */ 2027 (void) snprintf(errstr, errstr_size, "%s", 2028 gettext("Invalid host:port format")); 2029 return (0); 2030 } else 2031 /* Cut off the :<port> part. */ 2032 *s = '\0'; 2033 } 2034 2035 buffer[0] = '\0'; 2036 /* 2037 * Resolve hostname and fill in hostent structure. 2038 */ 2039 if (!__s_api_hostname2ip(server, &result, buffer, NSS_BUFLEN_HOSTS, 2040 &err)) { 2041 /* 2042 * The only possible error here could be TRY_AGAIN if buffer was 2043 * not big enough. NSS_BUFLEN_HOSTS should have been enough 2044 * though. 2045 */ 2046 (void) snprintf(errstr, errstr_size, "%s", 2047 gettext("Unable to resolve address.")); 2048 return (0); 2049 } 2050 2051 2052 buffer[0] = '\0'; 2053 /* 2054 * Convert the address to string. 2055 */ 2056 if (!inet_ntop(result.h_addrtype, result.h_addr_list[0], buffer, 2057 NSS_BUFLEN_HOSTS)) { 2058 /* There's not much we can do. */ 2059 (void) snprintf(errstr, errstr_size, "%s", 2060 gettext("Unable to convert address to string.")); 2061 return (0); 2062 } 2063 2064 /* Put together the address and the port */ 2065 if (port > 0) { 2066 switch (result.h_addrtype) { 2067 case AF_INET6: 2068 (void) snprintf(newaddr, 2069 /* [IP]:<port>\0 */ 2070 1 + strlen(buffer) + 1 + 1 + 5 + 1, 2071 "[%s]:%hu", 2072 buffer, 2073 port); 2074 break; 2075 /* AF_INET */ 2076 default : 2077 (void) snprintf(newaddr, 2078 /* IP:<port>\0 */ 2079 strlen(buffer) + 1 + 5 + 1, 2080 "%s:%hu", 2081 buffer, 2082 port); 2083 break; 2084 } 2085 } else { 2086 (void) strncpy(newaddr, buffer, newaddr_size); 2087 } 2088 2089 return (1); 2090 } 2091 2092 2093 /* 2094 * This finction initializes a none-TLS LDAP session. On success LDAP* 2095 * is returned (pointed by *ldp). Otherwise, the function returns 2096 * an NS error code and provides an additional info pointed by *errorp. 2097 */ 2098 static 2099 ns_ldap_return_code 2100 createNonTLSSession(const char *serverAddr, 2101 uint16_t port, int gssapi, 2102 LDAP **ldp, ns_ldap_error_t **errorp) 2103 { 2104 char errstr[MAXERROR]; 2105 char *addr; 2106 int is_ip = 0; 2107 /* [INET6_ADDRSTRLEN]:<port>\0 */ 2108 char svraddr[1+INET6_ADDRSTRLEN+1+1+5+1]; 2109 #ifdef DEBUG 2110 (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n", 2111 thr_self()); 2112 #endif /* DEBUG */ 2113 2114 if (gssapi == 0) { 2115 is_ip = (__s_api_isipv4((char *)serverAddr) || 2116 __s_api_isipv6((char *)serverAddr)); 2117 } 2118 2119 /* 2120 * Let's try to resolve IP address of server. 2121 */ 2122 if (is_ip == 0 && !gssapi && (ldap_in_nss_switch((char *)"hosts") > 0 || 2123 ldap_in_nss_switch((char *)"ipnodes") > 0)) { 2124 addr = strdup(serverAddr); 2125 if (addr == NULL) 2126 return (NS_LDAP_MEMORY); 2127 svraddr[0] = '\0'; 2128 if (cvt_hostname2ip(addr, svraddr, sizeof (svraddr), 2129 errstr, MAXERROR) == 1) { 2130 serverAddr = svraddr; 2131 free(addr); 2132 } else { 2133 free(addr); 2134 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 2135 strdup(errstr), NS_LDAP_MEMORY); 2136 return (NS_LDAP_INTERNAL); 2137 } 2138 } 2139 2140 /* Warning message IF cannot connect to host(s) */ 2141 if ((*ldp = ldap_init((char *)serverAddr, port)) == NULL) { 2142 char *p = strerror(errno); 2143 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR, 2144 strdup(p), NS_LDAP_MEMORY); 2145 return (NS_LDAP_INTERNAL); 2146 } 2147 2148 return (NS_LDAP_SUCCESS); 2149 } 2150 2151 /* 2152 * This finction initializes an LDAP session. 2153 * 2154 * INPUT: 2155 * auth - a structure specified an authenticastion method and credentials, 2156 * serverAddr - the address of a server to which a connection 2157 * will be established, 2158 * port - a port being listened by the server, 2159 * timeoutMilliSec - a timeout in milliseconds for the Bind operation. 2160 * 2161 * OUTPUT: 2162 * ldp - a pointer to an LDAP structure which will be used 2163 * for all the subsequent operations against the server. 2164 * If an error occurs, the function returns an NS error code 2165 * and provides an additional info pointed by *errorp. 2166 */ 2167 static 2168 ns_ldap_return_code 2169 createSession(const ns_cred_t *auth, const char *serverAddr, 2170 uint16_t port, int timeoutMilliSec, 2171 LDAP **ldp, ns_ldap_error_t **errorp) 2172 { 2173 int useSSL = 0, gssapi = 0; 2174 char errstr[MAXERROR]; 2175 2176 switch (auth->auth.type) { 2177 case NS_LDAP_AUTH_NONE: 2178 case NS_LDAP_AUTH_SIMPLE: 2179 case NS_LDAP_AUTH_SASL: 2180 break; 2181 case NS_LDAP_AUTH_TLS: 2182 useSSL = 1; 2183 break; 2184 default: 2185 (void) sprintf(errstr, 2186 gettext("openConnection: unsupported " 2187 "authentication method (%d)"), auth->auth.type); 2188 MKERROR(LOG_WARNING, *errorp, 2189 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 2190 NS_LDAP_MEMORY); 2191 return (NS_LDAP_INTERNAL); 2192 } 2193 2194 if (port == USE_DEFAULT_PORT) { 2195 port = useSSL ? LDAPS_PORT : LDAP_PORT; 2196 } 2197 2198 if (auth->auth.type == NS_LDAP_AUTH_SASL && 2199 auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) 2200 gssapi = 1; 2201 2202 if (useSSL) 2203 return (createTLSSession(auth, serverAddr, port, 2204 timeoutMilliSec, ldp, errorp)); 2205 else 2206 return (createNonTLSSession(serverAddr, port, gssapi, 2207 ldp, errorp)); 2208 } 2209 2210 /* 2211 * This finction performs a non-SASL bind operation. If an error accures, 2212 * the function returns an NS error code and provides an additional info 2213 * pointed by *errorp. 2214 */ 2215 static 2216 ns_ldap_return_code 2217 doSimpleBind(const ns_cred_t *auth, 2218 LDAP *ld, 2219 int timeoutSec, 2220 ns_ldap_error_t **errorp, 2221 int fail_if_new_pwd_reqd, 2222 int passwd_mgmt) 2223 { 2224 char *binddn, *passwd, errstr[MAXERROR], *errmsg; 2225 int msgId, errnum = 0, ldap_rc; 2226 ns_ldap_return_code ret_code; 2227 LDAPMessage *resultMsg = NULL; 2228 LDAPControl **controls; 2229 struct timeval tv; 2230 2231 binddn = auth->cred.unix_cred.userID; 2232 passwd = auth->cred.unix_cred.passwd; 2233 if (passwd == NULL || *passwd == '\0' || 2234 binddn == NULL || *binddn == '\0') { 2235 (void) sprintf(errstr, gettext("openConnection: " 2236 "missing credentials for Simple bind")); 2237 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS, 2238 strdup(errstr), NS_LDAP_MEMORY); 2239 (void) ldap_unbind(ld); 2240 return (NS_LDAP_INTERNAL); 2241 } 2242 2243 #ifdef DEBUG 2244 (void) fprintf(stderr, "tid= %d: +++Simple bind\n", 2245 thr_self()); 2246 #endif /* DEBUG */ 2247 msgId = ldap_simple_bind(ld, binddn, passwd); 2248 2249 if (msgId == -1) { 2250 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 2251 (void *)&errnum); 2252 (void) snprintf(errstr, sizeof (errstr), 2253 gettext("openConnection: simple bind failed " 2254 "- %s"), ldap_err2string(errnum)); 2255 (void) ldap_unbind(ld); 2256 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 2257 NS_LDAP_MEMORY); 2258 return (NS_LDAP_INTERNAL); 2259 } 2260 2261 tv.tv_sec = timeoutSec; 2262 tv.tv_usec = 0; 2263 ldap_rc = ldap_result(ld, msgId, 0, &tv, &resultMsg); 2264 2265 if ((ldap_rc == -1) || (ldap_rc == 0)) { 2266 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 2267 (void *)&errnum); 2268 (void) snprintf(errstr, sizeof (errstr), 2269 gettext("openConnection: simple bind failed " 2270 "- %s"), ldap_err2string(errnum)); 2271 (void) ldap_msgfree(resultMsg); 2272 (void) ldap_unbind(ld); 2273 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr), 2274 NS_LDAP_MEMORY); 2275 return (NS_LDAP_INTERNAL); 2276 } 2277 2278 /* 2279 * get ldaprc, controls, and error msg 2280 */ 2281 ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 2282 &errmsg, NULL, &controls, 1); 2283 2284 if (ldap_rc != LDAP_SUCCESS) { 2285 (void) snprintf(errstr, sizeof (errstr), 2286 gettext("openConnection: simple bind failed " 2287 "- unable to parse result")); 2288 (void) ldap_unbind(ld); 2289 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2290 strdup(errstr), NS_LDAP_MEMORY); 2291 return (NS_LDAP_INTERNAL); 2292 } 2293 2294 /* process the password management info, if any */ 2295 ret_code = process_pwd_mgmt("simple", 2296 errnum, controls, errmsg, 2297 errorp, 2298 fail_if_new_pwd_reqd, 2299 passwd_mgmt); 2300 2301 if (ret_code == NS_LDAP_INTERNAL) { 2302 (void) ldap_unbind(ld); 2303 } 2304 2305 return (ret_code); 2306 } 2307 2308 /* 2309 * This finction performs a SASL bind operation. If an error accures, 2310 * the function returns an NS error code and provides an additional info 2311 * pointed by *errorp. 2312 */ 2313 static 2314 ns_ldap_return_code 2315 doSASLBind(const ns_cred_t *auth, 2316 LDAP *ld, 2317 int timeoutSec, 2318 ns_ldap_error_t **errorp, 2319 int fail_if_new_pwd_reqd, 2320 int passwd_mgmt) 2321 { 2322 char *binddn, *passwd, *digest_md5_name, 2323 errstr[MAXERROR], *errmsg; 2324 struct berval cred; 2325 int ldap_rc, errnum = 0; 2326 ns_ldap_return_code ret_code; 2327 struct timeval tv; 2328 LDAPMessage *resultMsg; 2329 LDAPControl **controls; 2330 int min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF; 2331 ns_sasl_cb_param_t sasl_param; 2332 2333 if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE && 2334 auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) { 2335 (void) sprintf(errstr, 2336 gettext("openConnection: SASL options are " 2337 "not supported (%d) for non-GSSAPI sasl bind"), 2338 auth->auth.saslopt); 2339 MKERROR(LOG_WARNING, *errorp, 2340 LDAP_AUTH_METHOD_NOT_SUPPORTED, 2341 strdup(errstr), NS_LDAP_MEMORY); 2342 (void) ldap_unbind(ld); 2343 return (NS_LDAP_INTERNAL); 2344 } 2345 if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) { 2346 binddn = auth->cred.unix_cred.userID; 2347 passwd = auth->cred.unix_cred.passwd; 2348 if (passwd == NULL || *passwd == '\0' || 2349 binddn == NULL || *binddn == '\0') { 2350 (void) sprintf(errstr, 2351 gettext("openConnection: missing credentials " 2352 "for SASL bind")); 2353 MKERROR(LOG_WARNING, *errorp, 2354 LDAP_INVALID_CREDENTIALS, 2355 strdup(errstr), NS_LDAP_MEMORY); 2356 (void) ldap_unbind(ld); 2357 return (NS_LDAP_INTERNAL); 2358 } 2359 cred.bv_val = passwd; 2360 cred.bv_len = strlen(passwd); 2361 } 2362 2363 ret_code = NS_LDAP_SUCCESS; 2364 2365 switch (auth->auth.saslmech) { 2366 case NS_LDAP_SASL_CRAM_MD5: 2367 /* 2368 * NOTE: if iDS changes to support cram_md5, 2369 * please add password management code here. 2370 * Since ldap_sasl_cram_md5_bind_s does not 2371 * return anything that could be used to 2372 * extract the ldap rc/errmsg/control to 2373 * determine if bind failed due to password 2374 * policy, a new cram_md5_bind API will need 2375 * to be introduced. See 2376 * ldap_x_sasl_digest_md5_bind() and case 2377 * NS_LDAP_SASL_DIGEST_MD5 below for details. 2378 */ 2379 if ((ldap_rc = ldap_sasl_cram_md5_bind_s(ld, binddn, 2380 &cred, NULL, NULL)) != LDAP_SUCCESS) { 2381 (void) ldap_get_option(ld, 2382 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 2383 (void) snprintf(errstr, sizeof (errstr), 2384 gettext("openConnection: " 2385 "sasl/CRAM-MD5 bind failed - %s"), 2386 ldap_err2string(errnum)); 2387 MKERROR(LOG_WARNING, *errorp, errnum, 2388 strdup(errstr), NS_LDAP_MEMORY); 2389 (void) ldap_unbind(ld); 2390 return (NS_LDAP_INTERNAL); 2391 } 2392 break; 2393 case NS_LDAP_SASL_DIGEST_MD5: 2394 digest_md5_name = malloc(strlen(binddn) + 5); 2395 /* 5 = strlen("dn: ") + 1 */ 2396 if (digest_md5_name == NULL) { 2397 (void) ldap_unbind(ld); 2398 return (NS_LDAP_MEMORY); 2399 } 2400 (void) strcpy(digest_md5_name, "dn: "); 2401 (void) strcat(digest_md5_name, binddn); 2402 2403 tv.tv_sec = timeoutSec; 2404 tv.tv_usec = 0; 2405 ldap_rc = ldap_x_sasl_digest_md5_bind(ld, 2406 digest_md5_name, &cred, NULL, NULL, 2407 &tv, &resultMsg); 2408 2409 if (resultMsg == NULL) { 2410 free(digest_md5_name); 2411 (void) ldap_get_option(ld, 2412 LDAP_OPT_ERROR_NUMBER, (void *)&errnum); 2413 (void) snprintf(errstr, sizeof (errstr), 2414 gettext("openConnection: " 2415 "DIGEST-MD5 bind failed - %s"), 2416 ldap_err2string(errnum)); 2417 (void) ldap_unbind(ld); 2418 MKERROR(LOG_WARNING, *errorp, errnum, 2419 strdup(errstr), NS_LDAP_MEMORY); 2420 return (NS_LDAP_INTERNAL); 2421 } 2422 2423 /* 2424 * get ldaprc, controls, and error msg 2425 */ 2426 ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL, 2427 &errmsg, NULL, &controls, 1); 2428 2429 if (ldap_rc != LDAP_SUCCESS) { 2430 free(digest_md5_name); 2431 (void) snprintf(errstr, sizeof (errstr), 2432 gettext("openConnection: " 2433 "DIGEST-MD5 bind failed " 2434 "- unable to parse result")); 2435 (void) ldap_unbind(ld); 2436 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2437 strdup(errstr), NS_LDAP_MEMORY); 2438 return (NS_LDAP_INTERNAL); 2439 } 2440 2441 /* process the password management info, if any */ 2442 ret_code = process_pwd_mgmt("sasl/DIGEST-MD5", 2443 errnum, controls, errmsg, 2444 errorp, 2445 fail_if_new_pwd_reqd, 2446 passwd_mgmt); 2447 2448 if (ret_code == NS_LDAP_INTERNAL) { 2449 (void) ldap_unbind(ld); 2450 } 2451 2452 free(digest_md5_name); 2453 break; 2454 case NS_LDAP_SASL_GSSAPI: 2455 if (sasl_gssapi_inited == 0) { 2456 ret_code = __s_api_sasl_gssapi_init(); 2457 if (ret_code != NS_LDAP_SUCCESS) { 2458 (void) snprintf(errstr, sizeof (errstr), 2459 gettext("openConnection: " 2460 "GSSAPI initialization " 2461 "failed")); 2462 (void) ldap_unbind(ld); 2463 MKERROR(LOG_WARNING, *errorp, ret_code, 2464 strdup(errstr), NS_LDAP_MEMORY); 2465 return (ret_code); 2466 } 2467 } 2468 (void) memset(&sasl_param, 0, 2469 sizeof (ns_sasl_cb_param_t)); 2470 sasl_param.authid = NULL; 2471 sasl_param.authzid = ""; 2472 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN, 2473 (void *)&min_ssf); 2474 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX, 2475 (void *)&max_ssf); 2476 2477 ldap_rc = ldap_sasl_interactive_bind_s( 2478 ld, NULL, "GSSAPI", 2479 NULL, NULL, LDAP_SASL_INTERACTIVE, 2480 __s_api_sasl_bind_callback, 2481 &sasl_param); 2482 2483 if (ldap_rc != LDAP_SUCCESS) { 2484 (void) snprintf(errstr, sizeof (errstr), 2485 gettext("openConnection: " 2486 "GSSAPI bind failed " 2487 "- %d %s"), 2488 ldap_rc, 2489 ldap_err2string(ldap_rc)); 2490 (void) ldap_unbind(ld); 2491 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, 2492 strdup(errstr), NS_LDAP_MEMORY); 2493 return (NS_LDAP_INTERNAL); 2494 } 2495 2496 break; 2497 default: 2498 (void) ldap_unbind(ld); 2499 (void) sprintf(errstr, 2500 gettext("openConnection: unsupported SASL " 2501 "mechanism (%d)"), auth->auth.saslmech); 2502 MKERROR(LOG_WARNING, *errorp, 2503 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr), 2504 NS_LDAP_MEMORY); 2505 return (NS_LDAP_INTERNAL); 2506 } 2507 2508 return (ret_code); 2509 } 2510 2511 /* 2512 * This function performs an LDAP Bind operation proceeding 2513 * from a type of the connection specified by auth->auth.type. 2514 * 2515 * INPUT: 2516 * auth - a structure specified an authenticastion method and credentials, 2517 * ld - a pointer returned by the createSession() function, 2518 * timeoutSec - a timeout in seconds for the Bind operation, 2519 * fail_if_new_pwd_reqd - a flag indicating that the call should fail 2520 * if a new password is required, 2521 * passwd_mgmt - a flag indicating that the server supports 2522 * password management. 2523 * 2524 * OUTPUT: 2525 * If an error accures, the function returns an NS error code 2526 * and provides an additional info pointed by *errorp. 2527 */ 2528 static 2529 ns_ldap_return_code 2530 performBind(const ns_cred_t *auth, 2531 LDAP *ld, 2532 int timeoutSec, 2533 ns_ldap_error_t **errorp, 2534 int fail_if_new_pwd_reqd, 2535 int passwd_mgmt) 2536 { 2537 int bindType; 2538 char errstr[MAXERROR]; 2539 2540 ns_ldap_return_code (*binder)(const ns_cred_t *auth, 2541 LDAP *ld, 2542 int timeoutSec, 2543 ns_ldap_error_t **errorp, 2544 int fail_if_new_pwd_reqd, 2545 int passwd_mgmt) = NULL; 2546 2547 if (!ld) { 2548 (void) sprintf(errstr, 2549 "performBind: LDAP session " 2550 "is not initialized."); 2551 MKERROR(LOG_WARNING, *errorp, 2552 LDAP_AUTH_METHOD_NOT_SUPPORTED, 2553 strdup(errstr), NS_LDAP_MEMORY); 2554 return (NS_LDAP_INTERNAL); 2555 } 2556 2557 bindType = auth->auth.type == NS_LDAP_AUTH_TLS ? 2558 auth->auth.tlstype : auth->auth.type; 2559 2560 switch (bindType) { 2561 case NS_LDAP_AUTH_NONE: 2562 #ifdef DEBUG 2563 (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n", 2564 thr_self()); 2565 #endif /* DEBUG */ 2566 break; 2567 case NS_LDAP_AUTH_SIMPLE: 2568 binder = doSimpleBind; 2569 break; 2570 case NS_LDAP_AUTH_SASL: 2571 binder = doSASLBind; 2572 break; 2573 default: 2574 (void) sprintf(errstr, 2575 gettext("openConnection: unsupported " 2576 "authentication method " 2577 "(%d)"), bindType); 2578 MKERROR(LOG_WARNING, *errorp, 2579 LDAP_AUTH_METHOD_NOT_SUPPORTED, 2580 strdup(errstr), NS_LDAP_MEMORY); 2581 (void) ldap_unbind(ld); 2582 return (NS_LDAP_INTERNAL); 2583 } 2584 2585 if (binder != NULL) { 2586 return (*binder)(auth, 2587 ld, 2588 timeoutSec, 2589 errorp, 2590 fail_if_new_pwd_reqd, 2591 passwd_mgmt); 2592 } 2593 2594 return (NS_LDAP_SUCCESS); 2595 } 2596