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