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