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