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