1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  * Copyright 2012 Milan Jurik. All rights reserved.
  25  */
  26 
  27 #define __STANDALONE_MODULE__
  28 
  29 #include <stdio.h>
  30 #include <sys/types.h>
  31 #include <stdlib.h>
  32 #include <libintl.h>
  33 #include <string.h>
  34 #include <ctype.h>
  35 
  36 #include <sys/stat.h>
  37 #include <fcntl.h>
  38 #include <unistd.h>
  39 #include <syslog.h>
  40 #include <locale.h>
  41 #include <errno.h>
  42 #include <sys/time.h>
  43 
  44 #include <arpa/inet.h>
  45 #include <netdb.h>
  46 #include <strings.h>
  47 
  48 #include <thread.h>
  49 
  50 #include <nsswitch.h>
  51 #include <nss_dbdefs.h>
  52 #include <nss.h>
  53 
  54 #include "ns_cache_door.h"
  55 #include "ns_internal.h"
  56 #include "ns_connmgmt.h"
  57 
  58 typedef enum {
  59         INFO_SERVER_JUST_INITED = -1,
  60         INFO_SERVER_UNKNOWN     = 0,
  61         INFO_SERVER_CONNECTING  = 1,
  62         INFO_SERVER_UP          = 2,
  63         INFO_SERVER_ERROR       = 3,
  64         INFO_SERVER_REMOVED     = 4
  65 } dir_server_status_t;
  66 
  67 typedef enum {
  68         INFO_STATUS_NEW         = 2,
  69         INFO_STATUS_OLD         = 3
  70 } dir_server_info_t;
  71 
  72 typedef struct dir_server {
  73         char                    *ip;
  74         char                    **controls;
  75         char                    **saslMech;
  76         dir_server_status_t     status;
  77         mutex_t                 updateStatus;
  78         dir_server_info_t       info;
  79 } dir_server_t;
  80 
  81 typedef struct dir_server_list {
  82         dir_server_t    **nsServers;
  83 
  84         rwlock_t        listDestroyLock;
  85 } dir_server_list_t;
  86 
  87 struct {
  88         /* The local list of the directory servers' root DSEs. */
  89         dir_server_list_t       *list;
  90         /* The flag indicating if libsldap is in the 'Standalone' mode. */
  91         int                     standalone;
  92         /*
  93          * The mutex ensuring that only one thread performs
  94          * the initialization of the list.
  95          */
  96         mutex_t                 listReplaceLock;
  97         /*
  98          * A flag indicating that a particular thread is
  99          * in the 'ldap_cachemgr' mode. It is stored by thread as
 100          * a thread specific data.
 101          */
 102         const int               initFlag;
 103         /*
 104          * A thread specific key storing
 105          * the the 'ldap_cachemgr' mode indicator.
 106          */
 107         thread_key_t            standaloneInitKey;
 108 } dir_servers = {NULL, 0, DEFAULTMUTEX, '1'};
 109 
 110 typedef struct switchDatabase {
 111         char *conf;
 112         uint32_t alloced;
 113 } switch_database_t;
 114 
 115 static thread_key_t switchConfigKey;
 116 
 117 #pragma init(createStandaloneKey)
 118 
 119 #define DONT_INCLUDE_ATTR_NAMES 0
 120 #define INCLUDE_ATTR_NAMES      1
 121 #define IS_PROFILE              1
 122 #define NOT_PROFILE             0
 123 /* INET6_ADDRSTRLEN + ":" + <5-digit port> + some round-up */
 124 #define MAX_HOSTADDR_LEN (INET6_ADDRSTRLEN + 6 + 12)
 125 
 126 static
 127 void
 128 switch_conf_disposer(void *data)
 129 {
 130         switch_database_t *localData = (switch_database_t *)data;
 131 
 132         free(localData->conf);
 133         free(localData);
 134 }
 135 
 136 /*
 137  * This function initializes an indication that a thread obtaining a root DSE
 138  * will be switched to the 'ldap_cachemgr' mode. Within the thread libsldap
 139  * will not invoke the __s_api_requestServer function. Instead, the library
 140  * will establish a connection to the server specified by
 141  * the __ns_ldap_getRootDSE function.
 142  * Since  ldap_cachmgr can obtain a DUAProfile and root DSEs at the same time
 143  * and we do not want to affect a thread obtaining a DUAProfile,
 144  * the 'ldap_cachemgr' mode is thread private.
 145  * In addition, this function creates a key holding temporary configuration
 146  * for the "hosts" and "ipnodes" databases which is used by the "SKIPDB"
 147  * mechanism (__s_api_ip2hostname() & _s_api_hostname2ip()).
 148  */
 149 static
 150 void
 151 createStandaloneKey()
 152 {
 153         if (thr_keycreate(&dir_servers.standaloneInitKey, NULL) != 0) {
 154                 syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
 155                 "key needed for sharing ldap connections"));
 156         }
 157         if (thr_keycreate(&switchConfigKey, switch_conf_disposer) != 0) {
 158                 syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
 159                     "key containing current nsswitch configuration"));
 160         }
 161 }
 162 
 163 /*
 164  * This function sets the 'ldap_cachemgr' mode indication.
 165  */
 166 void
 167 __s_api_setInitMode()
 168 {
 169         (void) thr_setspecific(dir_servers.standaloneInitKey,
 170             (void *) &dir_servers.initFlag);
 171 }
 172 
 173 /*
 174  * This function unset the 'ldap_cachemgr' mode indication.
 175  */
 176 void
 177 __s_api_unsetInitMode()
 178 {
 179         (void) thr_setspecific(dir_servers.standaloneInitKey, NULL);
 180 }
 181 
 182 /*
 183  * This function checks if the 'ldap_cachemgr' mode indication is set.
 184  */
 185 int
 186 __s_api_isInitializing() {
 187         int *flag = NULL;
 188 
 189         (void) thr_getspecific(dir_servers.standaloneInitKey, (void **) &flag);
 190 
 191         return (flag != NULL && *flag == dir_servers.initFlag);
 192 }
 193 
 194 /*
 195  * This function checks if the process runs in the 'Standalone' mode.
 196  * In this mode libsldap will check the local, process private list of root DSEs
 197  * instead of requesting them via a door call to ldap_cachemgr.
 198  */
 199 int
 200 __s_api_isStandalone()
 201 {
 202         int     mode;
 203 
 204         (void) mutex_lock(&dir_servers.listReplaceLock);
 205         mode = dir_servers.standalone;
 206         (void) mutex_unlock(&dir_servers.listReplaceLock);
 207 
 208         return (mode);
 209 }
 210 
 211 
 212 static
 213 int
 214 remove_ldap(char *dst, char *src, int dst_buf_len)
 215 {
 216         int i = 0;
 217 
 218         if (strlen(src) >= dst_buf_len)
 219                 return (0);
 220 
 221         while (*src != '\0') {
 222                 /* Copy up to one space from source. */
 223                 if (isspace(*src)) {
 224                         dst[i++] = *src;
 225                         while (isspace(*src))
 226                                 src++;
 227                 }
 228 
 229                 /* If not "ldap", just copy. */
 230                 if (strncmp(src, "ldap", 4) != 0) {
 231                         while (!isspace(*src)) {
 232                                 dst[i++] = *src++;
 233                                 /* At the end of string? */
 234                                 if (dst[i-1] == '\0')
 235                                         return (1);
 236                         }
 237                         /* Copy up to one space from source. */
 238                         if (isspace(*src)) {
 239                                 dst[i++] = *src;
 240                                 while (isspace(*src))
 241                                         src++;
 242                         }
 243                         /* Copy also the criteria section */
 244                         if (*src == '[')
 245                                 while (*src != ']') {
 246                                         dst[i++] = *src++;
 247                                         /* Shouln't happen if format is right */
 248                                         if (dst[i-1] == '\0')
 249                                                 return (1);
 250                                 }
 251                 }
 252 
 253                 /* If next part is ldap, skip over it ... */
 254                 if (strncmp(src, "ldap", 4) == 0) {
 255                         if (isspace(*(src+4)) || *(src+4) == '\0') {
 256                                 src += 4;
 257                                 while (isspace(*src))
 258                                         src++;
 259                                 if (*src == '[') {
 260                                         while (*src++ != ']') {
 261                                                 /*
 262                                                  * See comment above about
 263                                                  * correct format.
 264                                                  */
 265                                                 if (*src == '\0') {
 266                                                         dst[i++] = '\0';
 267                                                         return (1);
 268                                                 }
 269                                         }
 270                                 }
 271                                 while (isspace(*src))
 272                                         src++;
 273                         }
 274                 }
 275                 if (*src == '\0')
 276                         dst[i++] = '\0';
 277         }
 278 
 279         return (1);
 280 }
 281 
 282 static
 283 char *
 284 get_db(const char *db_name)
 285 {
 286         char                    *ptr;
 287         switch_database_t       *hostService = NULL;
 288         FILE                    *fp = fopen(__NSW_CONFIG_FILE, "rF");
 289         char                    *linep, line[NSS_BUFSIZ];
 290 
 291         if (fp == NULL) {
 292                 syslog(LOG_WARNING, gettext("libsldap: can not read %s"),
 293                     __NSW_CONFIG_FILE);
 294                 return (NULL);
 295         }
 296 
 297         while ((linep = fgets(line, NSS_BUFSIZ, fp)) != NULL) {
 298                 while (isspace(*linep)) {
 299                         ++linep;
 300                 }
 301                 if (*linep == '#') {
 302                         continue;
 303                 }
 304                 if (strncmp(linep, db_name, strlen(db_name)) != 0) {
 305                         continue;
 306                 }
 307                 if ((linep = strchr(linep, ':')) != NULL) {
 308                         if (linep[strlen(linep) - 1] == '\n') {
 309                                 linep[strlen(linep) - 1] = '\0';
 310                         }
 311 
 312                         while (isspace(*++linep))
 313                                 ;
 314 
 315                         if ((ptr = strchr(linep, '#')) != NULL) {
 316                                 while (--ptr >= linep && isspace(*ptr))
 317                                         ;
 318                                 *(ptr + 1) = '\0';
 319                         }
 320 
 321                         if (strlen(linep) == 0) {
 322                                 continue;
 323                         }
 324                         break;
 325                 }
 326         }
 327 
 328         (void) fclose(fp);
 329 
 330         if (linep == NULL) {
 331                 syslog(LOG_WARNING,
 332                     gettext("libsldap: the %s database "
 333                     "is missing from %s"),
 334                     db_name,
 335                     __NSW_CONFIG_FILE);
 336                 return (NULL);
 337         }
 338 
 339         (void) thr_getspecific(switchConfigKey, (void **) &hostService);
 340         if (hostService == NULL) {
 341                 hostService = calloc(1, sizeof (switch_database_t));
 342                 if (hostService == NULL) {
 343                         return (NULL);
 344                 }
 345                 (void) thr_setspecific(switchConfigKey, hostService);
 346         }
 347 
 348         /*
 349          * In a long-living process threads can perform several
 350          * getXbyY requests. And the windows between those requests
 351          * can be long. The nsswitch configuration can change from time
 352          * to time. So instead of allocating/freeing memory every time
 353          * the API is called, reallocate memory only when the current
 354          * configuration for the database being used is longer than
 355          * the previous one.
 356          */
 357         if (strlen(linep) >= hostService->alloced) {
 358                 ptr = (char *)realloc((void *)hostService->conf,
 359                     strlen(linep) + 1);
 360                 if (ptr == NULL) {
 361                         free((void *)hostService->conf);
 362                         hostService->conf = NULL;
 363                         hostService->alloced = 0;
 364                         return (NULL);
 365                 }
 366                 bzero(ptr, strlen(linep) + 1);
 367                 hostService->conf = ptr;
 368                 hostService->alloced = strlen(linep) + 1;
 369         }
 370 
 371         if (remove_ldap(hostService->conf, linep, hostService->alloced))
 372                 return (hostService->conf);
 373         else
 374                 return (NULL);
 375 }
 376 
 377 static
 378 void
 379 _initf_ipnodes(nss_db_params_t *p)
 380 {
 381         char *services = get_db("ipnodes");
 382 
 383         p->name = NSS_DBNAM_IPNODES;
 384         p->flags |= NSS_USE_DEFAULT_CONFIG;
 385         p->default_config = services == NULL ? "" : services;
 386 }
 387 
 388 static
 389 void
 390 _initf_hosts(nss_db_params_t *p)
 391 {
 392         char *services = get_db("hosts");
 393 
 394         p->name = NSS_DBNAM_HOSTS;
 395         p->flags |= NSS_USE_DEFAULT_CONFIG;
 396         p->default_config = services == NULL ? "" : services;
 397 }
 398 
 399 /*
 400  * This function is an analog of the standard gethostbyaddr_r()
 401  * function with an exception that it removes the 'ldap' back-end
 402  * (if any) from the host/ipnodes nsswitch's databases and then
 403  * looks up using remaining back-ends.
 404  */
 405 static
 406 struct hostent *
 407 _filter_gethostbyaddr_r(const char *addr, int len, int type,
 408         struct hostent *result, char *buffer, int buflen,
 409         int *h_errnop)
 410 {
 411         DEFINE_NSS_DB_ROOT(db_root_hosts);
 412         DEFINE_NSS_DB_ROOT(db_root_ipnodes);
 413         nss_XbyY_args_t arg;
 414         nss_status_t    res;
 415         int             (*str2ent)();
 416         void            (*nss_initf)();
 417         nss_db_root_t   *nss_db_root;
 418         int             dbop;
 419 
 420         switch (type) {
 421         case AF_INET:
 422                 str2ent         = str2hostent;
 423                 nss_initf       = _initf_hosts;
 424                 nss_db_root     = &db_root_hosts;
 425                 dbop            = NSS_DBOP_HOSTS_BYADDR;
 426                 break;
 427         case AF_INET6:
 428                 str2ent         = str2hostent6;
 429                 nss_initf       = _initf_ipnodes;
 430                 nss_db_root     = &db_root_ipnodes;
 431                 dbop            = NSS_DBOP_IPNODES_BYADDR;
 432         default:
 433                 return (NULL);
 434         }
 435 
 436         NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
 437 
 438         arg.key.hostaddr.addr   = addr;
 439         arg.key.hostaddr.len    = len;
 440         arg.key.hostaddr.type   = type;
 441         arg.stayopen            = 0;
 442         arg.h_errno             = NETDB_SUCCESS;
 443 
 444         res = nss_search(nss_db_root, nss_initf, dbop, &arg);
 445         arg.status = res;
 446         *h_errnop = arg.h_errno;
 447         return (struct hostent *)NSS_XbyY_FINI(&arg);
 448 }
 449 
 450 /*
 451  * This routine is an analog of gethostbyaddr_r().
 452  * But in addition __s_api_hostname2ip() performs the "LDAP SKIPDB" activity
 453  * prior to querying the name services.
 454  * If the buffer is not big enough to accommodate a returning data,
 455  * NULL is returned and h_errnop is set to TRY_AGAIN.
 456  */
 457 struct hostent *
 458 __s_api_hostname2ip(const char *name,
 459         struct hostent *result, char *buffer, int buflen,
 460         int *h_errnop)
 461 {
 462         DEFINE_NSS_DB_ROOT(db_root_ipnodes);
 463         DEFINE_NSS_DB_ROOT(db_root_hosts);
 464         nss_XbyY_args_t arg;
 465         nss_status_t    res;
 466         struct in_addr  addr;
 467         struct in6_addr addr6;
 468 
 469         if (inet_pton(AF_INET, name, &addr) > 0) {
 470                 if (buflen < strlen(name) + 1 +
 471                     sizeof (char *) * 2 + /* The h_aliases member */
 472                     sizeof (struct in_addr) +
 473                     sizeof (struct in_addr *) * 2) {
 474                         *h_errnop = TRY_AGAIN;
 475                         return (NULL);
 476                 }
 477 
 478                 result->h_addrtype = AF_INET;
 479                 result->h_length = sizeof (struct in_addr);
 480                 (void) strncpy(buffer, name, buflen);
 481 
 482                 result->h_addr_list = (char **)ROUND_UP(
 483                     buffer + strlen(name) + 1,
 484                     sizeof (char *));
 485                 result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
 486                     sizeof (char *));
 487                 result->h_aliases[0] = buffer;
 488                 result->h_aliases[1] = NULL;
 489                 bcopy(&addr,
 490                     buffer + buflen - sizeof (struct in_addr),
 491                     sizeof (struct in_addr));
 492                 result->h_addr_list[0] = buffer + buflen -
 493                     sizeof (struct in_addr);
 494                 result->h_addr_list[1] = NULL;
 495                 result->h_aliases = result->h_addr_list;
 496                 result->h_name = buffer;
 497 
 498                 *h_errnop = NETDB_SUCCESS;
 499                 return (result);
 500         }
 501         if (inet_pton(AF_INET6, name, &addr6) > 0) {
 502                 if (buflen < strlen(name) + 1 +
 503                     sizeof (char *) * 2 + /* The h_aliases member */
 504                     sizeof (struct in6_addr) +
 505                     sizeof (struct in6_addr *) * 2) {
 506                         *h_errnop = TRY_AGAIN;
 507                         return (NULL);
 508                 }
 509 
 510                 result->h_addrtype = AF_INET6;
 511                 result->h_length = sizeof (struct in6_addr);
 512                 (void) strncpy(buffer, name, buflen);
 513 
 514                 result->h_addr_list = (char **)ROUND_UP(
 515                     buffer + strlen(name) + 1,
 516                     sizeof (char *));
 517                 result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
 518                     sizeof (char *));
 519                 result->h_aliases[0] = buffer;
 520                 result->h_aliases[1] = NULL;
 521                 bcopy(&addr6,
 522                     buffer + buflen - sizeof (struct in6_addr),
 523                     sizeof (struct in6_addr));
 524                 result->h_addr_list[0] = buffer + buflen -
 525                     sizeof (struct in6_addr);
 526                 result->h_addr_list[1] = NULL;
 527                 result->h_aliases = result->h_addr_list;
 528                 result->h_name = buffer;
 529 
 530                 *h_errnop = NETDB_SUCCESS;
 531                 return (result);
 532         }
 533 
 534         NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
 535 
 536         arg.key.name = name;
 537         arg.stayopen = 0;
 538         arg.h_errno = NETDB_SUCCESS;
 539 
 540         res = nss_search(&db_root_ipnodes, _initf_ipnodes,
 541             NSS_DBOP_IPNODES_BYNAME, &arg);
 542         if (res == NSS_NOTFOUND || res == NSS_UNAVAIL) {
 543                 arg.h_errno = NETDB_SUCCESS;
 544                 res = nss_search(&db_root_hosts, _initf_hosts,
 545                     NSS_DBOP_HOSTS_BYNAME, &arg);
 546         }
 547         arg.status = res;
 548         *h_errnop = arg.h_errno;
 549         return ((struct hostent *)NSS_XbyY_FINI(&arg));
 550 }
 551 
 552 /*
 553  * Convert an IP to a host name.
 554  */
 555 ns_ldap_return_code
 556 __s_api_ip2hostname(char *ipaddr, char **hostname) {
 557         struct in_addr  in;
 558         struct in6_addr in6;
 559         struct hostent  *hp = NULL, hostEnt;
 560         char            buffer[NSS_BUFLEN_HOSTS];
 561         int             buflen = NSS_BUFLEN_HOSTS;
 562         char            *start = NULL,
 563                         *end = NULL,
 564                         delim = '\0';
 565         char            *port = NULL,
 566                         *addr = NULL;
 567         int             errorNum = 0,
 568                         len = 0;
 569 
 570         if (ipaddr == NULL || hostname == NULL)
 571                 return (NS_LDAP_INVALID_PARAM);
 572         *hostname = NULL;
 573         if ((addr = strdup(ipaddr)) == NULL)
 574                 return (NS_LDAP_MEMORY);
 575 
 576         if (addr[0] == '[') {
 577                 /*
 578                  * Assume it's [ipv6]:port
 579                  * Extract ipv6 IP
 580                  */
 581                 start = &addr[1];
 582                 if ((end = strchr(addr, ']')) != NULL) {
 583                         *end = '\0';
 584                         delim = ']';
 585                         if (*(end + 1) == ':')
 586                                 /* extract port */
 587                                 port = end + 2;
 588                 } else {
 589                         free(addr);
 590                         return (NS_LDAP_INVALID_PARAM);
 591                 }
 592         } else if ((end = strchr(addr, ':')) != NULL) {
 593                 /* assume it's ipv4:port */
 594                 *end = '\0';
 595                 delim = ':';
 596                 start = addr;
 597                 port = end + 1;
 598         } else
 599                 /* No port */
 600                 start = addr;
 601 
 602 
 603         if (inet_pton(AF_INET, start, &in) == 1) {
 604                 /* IPv4 */
 605                 hp = _filter_gethostbyaddr_r((char *)&in,
 606                     sizeof (in.s_addr),
 607                     AF_INET,
 608                     &hostEnt,
 609                     buffer,
 610                     buflen,
 611                     &errorNum);
 612                 if (hp && hp->h_name) {
 613                         /* hostname + '\0' */
 614                         len = strlen(hp->h_name) + 1;
 615                         if (port)
 616                                 /* ':' + port */
 617                                 len += strlen(port) + 1;
 618                         if ((*hostname = malloc(len)) == NULL) {
 619                                 free(addr);
 620                                 return (NS_LDAP_MEMORY);
 621                         }
 622 
 623                         if (port)
 624                                 (void) snprintf(*hostname, len, "%s:%s",
 625                                                 hp->h_name, port);
 626                         else
 627                                 (void) strlcpy(*hostname, hp->h_name, len);
 628 
 629                         free(addr);
 630                         return (NS_LDAP_SUCCESS);
 631                 } else {
 632                         free(addr);
 633                         return (NS_LDAP_NOTFOUND);
 634                 }
 635         } else if (inet_pton(AF_INET6, start, &in6) == 1) {
 636                 /* IPv6 */
 637                 hp = _filter_gethostbyaddr_r((char *)&in6,
 638                     sizeof (in6.s6_addr),
 639                     AF_INET6,
 640                     &hostEnt,
 641                     buffer,
 642                     buflen,
 643                     &errorNum);
 644                 if (hp && hp->h_name) {
 645                         /* hostname + '\0' */
 646                         len = strlen(hp->h_name) + 1;
 647                         if (port)
 648                                 /* ':' + port */
 649                                 len += strlen(port) + 1;
 650                         if ((*hostname = malloc(len)) == NULL) {
 651                                 free(addr);
 652                                 return (NS_LDAP_MEMORY);
 653                         }
 654 
 655                         if (port)
 656                                 (void) snprintf(*hostname, len, "%s:%s",
 657                                                 hp->h_name, port);
 658                         else
 659                                 (void) strlcpy(*hostname, hp->h_name, len);
 660 
 661                         free(addr);
 662                         return (NS_LDAP_SUCCESS);
 663                 } else {
 664                         free(addr);
 665                         return (NS_LDAP_NOTFOUND);
 666                 }
 667         } else {
 668                 /*
 669                  * A hostname
 670                  * Return it as is
 671                  */
 672                 if (end)
 673                         *end = delim;
 674                 *hostname = addr;
 675                 return (NS_LDAP_SUCCESS);
 676         }
 677 }
 678 
 679 /*
 680  * This function obtains data returned by an LDAP search request and puts it
 681  * in a string in the ldap_cachmgr(1) door call format.
 682  *
 683  * INPUT:
 684  *     ld - a pointer to an LDAP structure used for a search operation,
 685  *     result_msg - a pointer to an LDAPMessage returned by the search,
 686  *     include_names - if set to INCLUDE_ATTR_NAMES, the output buffer will
 687  *                     contain attribute names.
 688  *                     Otherwise, only values will be return.
 689  *
 690  * OUTPUT:
 691  *      a buffer containing server info in the following format:
 692  *         [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
 693  *      Should be free'ed by the caller.
 694  */
 695 static
 696 ns_ldap_return_code
 697 convert_to_door_line(LDAP* ld,
 698                 LDAPMessage *result_msg,
 699                 int include_names,
 700                 int is_profile,
 701                 char **door_line)
 702 {
 703         uint32_t        total_length = 0, attr_len = 0, i;
 704         LDAPMessage     *e;
 705         char            *a, **vals;
 706         BerElement      *ber;
 707         int             seen_objectclass = 0, rewind = 0;
 708 
 709         if (!door_line) {
 710                 return (NS_LDAP_INVALID_PARAM);
 711         }
 712         *door_line = NULL;
 713 
 714         if ((e = ldap_first_entry(ld, result_msg)) == NULL) {
 715                 return (NS_LDAP_NOTFOUND);
 716         }
 717 
 718         /* calculate length of received data */
 719         for (a = ldap_first_attribute(ld, e, &ber);
 720             a != NULL;
 721             a = ldap_next_attribute(ld, e, ber)) {
 722 
 723                 if ((vals = ldap_get_values(ld, e, a)) != NULL) {
 724                         for (i = 0; vals[i] != NULL; i++) {
 725                                 total_length += (include_names ?
 726                                     strlen(a) : 0) +
 727                                     strlen(vals[i]) +
 728                                     strlen(DOORLINESEP) +1;
 729                         }
 730                         ldap_value_free(vals);
 731                 }
 732                 ldap_memfree(a);
 733         }
 734         if (ber != NULL) {
 735                 ber_free(ber, 0);
 736         }
 737 
 738         if (total_length == 0) {
 739                 return (NS_LDAP_NOTFOUND);
 740         }
 741 
 742         /* copy the data */
 743         /* add 1 for the last '\0' */
 744         *door_line  = (char *)malloc(total_length + 1);
 745         if (*door_line == NULL) {
 746                 return (NS_LDAP_MEMORY);
 747         }
 748 
 749         /* make it an empty string first */
 750         **door_line = '\0';
 751         a = ldap_first_attribute(ld, e, &ber);
 752         while (a != NULL) {
 753                 if (is_profile) {
 754                         /*
 755                          * If we're processing DUAConfigProfile, we need to make
 756                          * sure we put objectclass attribute first.
 757                          * __s_api_create_config_door_str depends on that.
 758                          */
 759                         if (seen_objectclass) {
 760                                 if (strcasecmp(a, "objectclass") == 0) {
 761                                         /* Skip objectclass now. */
 762                                         a = ldap_next_attribute(ld, e, ber);
 763                                         continue;
 764                                 }
 765                         } else {
 766                                 if (strcasecmp(a, "objectclass") == 0) {
 767                                         seen_objectclass = 1;
 768                                         rewind = 1;
 769                                 } else {
 770                                         /* Skip all but objectclass first. */
 771                                         a = ldap_next_attribute(ld, e, ber);
 772                                         continue;
 773                                 }
 774                         }
 775                 }
 776 
 777                 if ((vals = ldap_get_values(ld, e, a)) != NULL) {
 778                         for (i = 0; vals[i] != NULL; i++) {
 779                                 if (include_names) {
 780                                         attr_len += strlen(a);
 781                                 }
 782                                 attr_len += strlen(vals[i]) +
 783                                     strlen(DOORLINESEP) + 2;
 784                                 if (include_names) {
 785                                         (void) snprintf(*door_line +
 786                                             strlen(*door_line),
 787                                             attr_len,
 788                                             "%s=%s%s",
 789                                             a, vals[i],
 790                                             DOORLINESEP);
 791                                 } else {
 792                                         (void) snprintf(*door_line +
 793                                             strlen(*door_line),
 794                                             attr_len,
 795                                             "%s%s",
 796                                             vals[i],
 797                                             DOORLINESEP);
 798                                 }
 799                         }
 800                         ldap_value_free(vals);
 801                 }
 802                 ldap_memfree(a);
 803 
 804                 /* Rewind */
 805                 if (rewind) {
 806                         if (ber != NULL) {
 807                                 ber_free(ber, 0);
 808                         }
 809                         a = ldap_first_attribute(ld, e, &ber);
 810                         rewind = 0;
 811                 } else {
 812                         a = ldap_next_attribute(ld, e, ber);
 813                 }
 814         }
 815         if (ber != NULL) {
 816                 ber_free(ber, 0);
 817         }
 818 
 819         if (e != result_msg) {
 820                 (void) ldap_msgfree(e);
 821         }
 822 
 823         return (NS_LDAP_SUCCESS);
 824 }
 825 
 826 /*
 827  * This function looks up the base DN of a directory serving
 828  * a specified domain name.
 829  *
 830  * INPUT:
 831  *     ld - a pointer to an LDAP structure used for the search operation,
 832  *     domain_name - the name of a domain.
 833  *
 834  * OUTPUT:
 835  *     a buffer containing a directory's base DN found.
 836  *     Should be free'ed by the caller.
 837  */
 838 static
 839 ns_ldap_return_code
 840 getDirBaseDN(LDAP *ld, const char *domain_name, char **dir_base_dn)
 841 {
 842         struct timeval          tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
 843         char                    *attrs[2], *DNlist, *rest, *ptr;
 844         char                    filter[BUFSIZ], *a = NULL;
 845         int                     ldap_rc;
 846         LDAPMessage             *resultMsg = NULL;
 847         ns_ldap_return_code     ret_code;
 848 
 849         /* Get the whole list of naming contexts residing on the server */
 850         attrs[0] = "namingcontexts";
 851         attrs[1] = NULL;
 852         ldap_rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
 853             attrs, 0, NULL, NULL, &tv, 0, &resultMsg);
 854         switch (ldap_rc) {
 855                 /* If successful, the root DSE was found. */
 856                 case LDAP_SUCCESS:
 857                         break;
 858                 /*
 859                  * If the root DSE was not found, the server does
 860                  * not comply with the LDAP v3 protocol.
 861                  */
 862                 default:
 863                         if (resultMsg) {
 864                                 (void) ldap_msgfree(resultMsg);
 865                                 resultMsg = NULL;
 866                         }
 867 
 868                         return (NS_LDAP_OP_FAILED);
 869         }
 870 
 871         if ((ret_code = convert_to_door_line(ld,
 872             resultMsg,
 873             DONT_INCLUDE_ATTR_NAMES,
 874             NOT_PROFILE,
 875             &DNlist)) != NS_LDAP_SUCCESS) {
 876                 if (resultMsg) {
 877                         (void) ldap_msgfree(resultMsg);
 878                         resultMsg = NULL;
 879                 }
 880                 return (ret_code);
 881         }
 882 
 883         if (resultMsg) {
 884                 (void) ldap_msgfree(resultMsg);
 885                 resultMsg = NULL;
 886         }
 887 
 888         if (DNlist == NULL ||
 889             (ptr = strtok_r(DNlist, DOORLINESEP, &rest)) == NULL) {
 890                 return (NS_LDAP_NOTFOUND);
 891         }
 892         attrs[0] = "dn";
 893         do {
 894                 /*
 895                  * For each context try to find a NIS domain object
 896                  * which 'nisdomain' attribute's value matches the domain name
 897                  */
 898                 (void) snprintf(filter,
 899                     BUFSIZ,
 900                     "(&(objectclass=nisDomainObject)"
 901                     "(nisdomain=%s))",
 902                     domain_name);
 903                 ldap_rc = ldap_search_ext_s(ld,
 904                     ptr,
 905                     LDAP_SCOPE_SUBTREE,
 906                     filter,
 907                     attrs,
 908                     0,
 909                     NULL,
 910                     NULL,
 911                     &tv,
 912                     0,
 913                     &resultMsg);
 914                 if (ldap_rc != LDAP_SUCCESS) {
 915                         if (resultMsg) {
 916                                 (void) ldap_msgfree(resultMsg);
 917                                 resultMsg = NULL;
 918                         }
 919                         continue;
 920                 }
 921                 if ((a = ldap_get_dn(ld, resultMsg)) != NULL) {
 922                         *dir_base_dn = strdup(a);
 923                         ldap_memfree(a);
 924 
 925                         if (resultMsg) {
 926                                 (void) ldap_msgfree(resultMsg);
 927                                 resultMsg = NULL;
 928                         }
 929 
 930                         if (!*dir_base_dn) {
 931                                 free(DNlist);
 932                                 return (NS_LDAP_MEMORY);
 933                         }
 934                         break;
 935                 }
 936 
 937                 if (resultMsg) {
 938                         (void) ldap_msgfree(resultMsg);
 939                         resultMsg = NULL;
 940                 }
 941         } while (ptr = strtok_r(NULL, DOORLINESEP, &rest));
 942 
 943         free(DNlist);
 944 
 945         if (!*dir_base_dn) {
 946                 return (NS_LDAP_NOTFOUND);
 947         }
 948 
 949         return (NS_LDAP_SUCCESS);
 950 }
 951 
 952 /*
 953  * This function parses the results of a search operation
 954  * requesting a DUAProfile.
 955  *
 956  * INPUT:
 957  *     ld - a pointer to an LDAP structure used for the search operation,
 958  *     dir_base_dn - the name of a directory's base DN,
 959  *     profile_name - the name of a DUAProfile to be obtained.
 960  *
 961  * OUTPUT:
 962  *      a buffer containing the DUAProfile in the following format:
 963  *        [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
 964  *      Should be free'ed by the caller.
 965  */
 966 static
 967 ns_ldap_return_code
 968 getDUAProfile(LDAP *ld,
 969                 const char *dir_base_dn,
 970                 const char *profile_name,
 971                 char **profile)
 972 {
 973         char                    searchBaseDN[BUFSIZ], filter[BUFSIZ];
 974         LDAPMessage             *resultMsg = NULL;
 975         struct timeval          tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
 976         int                     ldap_rc;
 977         ns_ldap_return_code     ret_code;
 978 
 979         (void) snprintf(searchBaseDN, BUFSIZ, "ou=profile,%s", dir_base_dn);
 980         (void) snprintf(filter,
 981             BUFSIZ,
 982             _PROFILE_FILTER,
 983             _PROFILE1_OBJECTCLASS,
 984             _PROFILE2_OBJECTCLASS,
 985             profile_name);
 986         ldap_rc = ldap_search_ext_s(ld,
 987             searchBaseDN,
 988             LDAP_SCOPE_SUBTREE,
 989             filter,
 990             NULL,
 991             0,
 992             NULL,
 993             NULL,
 994             &tv,
 995             0,
 996             &resultMsg);
 997 
 998         switch (ldap_rc) {
 999                 /* If successful, the DUA profile was found. */
1000                 case LDAP_SUCCESS:
1001                         break;
1002                 /*
1003                  * If the root DSE was not found, the server does
1004                  * not comply with the LDAP v3 protocol.
1005                  */
1006                 default:
1007                         if (resultMsg) {
1008                                 (void) ldap_msgfree(resultMsg);
1009                                 resultMsg = NULL;
1010                         }
1011 
1012                         return (NS_LDAP_OP_FAILED);
1013         }
1014 
1015         ret_code = convert_to_door_line(ld,
1016             resultMsg,
1017             INCLUDE_ATTR_NAMES,
1018             IS_PROFILE,
1019             profile);
1020         if (resultMsg) {
1021                 (void) ldap_msgfree(resultMsg);
1022                 resultMsg = NULL;
1023         }
1024         return (ret_code);
1025 }
1026 
1027 /*
1028  * This function derives the directory's base DN from a provided domain name.
1029  *
1030  * INPUT:
1031  *     domain_name - the name of a domain to be converted into a base DN,
1032  *     buffer - contains the derived base DN,
1033  *     buf_len - the length of the buffer.
1034  *
1035  * OUTPUT:
1036  *     The function returns the address of the buffer or NULL.
1037  */
1038 static
1039 char *
1040 domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len)
1041 {
1042         char            *nextDC, *chr;
1043         uint16_t        i, length;
1044 
1045         if (!domain_name || !buffer || buf_len == 0) {
1046                 return (NULL);
1047         }
1048 
1049         buffer[0] = '\0';
1050         nextDC = chr = domain_name;
1051         length = strlen(domain_name);
1052         for (i = 0; i < length + 1; ++i, ++chr) {
1053                 /* Simply replace dots with "dc=" */
1054                 if (*chr != '.' && *chr != '\0') {
1055                         continue;
1056                 }
1057                 *chr = '\0';
1058                 if (strlcat(buffer, "dc=", buf_len) >= buf_len)
1059                         return (NULL);
1060                 if (strlcat(buffer, nextDC, buf_len) >= buf_len)
1061                         return (NULL);
1062                 if (i < length) {
1063                         /*
1064                          * The end of the domain name
1065                          * has not been reached yet
1066                          */
1067                         if (strlcat(buffer, ",", buf_len) >= buf_len)
1068                                 return (NULL);
1069                         nextDC = chr + 1;
1070                         *chr = '.';
1071                 }
1072         }
1073 
1074         return (buffer);
1075 }
1076 
1077 /*
1078  * This function obtains the directory's base DN and a DUAProfile
1079  * from a specified server.
1080  *
1081  * INPUT:
1082  *     server - a structure describing a server to connect to and
1083  *              a DUAProfile to be obtained from the server,
1084  *     cred - credentials to be used during establishing connections to
1085  *            the server.
1086  *
1087  * OUTPUT:
1088  *     dua_profile - a buffer containing the DUAProfile in the following format:
1089  *        [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
1090  *     dir_base_dn - a buffer containing the base DN,
1091  *     errorp - an error object describing an error, if any.
1092  *
1093  *     All the output data structures should be free'ed by the caller.
1094  */
1095 ns_ldap_return_code
1096 __ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server,
1097         const ns_cred_t *cred,
1098         char **dua_profile,
1099         char **dir_base_dn,
1100         ns_ldap_error_t **errorp)
1101 {
1102         char                    serverAddr[MAX_HOSTADDR_LEN];
1103         char                    *dirBaseDN = NULL, *duaProfile = NULL;
1104         ns_cred_t               default_cred;
1105         ns_ldap_return_code     ret_code;
1106 
1107         ns_config_t             *config_struct = __s_api_create_config();
1108         ConnectionID            sessionId = 0;
1109         Connection              *session = NULL;
1110         char                    errmsg[MAXERROR];
1111         char                    buffer[NSS_BUFLEN_HOSTS];
1112         ns_conn_user_t          *cu = NULL;
1113 
1114         if (errorp == NULL) {
1115                 __s_api_destroy_config(config_struct);
1116                 return (NS_LDAP_INVALID_PARAM);
1117         }
1118 
1119         *errorp = NULL;
1120 
1121         if (server == NULL) {
1122                 __s_api_destroy_config(config_struct);
1123                 return (NS_LDAP_INVALID_PARAM);
1124         }
1125 
1126         if (config_struct == NULL) {
1127                 return (NS_LDAP_MEMORY);
1128         }
1129 
1130         /*
1131          * If no credentials are specified, try to establish a connection
1132          * as anonymous.
1133          */
1134         if (!cred) {
1135                 default_cred.cred.unix_cred.passwd = NULL;
1136                 default_cred.cred.unix_cred.userID = NULL;
1137                 default_cred.auth.type = NS_LDAP_AUTH_NONE;
1138         }
1139 
1140         /* Now create a default LDAP configuration */
1141 
1142         (void) strncpy(buffer, server->server, sizeof (buffer));
1143         if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer,
1144             errorp) != NS_LDAP_SUCCESS) {
1145                 __s_api_destroy_config(config_struct);
1146                 return (NS_LDAP_CONFIG);
1147         }
1148 
1149         /* Put together the address and the port specified by the user app. */
1150         if (server->port > 0) {
1151                 (void) snprintf(serverAddr,
1152                     sizeof (serverAddr),
1153                     "%s:%hu",
1154                     buffer,
1155                     server->port);
1156         } else {
1157                 (void) strncpy(serverAddr, buffer, sizeof (serverAddr));
1158         }
1159 
1160         /*
1161          * There is no default value for the 'Default Search Base DN' attribute.
1162          * Derive one from the domain name to make __s_api_crosscheck() happy.
1163          */
1164         if (domainname2baseDN(server->domainName ?
1165             server->domainName : config_struct->domainName,
1166             buffer, NSS_BUFLEN_HOSTS) == NULL) {
1167                 (void) snprintf(errmsg,
1168                     sizeof (errmsg),
1169                     gettext("Can not convert %s into a base DN name"),
1170                     server->domainName ?
1171                     server->domainName : config_struct->domainName);
1172                 MKERROR(LOG_ERR,
1173                     *errorp,
1174                     NS_LDAP_INTERNAL,
1175                     strdup(errmsg),
1176                     NS_LDAP_MEMORY);
1177                 __s_api_destroy_config(config_struct);
1178                 return (NS_LDAP_INTERNAL);
1179         }
1180         if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P,
1181             buffer, errorp) != NS_LDAP_SUCCESS) {
1182                 __s_api_destroy_config(config_struct);
1183                 return (NS_LDAP_CONFIG);
1184         }
1185 
1186         if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) {
1187                 __s_api_destroy_config(config_struct);
1188                 return (NS_LDAP_CONFIG);
1189         }
1190 
1191         __s_api_init_config(config_struct);
1192 
1193         __s_api_setInitMode();
1194 
1195         cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE);
1196         if (cu == NULL) {
1197                 return (NS_LDAP_INTERNAL);
1198         }
1199 
1200         if ((ret_code = __s_api_getConnection(serverAddr,
1201             NS_LDAP_NEW_CONN,
1202             cred ? cred : &default_cred,
1203             &sessionId,
1204             &session,
1205             errorp,
1206             0,
1207             0,
1208             cu)) != NS_LDAP_SUCCESS) {
1209                 __s_api_conn_user_free(cu);
1210                 __s_api_unsetInitMode();
1211                 return (ret_code);
1212         }
1213 
1214         __s_api_unsetInitMode();
1215 
1216         if ((ret_code = getDirBaseDN(session->ld,
1217             server->domainName ?
1218             server->domainName :
1219             config_struct->domainName,
1220             &dirBaseDN)) != NS_LDAP_SUCCESS) {
1221                 (void) snprintf(errmsg,
1222                     sizeof (errmsg),
1223                     gettext("Can not find the "
1224                     "nisDomainObject for domain %s\n"),
1225                     server->domainName ?
1226                     server->domainName : config_struct->domainName);
1227                 MKERROR(LOG_ERR,
1228                     *errorp,
1229                     ret_code,
1230                     strdup(errmsg),
1231                     NS_LDAP_MEMORY);
1232                 __s_api_conn_user_free(cu);
1233                 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1234                 return (ret_code);
1235         }
1236 
1237         /*
1238          * And here obtain a DUAProfile which will be used
1239          * as a real configuration.
1240          */
1241         if ((ret_code = getDUAProfile(session->ld,
1242             dirBaseDN,
1243             server->profileName ?
1244             server->profileName : "default",
1245             &duaProfile)) != NS_LDAP_SUCCESS) {
1246                 (void) snprintf(errmsg,
1247                     sizeof (errmsg),
1248                     gettext("Can not find the "
1249                     "%s DUAProfile\n"),
1250                     server->profileName ?
1251                     server->profileName : "default");
1252                 MKERROR(LOG_ERR,
1253                     *errorp,
1254                     ret_code,
1255                     strdup(errmsg),
1256                     NS_LDAP_MEMORY);
1257                 __s_api_conn_user_free(cu);
1258                 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1259                 return (ret_code);
1260         }
1261 
1262         if (dir_base_dn) {
1263                 *dir_base_dn = dirBaseDN;
1264         } else {
1265                 free(dirBaseDN);
1266         }
1267 
1268         if (dua_profile) {
1269                 *dua_profile = duaProfile;
1270         } else {
1271                 free(duaProfile);
1272         }
1273 
1274         __s_api_conn_user_free(cu);
1275         DropConnection(sessionId, NS_LDAP_NEW_CONN);
1276 
1277         return (NS_LDAP_SUCCESS);
1278 }
1279 
1280 /*
1281  * This function obtains the root DSE from a specified server.
1282  *
1283  * INPUT:
1284  *     server_addr - an adress of a server to be connected to.
1285  *
1286  * OUTPUT:
1287  *     root_dse - a buffer containing the root DSE in the following format:
1288  *          [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
1289  *        For example: ( here | used as DOORLINESEP for visual purposes)
1290  *          supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL
1291  *        Should be free'ed by the caller.
1292  */
1293 ns_ldap_return_code
1294 __ns_ldap_getRootDSE(const char *server_addr,
1295                 char **root_dse,
1296                 ns_ldap_error_t **errorp,
1297                 int anon_fallback)
1298 {
1299         char                    errmsg[MAXERROR];
1300         ns_ldap_return_code     ret_code;
1301 
1302         ConnectionID            sessionId = 0;
1303         Connection              *session = NULL;
1304 
1305         struct timeval          tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
1306         char                    *attrs[3];
1307         int                     ldap_rc, ldaperrno = 0;
1308         LDAPMessage             *resultMsg = NULL;
1309         void                    **paramVal = NULL;
1310 
1311         ns_cred_t               anon;
1312         ns_conn_user_t          *cu = NULL;
1313 
1314         if (errorp == NULL) {
1315                 return (NS_LDAP_INVALID_PARAM);
1316         }
1317 
1318         *errorp = NULL;
1319 
1320         if (!root_dse) {
1321                 return (NS_LDAP_INVALID_PARAM);
1322         }
1323 
1324         if (!server_addr) {
1325                 return (NS_LDAP_INVALID_PARAM);
1326         }
1327 
1328         __s_api_setInitMode();
1329 
1330         cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE);
1331         if (cu == NULL) {
1332                 return (NS_LDAP_INTERNAL);
1333         }
1334 
1335         /*
1336          * All the credentials will be taken from the current
1337          * libsldap configuration.
1338          */
1339         if ((ret_code = __s_api_getConnection(server_addr,
1340             NS_LDAP_NEW_CONN,
1341             NULL,
1342             &sessionId,
1343             &session,
1344             errorp,
1345             0,
1346             0,
1347             cu)) != NS_LDAP_SUCCESS) {
1348                 /* Fallback to anonymous mode is disabled. Stop. */
1349                 if (anon_fallback == 0) {
1350                         syslog(LOG_WARNING,
1351                             gettext("libsldap: can not get the root DSE from "
1352                             " the %s server: %s. "
1353                             "Falling back to anonymous disabled.\n"),
1354                             server_addr,
1355                             errorp && *errorp && (*errorp)->message ?
1356                             (*errorp)->message : "");
1357                         if (errorp != NULL && *errorp != NULL) {
1358                                 (void) __ns_ldap_freeError(errorp);
1359                         }
1360                         __s_api_unsetInitMode();
1361                         return (ret_code);
1362                 }
1363 
1364                 /*
1365                  * Fallback to anonymous, non-SSL mode for backward
1366                  * compatibility reasons. This mode should only be used when
1367                  * this function (__ns_ldap_getRootDSE) is called from
1368                  * ldap_cachemgr(1M).
1369                  */
1370                 syslog(LOG_WARNING,
1371                     gettext("libsldap: Falling back to anonymous, non-SSL"
1372                     " mode for __ns_ldap_getRootDSE. %s\n"),
1373                     errorp && *errorp && (*errorp)->message ?
1374                     (*errorp)->message : "");
1375 
1376                 /* Setup the anon credential for anonymous connection. */
1377                 (void) memset(&anon, 0, sizeof (ns_cred_t));
1378                 anon.auth.type = NS_LDAP_AUTH_NONE;
1379 
1380                 if (*errorp != NULL) {
1381                         (void) __ns_ldap_freeError(errorp);
1382                 }
1383                 *errorp = NULL;
1384 
1385                 ret_code = __s_api_getConnection(server_addr,
1386                     NS_LDAP_NEW_CONN,
1387                     &anon,
1388                     &sessionId,
1389                     &session,
1390                     errorp,
1391                     0,
1392                     0,
1393                     cu);
1394 
1395                 if (ret_code != NS_LDAP_SUCCESS) {
1396                         __s_api_conn_user_free(cu);
1397                         __s_api_unsetInitMode();
1398                         return (ret_code);
1399                 }
1400         }
1401 
1402         __s_api_unsetInitMode();
1403 
1404         /* get search timeout value */
1405         (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, &paramVal, errorp);
1406         if (paramVal != NULL && *paramVal != NULL) {
1407                 tv.tv_sec = **((int **)paramVal);
1408                 (void) __ns_ldap_freeParam(&paramVal);
1409         }
1410         if (*errorp != NULL) {
1411                 (void) __ns_ldap_freeError(errorp);
1412         }
1413 
1414         /* Get root DSE from the server specified by the caller. */
1415         attrs[0] = "supportedControl";
1416         attrs[1] = "supportedsaslmechanisms";
1417         attrs[2] = NULL;
1418         ldap_rc = ldap_search_ext_s(session->ld,
1419             "",
1420             LDAP_SCOPE_BASE,
1421             "(objectclass=*)",
1422             attrs,
1423             0,
1424             NULL,
1425             NULL,
1426             &tv,
1427             0,
1428             &resultMsg);
1429 
1430         if (ldap_rc != LDAP_SUCCESS) {
1431                 /*
1432                  * If the root DSE was not found, the server does
1433                  * not comply with the LDAP v3 protocol.
1434                  */
1435                 (void) ldap_get_option(session->ld,
1436                     LDAP_OPT_ERROR_NUMBER,
1437                     &ldaperrno);
1438                 (void) snprintf(errmsg,
1439                     sizeof (errmsg),
1440                     gettext(ldap_err2string(ldaperrno)));
1441                 MKERROR(LOG_ERR,
1442                     *errorp,
1443                     NS_LDAP_OP_FAILED,
1444                     strdup(errmsg),
1445                     NS_LDAP_MEMORY);
1446 
1447                 if (resultMsg) {
1448                         (void) ldap_msgfree(resultMsg);
1449                         resultMsg = NULL;
1450                 }
1451 
1452                 __s_api_conn_user_free(cu);
1453                 return (NS_LDAP_OP_FAILED);
1454         }
1455         __s_api_conn_user_free(cu);
1456 
1457         ret_code = convert_to_door_line(session->ld,
1458             resultMsg,
1459             INCLUDE_ATTR_NAMES,
1460             NOT_PROFILE,
1461             root_dse);
1462         if (ret_code == NS_LDAP_NOTFOUND) {
1463                 (void) snprintf(errmsg,
1464                     sizeof (errmsg),
1465                     gettext("No root DSE data "
1466                     "for server %s returned."),
1467                     server_addr);
1468                 MKERROR(LOG_ERR,
1469                     *errorp,
1470                     NS_LDAP_NOTFOUND,
1471                     strdup(errmsg),
1472                     NS_LDAP_MEMORY);
1473         }
1474 
1475         if (resultMsg) {
1476                 (void) ldap_msgfree(resultMsg);
1477                 resultMsg = NULL;
1478         }
1479 
1480         DropConnection(sessionId, NS_LDAP_NEW_CONN);
1481 
1482         return (ret_code);
1483 }
1484 
1485 /*
1486  * This function destroys the local list of root DSEs. The input parameter is
1487  * a pointer to the list to be erased.
1488  * The type of the pointer passed to this function should be
1489  * (dir_server_list_t *).
1490  */
1491 static
1492 void *
1493 disposeOfOldList(void *param)
1494 {
1495         dir_server_list_t       *old_list = (dir_server_list_t *)param;
1496         long                    i = 0, j;
1497 
1498         (void) rw_wrlock(&old_list->listDestroyLock);
1499         /* Destroy the old list */
1500         while (old_list->nsServers[i]) {
1501                 free(old_list->nsServers[i]->ip);
1502                 j = 0;
1503                 while (old_list->nsServers[i]->controls &&
1504                     old_list->nsServers[i]->controls[j]) {
1505                         free(old_list->nsServers[i]->controls[j]);
1506                         ++j;
1507                 }
1508                 free(old_list->nsServers[i]->controls);
1509                 j = 0;
1510                 while (old_list->nsServers[i]->saslMech &&
1511                     old_list->nsServers[i]->saslMech[j]) {
1512                         free(old_list->nsServers[i]->saslMech[j]);
1513                         ++j;
1514                 }
1515                 free(old_list->nsServers[i]->saslMech);
1516                 ++i;
1517         }
1518         /*
1519          * All the structures pointed by old_list->nsServers were allocated
1520          * in one chunck. The nsServers[0] pointer points to the beginning
1521          * of that chunck.
1522          */
1523         free(old_list->nsServers[0]);
1524         free(old_list->nsServers);
1525         (void) rw_unlock(&old_list->listDestroyLock);
1526         (void) rwlock_destroy(&old_list->listDestroyLock);
1527         free(old_list);
1528 
1529         return (NULL);
1530 }
1531 
1532 /*
1533  * This function cancels the Standalone mode and destroys the list of root DSEs.
1534  */
1535 void
1536 __ns_ldap_cancelStandalone(void)
1537 {
1538         dir_server_list_t       *old_list;
1539 
1540         (void) mutex_lock(&dir_servers.listReplaceLock);
1541         dir_servers.standalone = 0;
1542         if (!dir_servers.list) {
1543                 (void) mutex_unlock(&dir_servers.listReplaceLock);
1544                 return;
1545         }
1546         old_list = dir_servers.list;
1547         dir_servers.list = NULL;
1548         (void) mutex_unlock(&dir_servers.listReplaceLock);
1549 
1550         (void) disposeOfOldList(old_list);
1551 }
1552 
1553 
1554 static
1555 void*
1556 create_ns_servers_entry(void *param)
1557 {
1558 #define CHUNK_SIZE 16
1559 
1560         dir_server_t            *server = (dir_server_t *)param;
1561         ns_ldap_return_code     *retCode = calloc(1,
1562             sizeof (ns_ldap_return_code));
1563         uint32_t                sc_counter = 0, sm_counter = 0;
1564         uint32_t                sc_mem_blocks = 1, sm_mem_blocks = 1;
1565         char                    *rootDSE = NULL, *attr, *val, *rest, **ptr;
1566         ns_ldap_error_t         *error = NULL;
1567 
1568         if (retCode == NULL) {
1569                 return (NULL);
1570         }
1571 
1572         /*
1573          * We call this function in non anon-fallback mode because we
1574          * want the whole procedure to fail as soon as possible to
1575          * indicate there are problems with connecting to the server.
1576          */
1577         *retCode = __ns_ldap_getRootDSE(server->ip,
1578             &rootDSE,
1579             &error,
1580             SA_ALLOW_FALLBACK);
1581 
1582         if (*retCode == NS_LDAP_MEMORY) {
1583                 free(retCode);
1584                 return (NULL);
1585         }
1586 
1587         /*
1588          * If the root DSE can not be obtained, log an error and keep the
1589          * server.
1590          */
1591         if (*retCode != NS_LDAP_SUCCESS) {
1592                 server->status = INFO_SERVER_ERROR;
1593                 syslog(LOG_WARNING,
1594                     gettext("libsldap (\"standalone\" mode): "
1595                     "can not obtain the root DSE from %s. %s"),
1596                     server->ip,
1597                     error && error->message ? error->message : "");
1598                 if (error) {
1599                         (void) __ns_ldap_freeError(&error);
1600                 }
1601                 return (retCode);
1602         }
1603 
1604         /* Get the first attribute of the root DSE. */
1605         attr = strtok_r(rootDSE, DOORLINESEP, &rest);
1606         if (attr == NULL) {
1607                 free(rootDSE);
1608                 server->status = INFO_SERVER_ERROR;
1609                 syslog(LOG_WARNING,
1610                     gettext("libsldap (\"standalone\" mode): "
1611                     "the root DSE from %s is empty or corrupted."),
1612                     server->ip);
1613                 *retCode = NS_LDAP_INTERNAL;
1614                 return (retCode);
1615         }
1616 
1617         server->controls = (char **)calloc(CHUNK_SIZE, sizeof (char *));
1618         server->saslMech = (char **)calloc(CHUNK_SIZE, sizeof (char *));
1619         if (server->controls == NULL || server->saslMech == NULL) {
1620                 free(rootDSE);
1621                 free(retCode);
1622                 return (NULL);
1623         }
1624 
1625         do {
1626                 if ((val = strchr(attr, '=')) == NULL) {
1627                         continue;
1628                 }
1629                 ++val;
1630 
1631                 if (strncasecmp(attr,
1632                     _SASLMECHANISM,
1633                     _SASLMECHANISM_LEN) == 0) {
1634                         if (sm_counter == CHUNK_SIZE * sm_mem_blocks - 1) {
1635                                 ptr = (char **)realloc(server->saslMech,
1636                                     CHUNK_SIZE *
1637                                     ++sm_mem_blocks *
1638                                     sizeof (char *));
1639                                 if (ptr == NULL) {
1640                                         *retCode = NS_LDAP_MEMORY;
1641                                         break;
1642                                 }
1643                                 bzero((char *)ptr +
1644                                     (sm_counter + 1) *
1645                                     sizeof (char *),
1646                                     CHUNK_SIZE *
1647                                     sm_mem_blocks *
1648                                     sizeof (char *) -
1649                                     (sm_counter + 1) *
1650                                     sizeof (char *));
1651                                 server->saslMech = ptr;
1652                         }
1653                         server->saslMech[sm_counter] = strdup(val);
1654                         if (server->saslMech[sm_counter] == NULL) {
1655                                 *retCode = NS_LDAP_MEMORY;
1656                                 break;
1657                         }
1658                         ++sm_counter;
1659                         continue;
1660                 }
1661                 if (strncasecmp(attr,
1662                     _SUPPORTEDCONTROL,
1663                     _SUPPORTEDCONTROL_LEN) == 0) {
1664                         if (sc_counter == CHUNK_SIZE * sc_mem_blocks - 1) {
1665                                 ptr = (char **)realloc(server->controls,
1666                                     CHUNK_SIZE *
1667                                     ++sc_mem_blocks *
1668                                     sizeof (char *));
1669                                 if (ptr == NULL) {
1670                                         *retCode = NS_LDAP_MEMORY;
1671                                         break;
1672                                 }
1673                                 bzero((char *)ptr +
1674                                     (sc_counter + 1) *
1675                                     sizeof (char *),
1676                                     CHUNK_SIZE *
1677                                     sc_mem_blocks *
1678                                     sizeof (char *) -
1679                                     (sc_counter + 1) *
1680                                     sizeof (char *));
1681                                 server->controls = ptr;
1682                         }
1683 
1684                         server->controls[sc_counter] = strdup(val);
1685                         if (server->controls[sc_counter] == NULL) {
1686                                 *retCode = NS_LDAP_MEMORY;
1687                                 break;
1688                         }
1689                         ++sc_counter;
1690                         continue;
1691                 }
1692 
1693         } while (attr = strtok_r(NULL, DOORLINESEP, &rest));
1694 
1695         free(rootDSE);
1696 
1697         if (*retCode == NS_LDAP_MEMORY) {
1698                 free(retCode);
1699                 return (NULL);
1700         }
1701 
1702         server->controls[sc_counter] = NULL;
1703         server->saslMech[sm_counter] = NULL;
1704 
1705         server->status = INFO_SERVER_UP;
1706 
1707         return (retCode);
1708 #undef CHUNK_SIZE
1709 }
1710 
1711 
1712 /*
1713  * This function creates a new local list of root DSEs from all the servers
1714  * mentioned in the DUAProfile (or local NS BEC) and returns
1715  * a pointer to the list.
1716  */
1717 static
1718 ns_ldap_return_code
1719 createDirServerList(dir_server_list_t **new_list,
1720                 ns_ldap_error_t **errorp)
1721 {
1722         char                    **serverList;
1723         ns_ldap_return_code     retCode = NS_LDAP_SUCCESS;
1724         dir_server_t            *tmpSrvArray;
1725         long                    srvListLength, i;
1726         thread_t                *thrPool, thrID;
1727         void                    *status = NULL;
1728 
1729         if (errorp == NULL) {
1730                 return (NS_LDAP_INVALID_PARAM);
1731         }
1732 
1733         *errorp = NULL;
1734 
1735         if (new_list == NULL) {
1736                 return (NS_LDAP_INVALID_PARAM);
1737         }
1738 
1739         retCode = __s_api_getServers(&serverList, errorp);
1740         if (retCode != NS_LDAP_SUCCESS || serverList == NULL) {
1741                 return (retCode);
1742         }
1743 
1744         for (i = 0; serverList[i]; ++i) {
1745                 ;
1746         }
1747         srvListLength = i;
1748 
1749         thrPool = calloc(srvListLength, sizeof (thread_t));
1750         if (thrPool == NULL) {
1751                 __s_api_free2dArray(serverList);
1752                 return (NS_LDAP_MEMORY);
1753         }
1754 
1755         *new_list = (dir_server_list_t *)calloc(1,
1756             sizeof (dir_server_list_t));
1757         if (*new_list == NULL) {
1758                 __s_api_free2dArray(serverList);
1759                 free(thrPool);
1760                 return (NS_LDAP_MEMORY);
1761         }
1762         (void) rwlock_init(&(*new_list)->listDestroyLock, USYNC_THREAD, NULL);
1763 
1764         (*new_list)->nsServers = (dir_server_t **)calloc(srvListLength + 1,
1765             sizeof (dir_server_t *));
1766         if ((*new_list)->nsServers == NULL) {
1767                 free(*new_list);
1768                 *new_list = NULL;
1769                 __s_api_free2dArray(serverList);
1770                 free(thrPool);
1771                 return (NS_LDAP_MEMORY);
1772         }
1773 
1774         /*
1775          * Allocate a set of dir_server_t structures as an array,
1776          * with one alloc call and then initialize the nsServers pointers
1777          * with the addresses of the array's members.
1778          */
1779         tmpSrvArray = (dir_server_t *)calloc(srvListLength,
1780             sizeof (dir_server_t));
1781         for (i = 0; i < srvListLength; ++i) {
1782                 (*new_list)->nsServers[i] = &tmpSrvArray[i];
1783 
1784                 (*new_list)->nsServers[i]->info = INFO_STATUS_NEW;
1785                 (void) mutex_init(&(*new_list)->nsServers[i]->updateStatus,
1786                     USYNC_THREAD,
1787                     NULL);
1788 
1789                 (*new_list)->nsServers[i]->ip = strdup(serverList[i]);
1790                 if ((*new_list)->nsServers[i]->ip == NULL) {
1791                         retCode = NS_LDAP_MEMORY;
1792                         break;
1793                 }
1794 
1795                 (*new_list)->nsServers[i]->status = INFO_SERVER_CONNECTING;
1796 
1797                 switch (thr_create(NULL,
1798                     0,
1799                     create_ns_servers_entry,
1800                     (*new_list)->nsServers[i],
1801                     0,
1802                     &thrID)) {
1803                 case EAGAIN:
1804                         (*new_list)->nsServers[i]->status =
1805                             INFO_SERVER_ERROR;
1806                         continue;
1807                 case ENOMEM:
1808                         (*new_list)->nsServers[i]->status =
1809                             INFO_SERVER_ERROR;
1810                         continue;
1811                 default:
1812                         thrPool[i] = thrID;
1813                         continue;
1814                 }
1815         }
1816 
1817         for (i = 0; i < srvListLength; ++i) {
1818                 if (thrPool[i] != 0 &&
1819                     thr_join(thrPool[i], NULL, &status) == 0) {
1820                         if (status == NULL) {
1821                                 /*
1822                                  * Some memory allocation problems occured. Just
1823                                  * ignore the server and hope there will be some
1824                                  * other good ones.
1825                                  */
1826                                 (*new_list)->nsServers[i]->status =
1827                                     INFO_SERVER_ERROR;
1828                         }
1829                         free(status);
1830                 }
1831         }
1832 
1833         __s_api_free2dArray(serverList);
1834         free(thrPool);
1835 
1836         if (retCode == NS_LDAP_MEMORY) {
1837                 (void) disposeOfOldList(*new_list);
1838                 return (NS_LDAP_MEMORY);
1839         }
1840 
1841         return (NS_LDAP_SUCCESS);
1842 }
1843 
1844 /*
1845  * This functions replaces the local list of root DSEs with a new one and starts
1846  * a thread destroying the old list. There is no need for other threads to wait
1847  * until the old list will be destroyed.
1848  * Since it is possible that more than one thread can start creating the list,
1849  * this function should be protected by mutexes to be sure that only one thread
1850  * performs the initialization.
1851  */
1852 static
1853 ns_ldap_return_code
1854 initGlobalList(ns_ldap_error_t **error)
1855 {
1856         dir_server_list_t       *new_list, *old_list;
1857         ns_ldap_return_code     ret_code;
1858         thread_t                tid;
1859 
1860         ret_code = createDirServerList(&new_list, error);
1861         if (ret_code != NS_LDAP_SUCCESS) {
1862                 return (ret_code);
1863         }
1864 
1865         old_list = dir_servers.list;
1866         dir_servers.list = new_list;
1867 
1868         if (old_list) {
1869                 (void) thr_create(NULL,
1870                     0,
1871                     disposeOfOldList,
1872                     old_list,
1873                     THR_DETACHED,
1874                     &tid);
1875         }
1876 
1877         return (NS_LDAP_SUCCESS);
1878 }
1879 
1880 static
1881 struct {
1882         char *authMech;
1883         ns_auth_t auth;
1884 } authArray[] = {{"none", {NS_LDAP_AUTH_NONE,
1885                         NS_LDAP_TLS_NONE,
1886                         NS_LDAP_SASL_NONE,
1887                         NS_LDAP_SASLOPT_NONE}},
1888                 {"simple", {NS_LDAP_AUTH_SIMPLE,
1889                         NS_LDAP_TLS_NONE,
1890                         NS_LDAP_SASL_NONE,
1891                         NS_LDAP_SASLOPT_NONE}},
1892                 {"tls:simple", {NS_LDAP_AUTH_TLS,
1893                         NS_LDAP_TLS_SIMPLE,
1894                         NS_LDAP_SASL_NONE,
1895                         NS_LDAP_SASLOPT_NONE}},
1896                 {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS,
1897                         NS_LDAP_TLS_SASL,
1898                         NS_LDAP_SASL_CRAM_MD5,
1899                         NS_LDAP_SASLOPT_NONE}},
1900                 {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS,
1901                         NS_LDAP_TLS_SASL,
1902                         NS_LDAP_SASL_DIGEST_MD5,
1903                         NS_LDAP_SASLOPT_NONE}},
1904                 {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL,
1905                         NS_LDAP_TLS_SASL,
1906                         NS_LDAP_SASL_CRAM_MD5,
1907                         NS_LDAP_SASLOPT_NONE}},
1908                 {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL,
1909                         NS_LDAP_TLS_SASL,
1910                         NS_LDAP_SASL_DIGEST_MD5,
1911                         NS_LDAP_SASLOPT_NONE}},
1912                 {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL,
1913                         NS_LDAP_TLS_SASL,
1914                         NS_LDAP_SASL_GSSAPI,
1915                         NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}},
1916                 {NULL, {NS_LDAP_AUTH_NONE,
1917                         NS_LDAP_TLS_NONE,
1918                         NS_LDAP_SASL_NONE,
1919                         NS_LDAP_SASLOPT_NONE}}};
1920 
1921 ns_ldap_return_code
1922 __ns_ldap_initAuth(const char *auth_mech,
1923                 ns_auth_t *auth,
1924                 ns_ldap_error_t **errorp)
1925 {
1926         uint32_t        i;
1927         char            errmsg[MAXERROR];
1928 
1929         if (auth_mech == NULL) {
1930                 (void) snprintf(errmsg,
1931                     sizeof (errmsg),
1932                     gettext("Invalid authentication method specified\n"));
1933                 MKERROR(LOG_WARNING,
1934                     *errorp,
1935                     NS_LDAP_INTERNAL,
1936                     strdup(errmsg),
1937                     NS_LDAP_MEMORY);
1938                 return (NS_LDAP_INTERNAL);
1939         }
1940 
1941         for (i = 0; authArray[i].authMech != NULL; ++i) {
1942                 if (strcasecmp(auth_mech, authArray[i].authMech) == 0) {
1943                         *auth = authArray[i].auth;
1944                         return (NS_LDAP_SUCCESS);
1945                 }
1946         }
1947 
1948         (void) snprintf(errmsg,
1949             sizeof (errmsg),
1950             gettext("Invalid authentication method specified\n"));
1951         MKERROR(LOG_WARNING,
1952             *errorp,
1953             NS_LDAP_INTERNAL,
1954             strdup(errmsg),
1955             NS_LDAP_MEMORY);
1956         return (NS_LDAP_INTERNAL);
1957 }
1958 
1959 /*
1960  * This function "informs" libsldap that a client application has specified
1961  * a directory to use. The function obtains a DUAProfile, credentials,
1962  * and naming context. During all further operations on behalf
1963  * of the application requested a standalone schema libsldap will use
1964  * the information obtained by __ns_ldap_initStandalone() instead of
1965  * door_call(3C)ing ldap_cachemgr(1M).
1966  *
1967  * INPUT:
1968  *     sa_conf - a structure describing where and in which way to obtain all
1969  *               the configuration describing how to communicate to
1970  *               a choosen LDAP directory,
1971  *     errorp - an error object describing an error occured.
1972  */
1973 ns_ldap_return_code
1974 __ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf,
1975                         ns_ldap_error_t **errorp) {
1976 
1977         ns_cred_t       user_cred = {{NS_LDAP_AUTH_NONE,
1978                                         NS_LDAP_TLS_NONE,
1979                                         NS_LDAP_SASL_NONE,
1980                                         NS_LDAP_SASLOPT_NONE},
1981                                         NULL,
1982                                         {NULL, NULL}};
1983         char            *dua_profile = NULL;
1984         char            errmsg[MAXERROR];
1985         ns_config_t     *cfg;
1986         int             ret_code;
1987 
1988         if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL ||
1989             sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) {
1990                 (void) snprintf(errmsg,
1991                     sizeof (errmsg),
1992                     gettext("Bind DN and bind password"
1993                     " must both be provided\n"));
1994                 MKERROR(LOG_ERR,
1995                     *errorp,
1996                     NS_CONFIG_NOTLOADED,
1997                     strdup(errmsg),
1998                     NS_LDAP_MEMORY);
1999                 return (NS_LDAP_INTERNAL);
2000         }
2001 
2002         switch (sa_conf->type) {
2003         case NS_LDAP_SERVER:
2004                 if (sa_conf->SA_BIND_DN != NULL) {
2005                         user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN;
2006                         user_cred.auth.type = NS_LDAP_AUTH_SIMPLE;
2007                 }
2008 
2009                 if (sa_conf->SA_BIND_PWD != NULL) {
2010                         user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD;
2011                 }
2012 
2013                 if (sa_conf->SA_AUTH != NULL) {
2014                         user_cred.auth.type = sa_conf->SA_AUTH->type;
2015                         user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype;
2016                         user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech;
2017                         user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt;
2018                 }
2019 
2020                 if (sa_conf->SA_CERT_PATH != NULL) {
2021                         user_cred.hostcertpath = sa_conf->SA_CERT_PATH;
2022                 }
2023 
2024                 ret_code = __ns_ldap_getConnectionInfoFromDUA(
2025                     &sa_conf->ds_profile.server,
2026                     &user_cred,
2027                     &dua_profile,
2028                     NULL,
2029                     errorp);
2030                 if (ret_code != NS_LDAP_SUCCESS) {
2031                         return (ret_code);
2032                 }
2033 
2034                 cfg = __s_api_create_config_door_str(dua_profile, errorp);
2035                 if (cfg == NULL) {
2036                         free(dua_profile);
2037                         return (NS_LDAP_CONFIG);
2038                 }
2039 
2040                 if (sa_conf->SA_CERT_PATH != NULL) {
2041                         char            *certPathAttr;
2042                         ParamIndexType  type;
2043 
2044                         switch (cfg->version) {
2045                         case NS_LDAP_V1:
2046                                 certPathAttr = "NS_LDAP_CERT_PATH";
2047                                 break;
2048                         default:        /* Version 2 */
2049                                 certPathAttr = "NS_LDAP_HOST_CERTPATH";
2050                                 break;
2051                         }
2052 
2053                         if (__s_api_get_versiontype(cfg,
2054                                                 certPathAttr,
2055                                                 &type) == 0 &&
2056                             (ret_code = __ns_ldap_setParamValue(cfg,
2057                                                 type,
2058                                                 sa_conf->SA_CERT_PATH,
2059                                                 errorp)) != NS_LDAP_SUCCESS) {
2060                                 __s_api_destroy_config(cfg);
2061                                 return (ret_code);
2062                         }
2063                 }
2064 
2065                 if (sa_conf->SA_BIND_DN != NULL &&
2066                     sa_conf->SA_BIND_PWD != NULL) {
2067                         char *authMethods;
2068 
2069                         authMethods = __s_api_strValue(cfg, NS_LDAP_AUTH_P,
2070                             NS_FILE_FMT);
2071                         if (authMethods != NULL &&
2072                             strstr(authMethods, "sasl/GSSAPI") != NULL) {
2073                                 /*
2074                                  * The received DUAProfile specifies
2075                                  * sasl/GSSAPI as an auth. mechanism.
2076                                  * The bind DN and password will be
2077                                  * ignored.
2078                                  */
2079                                 syslog(LOG_INFO, gettext("sasl/GSSAPI will be "
2080                                     "used as an authentication method. "
2081                                     "The bind DN and password will "
2082                                     "be ignored.\n"));
2083                                 free(authMethods);
2084                                 break;
2085                         }
2086 
2087                         if (authMethods != NULL)
2088                                 free(authMethods);
2089 
2090                         if (__ns_ldap_setParamValue(cfg,
2091                                                 NS_LDAP_BINDDN_P,
2092                                                 sa_conf->SA_BIND_DN,
2093                                                 errorp) != NS_LDAP_SUCCESS) {
2094                                 __s_api_destroy_config(cfg);
2095                                 return (NS_LDAP_CONFIG);
2096                         }
2097 
2098                         if (__ns_ldap_setParamValue(cfg,
2099                             NS_LDAP_BINDPASSWD_P,
2100                             sa_conf->SA_BIND_PWD,
2101                             errorp) != NS_LDAP_SUCCESS) {
2102                                 __s_api_destroy_config(cfg);
2103                                 return (NS_LDAP_CONFIG);
2104                         }
2105                 }
2106 
2107                 break;
2108         default:        /* NS_CACHEMGR */
2109                 return (NS_LDAP_SUCCESS);
2110         }
2111 
2112         __s_api_init_config(cfg);
2113         /* Connection management should use the new config now. */
2114         __s_api_reinit_conn_mgmt_new_config(cfg);
2115         __ns_ldap_setServer(TRUE);
2116 
2117         (void) mutex_lock(&dir_servers.listReplaceLock);
2118         if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) {
2119                 (void) mutex_unlock(&dir_servers.listReplaceLock);
2120                 return (ret_code);
2121         }
2122         dir_servers.standalone = 1;
2123         (void) mutex_unlock(&dir_servers.listReplaceLock);
2124 
2125         return (NS_LDAP_SUCCESS);
2126 }
2127 
2128 /*
2129  * INPUT:
2130  *     serverAddr is the address of a server and
2131  *     request is one of the following:
2132  *     NS_CACHE_NEW:    get a new server address, addr is ignored.
2133  *     NS_CACHE_NORESP: get the next one, remove addr from list.
2134  *     NS_CACHE_NEXT:   get the next one, keep addr on list.
2135  *     NS_CACHE_WRITE:  get a non-replica server, if possible, if not, same
2136  *                      as NS_CACHE_NEXT.
2137  *     addrType:
2138  *     NS_CACHE_ADDR_IP: return server address as is, this is default.
2139  *     NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only
2140  *                             self credential case requires such format.
2141  * OUTPUT:
2142  *     ret
2143  *
2144  *     a structure of type ns_server_info_t containing the server address
2145  *     or name, server controls and supported SASL mechanisms.
2146  *     NOTE: Caller should allocate space for the structure and free
2147  *     all the space allocated by the function for the information contained
2148  *     in the structure.
2149  *
2150  *     error - an error object describing an error, if any.
2151  */
2152 ns_ldap_return_code
2153 __s_api_findRootDSE(const char *request,
2154                 const char *serverAddr,
2155                 const char *addrType,
2156                 ns_server_info_t *ret,
2157                 ns_ldap_error_t **error)
2158 {
2159         dir_server_list_t       *current_list = NULL;
2160         ns_ldap_return_code     ret_code;
2161         long                    i = 0;
2162         int                     matched = FALSE;
2163         dir_server_t            *server = NULL;
2164         char                    errmsg[MAXERROR];
2165 
2166         (void) mutex_lock(&dir_servers.listReplaceLock);
2167         if (dir_servers.list == NULL) {
2168                 (void) mutex_unlock(&dir_servers.listReplaceLock);
2169                 (void) snprintf(errmsg,
2170                     sizeof (errmsg),
2171                     gettext("The list of root DSEs is empty: "
2172                     "the Standalone mode was not properly initialized"));
2173                 MKERROR(LOG_ERR,
2174                     *error,
2175                     NS_CONFIG_NOTLOADED,
2176                     strdup(errmsg),
2177                     NS_LDAP_MEMORY);
2178                 return (NS_LDAP_INTERNAL);
2179         }
2180 
2181         current_list = dir_servers.list;
2182         (void) rw_rdlock(&current_list->listDestroyLock);
2183         (void) mutex_unlock(&dir_servers.listReplaceLock);
2184 
2185         /*
2186          * The code below is mostly the clone of the
2187          * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function.
2188          * Currently we have two different server lists: one is maintained
2189          * by libsldap ('standalone' mode), the other is in ldap_cachemgr
2190          * (a part of its standard functionality).
2191          */
2192 
2193         /*
2194          * If NS_CACHE_NEW, or the server info is new,
2195          * starts from the beginning of the list.
2196          */
2197         (void) mutex_lock(&current_list->nsServers[0]->updateStatus);
2198         if (strcmp(request, NS_CACHE_NEW) == 0 ||
2199             current_list->nsServers[0]->info == INFO_STATUS_NEW) {
2200                 matched = TRUE;
2201         }
2202         (void) mutex_unlock(&current_list->nsServers[i]->updateStatus);
2203 
2204         for (i = 0; current_list->nsServers[i]; ++i) {
2205                 /*
2206                  * Lock the updateStatus mutex to
2207                  * make sure the server status stays the same
2208                  * while the data is being processed.
2209                  */
2210                 if (matched == FALSE &&
2211                     strcmp(current_list->nsServers[i]->ip,
2212                     serverAddr) == 0) {
2213                         matched = TRUE;
2214                         if (strcmp(request, NS_CACHE_NORESP) == 0) {
2215 
2216                                 /*
2217                                  * if the server has already been removed,
2218                                  * don't bother.
2219                                  */
2220                                 (void) mutex_lock(&current_list->
2221                                     nsServers[i]->updateStatus);
2222                                 if (current_list->nsServers[i]->status ==
2223                                     INFO_SERVER_REMOVED) {
2224                                         (void) mutex_unlock(&current_list->
2225                                             nsServers[i]->
2226                                             updateStatus);
2227                                         continue;
2228                                 }
2229                                 (void) mutex_unlock(&current_list->
2230                                     nsServers[i]->
2231                                     updateStatus);
2232 
2233                                 /*
2234                                  * if the information is new,
2235                                  * give this server one more chance.
2236                                  */
2237                                 (void) mutex_lock(&current_list->
2238                                     nsServers[i]->
2239                                     updateStatus);
2240                                 if (current_list->nsServers[i]->info ==
2241                                     INFO_STATUS_NEW &&
2242                                     current_list->nsServers[i]->status  ==
2243                                     INFO_SERVER_UP) {
2244                                         server = current_list->nsServers[i];
2245                                         (void) mutex_unlock(&current_list->
2246                                             nsServers[i]->
2247                                             updateStatus);
2248                                         break;
2249                                 } else {
2250                                         /*
2251                                          * it is recommended that
2252                                          * before removing the
2253                                          * server from the list,
2254                                          * the server should be
2255                                          * contacted one more time
2256                                          * to make sure that it is
2257                                          * really unavailable.
2258                                          * For now, just trust the client
2259                                          * (i.e., the sldap library)
2260                                          * that it knows what it is
2261                                          * doing and would not try
2262                                          * to mess up the server
2263                                          * list.
2264                                          */
2265                                         current_list->nsServers[i]->status =
2266                                             INFO_SERVER_REMOVED;
2267                                         (void) mutex_unlock(&current_list->
2268                                             nsServers[i]->
2269                                             updateStatus);
2270                                         continue;
2271                                 }
2272                         } else {
2273                                 /*
2274                                  * req == NS_CACHE_NEXT or NS_CACHE_WRITE
2275                                  */
2276                                 continue;
2277                         }
2278                 }
2279 
2280                 if (matched) {
2281                         if (strcmp(request, NS_CACHE_WRITE) == 0) {
2282                                 /*
2283                                  * ldap_cachemgr checks here if the server
2284                                  * is not a non-replica server (a server
2285                                  * of type INFO_RW_WRITEABLE). But currently
2286                                  * it considers all the servers in its list
2287                                  * as those.
2288                                  */
2289                                 (void) mutex_lock(&current_list->
2290                                     nsServers[i]->
2291                                     updateStatus);
2292                                 if (current_list->nsServers[i]->status  ==
2293                                     INFO_SERVER_UP) {
2294                                         (void) mutex_unlock(&current_list->
2295                                             nsServers[i]->
2296                                             updateStatus);
2297                                         server = current_list->nsServers[i];
2298                                         break;
2299                                 }
2300                         } else {
2301                                 (void) mutex_lock(&current_list->
2302                                     nsServers[i]->
2303                                     updateStatus);
2304                                 if (current_list->nsServers[i]->status ==
2305                                     INFO_SERVER_UP) {
2306                                         (void) mutex_unlock(&current_list->
2307                                             nsServers[i]->
2308                                             updateStatus);
2309                                         server = current_list->nsServers[i];
2310                                         break;
2311                                 }
2312                         }
2313 
2314                         (void) mutex_unlock(&current_list->
2315                             nsServers[i]->
2316                             updateStatus);
2317                 }
2318         }
2319 
2320         if (server == NULL) {
2321                 (void) rw_unlock(&current_list->listDestroyLock);
2322                 (void) snprintf(errmsg,
2323                     sizeof (errmsg),
2324                     gettext("No servers are available"));
2325                 MKERROR(LOG_ERR,
2326                     *error,
2327                     NS_CONFIG_NOTLOADED,
2328                     strdup(errmsg),
2329                     NS_LDAP_MEMORY);
2330                 return (NS_LDAP_NOTFOUND);
2331         }
2332 
2333         (void) mutex_lock(&server->updateStatus);
2334         server->info = INFO_STATUS_OLD;
2335         (void) mutex_unlock(&server->updateStatus);
2336 
2337         if (ret == NULL) {
2338                 (void) rw_unlock(&current_list->listDestroyLock);
2339                 return (NS_LDAP_SUCCESS);
2340         }
2341 
2342         if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
2343                 ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN);
2344                 if (ret_code != NS_LDAP_SUCCESS) {
2345                         (void) snprintf(errmsg,
2346                             sizeof (errmsg),
2347                             gettext("The %s address "
2348                             "can not be resolved into "
2349                             "a host name. Returning "
2350                             "the address as it is."),
2351                             server->ip);
2352                         MKERROR(LOG_ERR,
2353                             *error,
2354                             NS_CONFIG_NOTLOADED,
2355                             strdup(errmsg),
2356                             NS_LDAP_MEMORY);
2357                         return (NS_LDAP_INTERNAL);
2358                 }
2359         }
2360 
2361         ret->server = strdup(server->ip);
2362 
2363         ret->controls = __s_api_cp2dArray(server->controls);
2364         ret->saslMechanisms = __s_api_cp2dArray(server->saslMech);
2365 
2366         (void) rw_unlock(&current_list->listDestroyLock);
2367 
2368         return (NS_LDAP_SUCCESS);
2369 }
2370 
2371 /*
2372  * This function iterates through the list of the configured LDAP servers
2373  * and "pings" those which are marked as removed or if any error occurred
2374  * during the previous receiving of the server's root DSE. If the
2375  * function is able to reach such a server and get its root DSE, it
2376  * marks the server as on-line. Otherwise, the server's status is set
2377  * to "Error".
2378  * For each server the function tries to connect to, it fires up
2379  * a separate thread and then waits until all the treads finish.
2380  * The function returns NS_LDAP_INTERNAL if the Standalone mode was not
2381  * initialized or was canceled prior to an invocation of
2382  * __ns_ldap_pingOfflineServers().
2383  */
2384 ns_ldap_return_code
2385 __ns_ldap_pingOfflineServers(void)
2386 {
2387         dir_server_list_t       *current_list = NULL;
2388         ns_ldap_return_code     retCode = NS_LDAP_SUCCESS;
2389         long                    srvListLength, i = 0;
2390         thread_t                *thrPool, thrID;
2391         void                    *status = NULL;
2392 
2393         (void) mutex_lock(&dir_servers.listReplaceLock);
2394         if (dir_servers.list == NULL) {
2395                 (void) mutex_unlock(&dir_servers.listReplaceLock);
2396                 return (NS_LDAP_INTERNAL);
2397         }
2398 
2399         current_list = dir_servers.list;
2400         (void) rw_wrlock(&current_list->listDestroyLock);
2401         (void) mutex_unlock(&dir_servers.listReplaceLock);
2402 
2403         while (current_list->nsServers[i] != NULL) {
2404                 ++i;
2405         }
2406         srvListLength = i;
2407 
2408         thrPool = calloc(srvListLength, sizeof (thread_t));
2409         if (thrPool == NULL) {
2410                 (void) rw_unlock(&current_list->listDestroyLock);
2411                 return (NS_LDAP_MEMORY);
2412         }
2413 
2414         for (i = 0; i < srvListLength; ++i) {
2415                 if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED &&
2416                     current_list->nsServers[i]->status != INFO_SERVER_ERROR) {
2417                         continue;
2418                 }
2419                 current_list->nsServers[i]->status = INFO_SERVER_CONNECTING;
2420                 current_list->nsServers[i]->info = INFO_STATUS_NEW;
2421 
2422                 __s_api_free2dArray(current_list->nsServers[i]->controls);
2423                 current_list->nsServers[i]->controls = NULL;
2424                 __s_api_free2dArray(current_list->nsServers[i]->saslMech);
2425                 current_list->nsServers[i]->saslMech = NULL;
2426 
2427                 switch (thr_create(NULL,
2428                     0,
2429                     create_ns_servers_entry,
2430                     current_list->nsServers[i],
2431                     0,
2432                     &thrID)) {
2433                 case EAGAIN:
2434                         current_list->nsServers[i]->status = INFO_SERVER_ERROR;
2435                         continue;
2436                 case ENOMEM:
2437                         current_list->nsServers[i]->status = INFO_SERVER_ERROR;
2438                         retCode = NS_LDAP_MEMORY;
2439                         break;
2440                 default:
2441                         thrPool[i] = thrID;
2442                         continue;
2443                 }
2444                 /* A memory allocation error has occured */
2445                 break;
2446 
2447         }
2448 
2449         for (i = 0; i < srvListLength; ++i) {
2450                 if (thrPool[i] != 0 &&
2451                     thr_join(thrPool[i], NULL, &status) == 0) {
2452                         if (status == NULL) {
2453                                 current_list->nsServers[i]->status =
2454                                     INFO_SERVER_ERROR;
2455                                 retCode = NS_LDAP_MEMORY;
2456                         }
2457                         free(status);
2458                 }
2459         }
2460 
2461         (void) rw_unlock(&current_list->listDestroyLock);
2462 
2463         free(thrPool);
2464 
2465         return (retCode);
2466 }