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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 #include <stdio.h>
  27 #include <sys/types.h>
  28 #include <stdlib.h>
  29 #include <libintl.h>
  30 #include <ctype.h>
  31 #include <syslog.h>
  32 #include <sys/stat.h>
  33 #include <fcntl.h>
  34 #include <unistd.h>
  35 #include <string.h>
  36 #include <strings.h>
  37 #include <priv.h>
  38 
  39 #include "ns_sldap.h"
  40 #include "ns_internal.h"
  41 #include "ns_cache_door.h"
  42 #include "ns_connmgmt.h"
  43 
  44 #define _NIS_FILTER     "nisdomain=*"
  45 #define _NIS_DOMAIN     "nisdomain"
  46 static const char *nis_domain_attrs[] = {
  47         _NIS_DOMAIN,
  48         (char *)NULL
  49 };
  50 
  51 static int validate_filter(ns_ldap_cookie_t *cookie);
  52 
  53 void
  54 __ns_ldap_freeEntry(ns_ldap_entry_t *ep)
  55 {
  56         int             j, k = 0;
  57 
  58         if (ep == NULL)
  59                 return;
  60 
  61         if (ep->attr_pair == NULL) {
  62                 free(ep);
  63                 return;
  64         }
  65         for (j = 0; j < ep->attr_count; j++) {
  66                 if (ep->attr_pair[j] == NULL)
  67                         continue;
  68                 if (ep->attr_pair[j]->attrname)
  69                         free(ep->attr_pair[j]->attrname);
  70                 if (ep->attr_pair[j]->attrvalue) {
  71                         for (k = 0; (k < ep->attr_pair[j]->value_count) &&
  72                             (ep->attr_pair[j]->attrvalue[k]); k++) {
  73                                 free(ep->attr_pair[j]->attrvalue[k]);
  74                         }
  75                         free(ep->attr_pair[j]->attrvalue);
  76                 }
  77                 free(ep->attr_pair[j]);
  78         }
  79         free(ep->attr_pair);
  80         free(ep);
  81 }
  82 
  83 static void
  84 _freeControlList(LDAPControl ***ctrls)
  85 {
  86         LDAPControl     **ctrl;
  87 
  88         if (ctrls == NULL || *ctrls == NULL)
  89                 return;
  90 
  91         for (ctrl = *ctrls; *ctrl != NULL; ctrl++)
  92                 ldap_control_free(*ctrl);
  93         free(*ctrls);
  94         *ctrls = NULL;
  95 }
  96 /*
  97  * Convert attribute type in a RDN that has an attribute mapping to the
  98  * original mappped type.
  99  * e.g.
 100  * cn<->cn-st and iphostnumber<->iphostnumber-st
 101  * cn-st=aaa+iphostnumber-st=10.10.01.01
 102  * is mapped to
 103  * cn=aaa+iphostnumber=10.10.01.01
 104  *
 105  * Input - service: e.g. hosts, passwd etc.
 106  *         rdn: RDN
 107  * Return: NULL - No attribute mapping in the RDN
 108  *         Non-NULL - The attribute type(s) in the RDN are mapped and
 109  *                    the memory is allocated for the new rdn.
 110  *
 111  */
 112 static char *
 113 _cvtRDN(const char *service, const char *rdn)
 114 {
 115         char    **attrs, **mapped_attrs, **mapp, *type, *value, *attr;
 116         char    *new_rdn = NULL;
 117         int     nAttr = 0, i, attr_mapped, len = 0;
 118 
 119         /* Break down "type=value\0" pairs. Assume RDN is normalized */
 120         if ((attrs = ldap_explode_rdn(rdn, 0)) == NULL)
 121                 return (NULL);
 122 
 123         for (nAttr = 0; attrs[nAttr] != NULL; nAttr++)
 124                 ;
 125 
 126         if ((mapped_attrs = (char **)calloc(nAttr, sizeof (char *))) == NULL) {
 127                 ldap_value_free(attrs);
 128                 return (NULL);
 129         }
 130 
 131         attr_mapped = 0;
 132         for (i = 0; i < nAttr; i++) {
 133                 /* Parse type=value pair */
 134                 if ((type = strtok_r(attrs[i], "=", &value)) == NULL ||
 135                     value == NULL)
 136                         goto cleanup;
 137                 /* Reverse map: e.g. cn-sm -> cn */
 138                 mapp = __ns_ldap_getOrigAttribute(service, type);
 139                 if (mapp != NULL && mapp[0] != NULL) {
 140                         /* The attribute mapping is found */
 141                         type = mapp[0];
 142                         attr_mapped = 1;
 143 
 144                         /* "type=value\0" */
 145                         len = strlen(type) + strlen(value) + 2;
 146 
 147                         /* Reconstruct type=value pair. A string is allocated */
 148                         if ((attr = (char *)calloc(1, len)) == NULL) {
 149                                 __s_api_free2dArray(mapp);
 150                                 goto cleanup;
 151                         }
 152                         (void) snprintf(attr, len, "%s=%s", type, value);
 153                         mapped_attrs[i] = attr;
 154                 } else {
 155                         /*
 156                          * No attribute mapping. attrs[i] is going to be copied
 157                          * later. Restore "type\0value\0" back to
 158                          * "type=value\0".
 159                          */
 160                         type[strlen(type)] = '=';
 161                 }
 162                 __s_api_free2dArray(mapp);
 163         }
 164         if (attr_mapped == 0)
 165                 /* No attribute mapping. Don't bother to reconstruct RDN */
 166                 goto cleanup;
 167 
 168         len = 0;
 169         /* Reconstruct RDN from type=value pairs */
 170         for (i = 0; i < nAttr; i++) {
 171                 if (mapped_attrs[i])
 172                         len += strlen(mapped_attrs[i]);
 173                 else
 174                         len += strlen(attrs[i]);
 175                 /* Add 1 for "+" */
 176                 len++;
 177         }
 178         if ((new_rdn = (char *)calloc(1, ++len)) == NULL)
 179                 goto cleanup;
 180         for (i = 0; i < nAttr; i++) {
 181                 if (i > 0)
 182                         /* Add seperator */
 183                         (void) strlcat(new_rdn, "+", len);
 184 
 185                 if (mapped_attrs[i])
 186                         (void) strlcat(new_rdn, mapped_attrs[i], len);
 187                 else
 188                         (void) strlcat(new_rdn, attrs[i], len);
 189 
 190         }
 191 cleanup:
 192         ldap_value_free(attrs);
 193         if (mapped_attrs) {
 194                 if (attr_mapped) {
 195                         for (i = 0; i < nAttr; i++) {
 196                                 if (mapped_attrs[i])
 197                                         free(mapped_attrs[i]);
 198                         }
 199                 }
 200                 free(mapped_attrs);
 201         }
 202 
 203         return (new_rdn);
 204 }
 205 /*
 206  * Convert attribute type in a DN that has an attribute mapping to the
 207  * original mappped type.
 208  * e.g
 209  * The mappings are cn<->cn-sm, iphostnumber<->iphostnumber-sm
 210  *
 211  * dn: cn-sm=aaa+iphostnumber-sm=9.9.9.9,dc=central,dc=sun,dc=com
 212  * is converted to
 213  * dn: cn=aaa+iphostnumber=9.9.9.9,dc=central,dc=sun,dc=com
 214  *
 215  * Input - service: e.g. hosts, passwd etc.
 216  *         dn: the value of a distinguished name
 217  * Return - NULL: error
 218  *          non-NULL: A converted DN and the memory is allocated
 219  */
 220 static char *
 221 _cvtDN(const char *service, const char *dn)
 222 {
 223         char    **mapped_rdns;
 224         char    **rdns, *new_rdn, *new_dn = NULL;
 225         int     nRdn = 0, i, len = 0, rdn_mapped;
 226 
 227         if (service == NULL || dn == NULL)
 228                 return (NULL);
 229 
 230         if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
 231                 return (NULL);
 232 
 233         for (nRdn = 0; rdns[nRdn] != NULL; nRdn++)
 234                 ;
 235 
 236         if ((mapped_rdns = (char **)calloc(nRdn, sizeof (char *))) == NULL) {
 237                 ldap_value_free(rdns);
 238                 return (NULL);
 239         }
 240 
 241         rdn_mapped = 0;
 242         /* Break down RDNs in a DN */
 243         for (i = 0; i < nRdn; i++) {
 244                 if ((new_rdn = _cvtRDN(service, rdns[i])) != NULL) {
 245                         mapped_rdns[i] = new_rdn;
 246                         rdn_mapped = 1;
 247                 }
 248         }
 249         if (rdn_mapped == 0) {
 250                 /*
 251                  * No RDN contains any attribute mapping.
 252                  * Don't bother to reconstruct DN from RDN. Copy DN directly.
 253                  */
 254                 new_dn = strdup(dn);
 255                 goto cleanup;
 256         }
 257         /*
 258          * Reconstruct dn from RDNs.
 259          * Calculate the length first.
 260          */
 261         for (i = 0; i < nRdn; i++) {
 262                 if (mapped_rdns[i])
 263                         len += strlen(mapped_rdns[i]);
 264                 else
 265                         len += strlen(rdns[i]);
 266 
 267                 /* add 1 for ',' */
 268                 len ++;
 269         }
 270         if ((new_dn = (char *)calloc(1, ++len)) == NULL)
 271                 goto cleanup;
 272         for (i = 0; i < nRdn; i++) {
 273                 if (i > 0)
 274                         /* Add seperator */
 275                         (void) strlcat(new_dn, ",", len);
 276 
 277                 if (mapped_rdns[i])
 278                         (void) strlcat(new_dn, mapped_rdns[i], len);
 279                 else
 280                         (void) strlcat(new_dn, rdns[i], len);
 281 
 282         }
 283 
 284 cleanup:
 285         ldap_value_free(rdns);
 286         if (mapped_rdns) {
 287                 if (rdn_mapped) {
 288                         for (i = 0; i < nRdn; i++) {
 289                                 if (mapped_rdns[i])
 290                                         free(mapped_rdns[i]);
 291                         }
 292                 }
 293                 free(mapped_rdns);
 294         }
 295 
 296         return (new_dn);
 297 }
 298 /*
 299  * Convert a single ldap entry from a LDAPMessage
 300  * into an ns_ldap_entry structure.
 301  * Schema map the entry if specified in flags
 302  */
 303 
 304 static int
 305 __s_api_cvtEntry(LDAP *ld, const char *service, LDAPMessage *e, int flags,
 306     ns_ldap_entry_t **ret, ns_ldap_error_t **error)
 307 {
 308 
 309         ns_ldap_entry_t *ep = NULL;
 310         ns_ldap_attr_t  **ap = NULL;
 311         BerElement      *ber;
 312         char            *attr = NULL;
 313         char            **vals = NULL;
 314         char            **mapping;
 315         char            *dn;
 316         int             nAttrs = 0;
 317         int             i, j, k = 0;
 318         char            **gecos_mapping = NULL;
 319         int             gecos_val_index[3] = { -1, -1, -1};
 320         char            errstr[MAXERROR];
 321         int             schema_mapping_existed = FALSE;
 322         int             gecos_mapping_existed = FALSE;
 323         int             gecos_attr_matched;
 324         int             auto_service = FALSE;
 325         int             rc = NS_LDAP_SUCCESS;
 326 
 327         if (e == NULL || ret == NULL || error == NULL)
 328                 return (NS_LDAP_INVALID_PARAM);
 329 
 330         *error = NULL;
 331 
 332         ep = (ns_ldap_entry_t *)calloc(1, sizeof (ns_ldap_entry_t));
 333         if (ep == NULL)
 334                 return (NS_LDAP_MEMORY);
 335 
 336         if (service != NULL &&
 337             (strncasecmp(service, "auto_", 5) == 0 ||
 338             strcasecmp(service, "automount") == 0))
 339                 auto_service = TRUE;
 340         /*
 341          * see if schema mapping existed for the given service
 342          */
 343         mapping = __ns_ldap_getOrigAttribute(service,
 344             NS_HASH_SCHEMA_MAPPING_EXISTED);
 345         if (mapping) {
 346                 schema_mapping_existed = TRUE;
 347                 __s_api_free2dArray(mapping);
 348                 mapping = NULL;
 349         } else if (auto_service) {
 350                 /*
 351                  * If service == auto_* and no
 352                  * schema mapping found
 353                  * then try automount
 354                  * There is certain case that schema mapping exist
 355                  * but __ns_ldap_getOrigAttribute(service,
 356                  *      NS_HASH_SCHEMA_MAPPING_EXISTED);
 357                  * returns NULL.
 358                  * e.g.
 359                  * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
 360                  * NS_LDAP_OBJECTCLASSMAP = automount:automountMap=MynisMap
 361                  * NS_LDAP_OBJECTCLASSMAP = automount:automount=MynisObject
 362                  *
 363                  * Make a check for schema_mapping_existed here
 364                  * so later on __s_api_convert_automountmapname won't be called
 365                  * unnecessarily. It is also used for attribute mapping
 366                  * and objectclass mapping.
 367                  */
 368                 mapping = __ns_ldap_getOrigAttribute("automount",
 369                     NS_HASH_SCHEMA_MAPPING_EXISTED);
 370                 if (mapping) {
 371                         schema_mapping_existed = TRUE;
 372                         __s_api_free2dArray(mapping);
 373                         mapping = NULL;
 374                 }
 375         }
 376 
 377         nAttrs = 1;  /* start with 1 for the DN attr */
 378         for (attr = ldap_first_attribute(ld, e, &ber); attr != NULL;
 379             attr = ldap_next_attribute(ld, e, ber)) {
 380                 nAttrs++;
 381                 ldap_memfree(attr);
 382                 attr = NULL;
 383         }
 384         ber_free(ber, 0);
 385         ber = NULL;
 386 
 387         ep->attr_count = nAttrs;
 388 
 389         /*
 390          * add 1 for "gecos" 1 to N attribute mapping,
 391          * just in case it is needed.
 392          * ep->attr_count will be updated later if that is true.
 393          */
 394         ap = (ns_ldap_attr_t **)calloc(ep->attr_count + 1,
 395             sizeof (ns_ldap_attr_t *));
 396         if (ap == NULL) {
 397                 __ns_ldap_freeEntry(ep);
 398                 ep = NULL;
 399                 return (NS_LDAP_MEMORY);
 400         }
 401         ep->attr_pair = ap;
 402 
 403         /* DN attribute */
 404         dn = ldap_get_dn(ld, e);
 405         ap[0] = (ns_ldap_attr_t *)calloc(1, sizeof (ns_ldap_attr_t));
 406         if (ap[0] == NULL) {
 407                 ldap_memfree(dn);
 408                 dn = NULL;
 409                 __ns_ldap_freeEntry(ep);
 410                 ep = NULL;
 411                 return (NS_LDAP_MEMORY);
 412         }
 413 
 414         if ((ap[0]->attrname = strdup("dn")) == NULL) {
 415                 ldap_memfree(dn);
 416                 dn = NULL;
 417                 __ns_ldap_freeEntry(ep);
 418                 ep = NULL;
 419                 return (NS_LDAP_INVALID_PARAM);
 420         }
 421         ap[0]->value_count = 1;
 422         if ((ap[0]->attrvalue = (char **)
 423             calloc(2, sizeof (char *))) == NULL) {
 424                 ldap_memfree(dn);
 425                 dn = NULL;
 426                 __ns_ldap_freeEntry(ep);
 427                 ep = NULL;
 428                 return (NS_LDAP_MEMORY);
 429         }
 430 
 431         if (schema_mapping_existed && ((flags & NS_LDAP_NOT_CVT_DN) == 0))
 432                 ap[0]->attrvalue[0] = _cvtDN(service, dn);
 433         else
 434                 ap[0]->attrvalue[0] = strdup(dn);
 435 
 436         if (ap[0]->attrvalue[0] == NULL) {
 437                 ldap_memfree(dn);
 438                 dn = NULL;
 439                 __ns_ldap_freeEntry(ep);
 440                 ep = NULL;
 441                 return (NS_LDAP_MEMORY);
 442         }
 443         ldap_memfree(dn);
 444         dn = NULL;
 445 
 446         if ((flags & NS_LDAP_NOMAP) == 0 && auto_service &&
 447             schema_mapping_existed) {
 448                 rc = __s_api_convert_automountmapname(service,
 449                     &ap[0]->attrvalue[0],
 450                     error);
 451                 if (rc != NS_LDAP_SUCCESS) {
 452                         __ns_ldap_freeEntry(ep);
 453                         ep = NULL;
 454                         return (rc);
 455                 }
 456         }
 457 
 458         /* other attributes */
 459         for (attr = ldap_first_attribute(ld, e, &ber), j = 1;
 460             attr != NULL && j != nAttrs;
 461             attr = ldap_next_attribute(ld, e, ber), j++) {
 462                 /* allocate new attr name */
 463 
 464                 if ((ap[j] = (ns_ldap_attr_t *)
 465                     calloc(1, sizeof (ns_ldap_attr_t))) == NULL) {
 466                         ber_free(ber, 0);
 467                         ber = NULL;
 468                         __ns_ldap_freeEntry(ep);
 469                         ep = NULL;
 470                         if (gecos_mapping)
 471                                 __s_api_free2dArray(gecos_mapping);
 472                         gecos_mapping = NULL;
 473                         return (NS_LDAP_MEMORY);
 474                 }
 475 
 476                 if ((flags & NS_LDAP_NOMAP) || schema_mapping_existed == FALSE)
 477                         mapping = NULL;
 478                 else
 479                         mapping = __ns_ldap_getOrigAttribute(service, attr);
 480 
 481                 if (mapping == NULL && auto_service &&
 482                     schema_mapping_existed && (flags & NS_LDAP_NOMAP) == 0)
 483                         /*
 484                          * if service == auto_* and no schema mapping found
 485                          * and schema_mapping_existed is TRUE and NS_LDAP_NOMAP
 486                          * is not set then try automount e.g.
 487                          * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
 488                          */
 489                         mapping = __ns_ldap_getOrigAttribute("automount",
 490                             attr);
 491 
 492                 if (mapping == NULL) {
 493                         if ((ap[j]->attrname = strdup(attr)) == NULL) {
 494                                 ber_free(ber, 0);
 495                                 ber = NULL;
 496                                 __ns_ldap_freeEntry(ep);
 497                                 ep = NULL;
 498                                 if (gecos_mapping)
 499                                         __s_api_free2dArray(gecos_mapping);
 500                                 gecos_mapping = NULL;
 501                                 return (NS_LDAP_MEMORY);
 502                         }
 503                 } else {
 504                         /*
 505                          * for "gecos" 1 to N mapping,
 506                          * do not remove the mapped attribute,
 507                          * just create a new gecos attribute
 508                          * and append it to the end of the attribute list
 509                          */
 510                         if (strcasecmp(mapping[0], "gecos") == 0) {
 511                                 ap[j]->attrname = strdup(attr);
 512                                 gecos_mapping_existed = TRUE;
 513                         } else {
 514                                 ap[j]->attrname = strdup(mapping[0]);
 515                         }
 516 
 517                         if (ap[j]->attrname == NULL) {
 518                                 ber_free(ber, 0);
 519                                 ber = NULL;
 520                                 __ns_ldap_freeEntry(ep);
 521                                 ep = NULL;
 522                                 if (gecos_mapping)
 523                                         __s_api_free2dArray(gecos_mapping);
 524                                 gecos_mapping = NULL;
 525                                 return (NS_LDAP_MEMORY);
 526                         }
 527                         /*
 528                          * 1 to N attribute mapping processing
 529                          * is only done for "gecos"
 530                          */
 531 
 532                         if (strcasecmp(mapping[0], "gecos") == 0) {
 533                                 /*
 534                                  * get attribute mapping for "gecos",
 535                                  * need to know the number and order of the
 536                                  * mapped attributes
 537                                  */
 538                                 if (gecos_mapping == NULL) {
 539                                         gecos_mapping =
 540                                             __ns_ldap_getMappedAttributes(
 541                                             service, mapping[0]);
 542                                         if (gecos_mapping == NULL ||
 543                                             gecos_mapping[0] == NULL) {
 544                                                 /*
 545                                                  * this should never happens,
 546                                                  * syslog the error
 547                                                  */
 548                                                 (void) sprintf(errstr,
 549                                                     gettext(
 550                                                     "Attribute mapping "
 551                                                     "inconsistency "
 552                                                     "found for attributes "
 553                                                     "'%s' and '%s'."),
 554                                                     mapping[0], attr);
 555                                                 syslog(LOG_ERR, "libsldap: %s",
 556                                                     errstr);
 557 
 558                                                 ber_free(ber, 0);
 559                                                 ber = NULL;
 560                                                 __ns_ldap_freeEntry(ep);
 561                                                 ep = NULL;
 562                                                 __s_api_free2dArray(mapping);
 563                                                 mapping = NULL;
 564                                                 if (gecos_mapping)
 565                                                         __s_api_free2dArray(
 566                                                             gecos_mapping);
 567                                                 gecos_mapping = NULL;
 568                                                 return (NS_LDAP_INTERNAL);
 569                                         }
 570                                 }
 571 
 572                                 /*
 573                                  * is this attribute the 1st, 2nd, or
 574                                  * 3rd attr in the mapping list?
 575                                  */
 576                                 gecos_attr_matched = FALSE;
 577                                 for (i = 0; i < 3 && gecos_mapping[i]; i++) {
 578                                         if (gecos_mapping[i] &&
 579                                             strcasecmp(gecos_mapping[i],
 580                                             attr) == 0) {
 581                                                 gecos_val_index[i] = j;
 582                                                 gecos_attr_matched = TRUE;
 583                                                 break;
 584                                         }
 585                                 }
 586                                 if (gecos_attr_matched == FALSE) {
 587                                         /*
 588                                          * Not match found.
 589                                          * This should never happens,
 590                                          * syslog the error
 591                                          */
 592                                         (void) sprintf(errstr,
 593                                             gettext(
 594                                             "Attribute mapping "
 595                                             "inconsistency "
 596                                             "found for attributes "
 597                                             "'%s' and '%s'."),
 598                                             mapping[0], attr);
 599                                         syslog(LOG_ERR, "libsldap: %s", errstr);
 600 
 601                                         ber_free(ber, 0);
 602                                         ber = NULL;
 603                                         __ns_ldap_freeEntry(ep);
 604                                         ep = NULL;
 605                                         __s_api_free2dArray(mapping);
 606                                         mapping = NULL;
 607                                         __s_api_free2dArray(gecos_mapping);
 608                                         gecos_mapping = NULL;
 609                                         return (NS_LDAP_INTERNAL);
 610                                 }
 611                         }
 612                         __s_api_free2dArray(mapping);
 613                         mapping = NULL;
 614                 }
 615 
 616                 if ((vals = ldap_get_values(ld, e, attr)) != NULL) {
 617 
 618                         if ((ap[j]->value_count =
 619                             ldap_count_values(vals)) == 0) {
 620                                 ldap_value_free(vals);
 621                                 vals = NULL;
 622                                 continue;
 623                         } else {
 624                                 ap[j]->attrvalue = (char **)
 625                                     calloc(ap[j]->value_count+1,
 626                                     sizeof (char *));
 627                                 if (ap[j]->attrvalue == NULL) {
 628                                         ber_free(ber, 0);
 629                                         ber = NULL;
 630                                         __ns_ldap_freeEntry(ep);
 631                                         ep = NULL;
 632                                         if (gecos_mapping)
 633                                                 __s_api_free2dArray(
 634                                                     gecos_mapping);
 635                                         gecos_mapping = NULL;
 636                                         return (NS_LDAP_MEMORY);
 637                                 }
 638                         }
 639 
 640                         /* map object classes if necessary */
 641                         if ((flags & NS_LDAP_NOMAP) == 0 &&
 642                             schema_mapping_existed && ap[j]->attrname &&
 643                             strcasecmp(ap[j]->attrname, "objectclass") == 0) {
 644                                 for (k = 0; k < ap[j]->value_count; k++) {
 645                                         mapping =
 646                                             __ns_ldap_getOrigObjectClass(
 647                                             service, vals[k]);
 648 
 649                                         if (mapping == NULL && auto_service)
 650                                                 /*
 651                                                  * if service == auto_* and no
 652                                                  * schema mapping found
 653                                                  * then try automount
 654                                                  */
 655                                         mapping =
 656                                             __ns_ldap_getOrigObjectClass(
 657                                             "automount", vals[k]);
 658 
 659                                         if (mapping == NULL) {
 660                                                 ap[j]->attrvalue[k] =
 661                                                     strdup(vals[k]);
 662                                         } else {
 663                                                 ap[j]->attrvalue[k] =
 664                                                     strdup(mapping[0]);
 665                                                 __s_api_free2dArray(mapping);
 666                                                 mapping = NULL;
 667                                         }
 668                                         if (ap[j]->attrvalue[k] == NULL) {
 669                                                 ber_free(ber, 0);
 670                                                 ber = NULL;
 671                                                 __ns_ldap_freeEntry(ep);
 672                                                 ep = NULL;
 673                                                 if (gecos_mapping)
 674                                                         __s_api_free2dArray(
 675                                                             gecos_mapping);
 676                                                 gecos_mapping = NULL;
 677                                                 return (NS_LDAP_MEMORY);
 678                                         }
 679                                 }
 680                         } else {
 681                                 for (k = 0; k < ap[j]->value_count; k++) {
 682                                         if ((ap[j]->attrvalue[k] =
 683                                             strdup(vals[k])) == NULL) {
 684                                                 ber_free(ber, 0);
 685                                                 ber = NULL;
 686                                                 __ns_ldap_freeEntry(ep);
 687                                                 ep = NULL;
 688                                                 if (gecos_mapping)
 689                                                         __s_api_free2dArray(
 690                                                             gecos_mapping);
 691                                                 gecos_mapping = NULL;
 692                                                 return (NS_LDAP_MEMORY);
 693                                         }
 694                                 }
 695                         }
 696 
 697                         ap[j]->attrvalue[k] = NULL;
 698                         ldap_value_free(vals);
 699                         vals = NULL;
 700                 }
 701 
 702                 ldap_memfree(attr);
 703                 attr = NULL;
 704         }
 705 
 706         ber_free(ber, 0);
 707         ber = NULL;
 708 
 709         if (gecos_mapping) {
 710                 __s_api_free2dArray(gecos_mapping);
 711                 gecos_mapping = NULL;
 712         }
 713 
 714         /* special processing for gecos 1 to up to 3 attribute mapping */
 715         if (schema_mapping_existed && gecos_mapping_existed) {
 716 
 717                 int     f = -1;
 718 
 719                 for (i = 0; i < 3; i++) {
 720                         k = gecos_val_index[i];
 721 
 722                         /*
 723                          * f is the index of the first returned
 724                          * attribute which "gecos" attribute mapped to
 725                          */
 726                         if (k != -1 && f == -1)
 727                                 f = k;
 728 
 729                         if (k != -1 && ap[k]->value_count > 0 &&
 730                             ap[k]->attrvalue[0] &&
 731                             strlen(ap[k]->attrvalue[0]) > 0) {
 732 
 733                                 if (k == f) {
 734                                         /*
 735                                          * Create and fill in the last reserved
 736                                          * ap with the data from the "gecos"
 737                                          * mapping attributes
 738                                          */
 739                                         ap[nAttrs] = (ns_ldap_attr_t *)
 740                                             calloc(1,
 741                                             sizeof (ns_ldap_attr_t));
 742                                         if (ap[nAttrs] == NULL) {
 743                                                 __ns_ldap_freeEntry(ep);
 744                                                 ep = NULL;
 745                                                 return (NS_LDAP_MEMORY);
 746                                         }
 747                                         ap[nAttrs]->attrvalue = (char **)calloc(
 748                                             2, sizeof (char *));
 749                                         if (ap[nAttrs]->attrvalue == NULL) {
 750                                                 __ns_ldap_freeEntry(ep);
 751                                                 ep = NULL;
 752                                                 return (NS_LDAP_MEMORY);
 753                                         }
 754                                         /* add 1 more for a possible "," */
 755                                         ap[nAttrs]->attrvalue[0] =
 756                                             (char *)calloc(
 757                                             strlen(ap[f]->attrvalue[0]) +
 758                                             2, 1);
 759                                         if (ap[nAttrs]->attrvalue[0] == NULL) {
 760                                                 __ns_ldap_freeEntry(ep);
 761                                                 ep = NULL;
 762                                                 return (NS_LDAP_MEMORY);
 763                                         }
 764                                         (void) strcpy(ap[nAttrs]->attrvalue[0],
 765                                             ap[f]->attrvalue[0]);
 766 
 767                                         ap[nAttrs]->attrname = strdup("gecos");
 768                                         if (ap[nAttrs]->attrname == NULL) {
 769                                                 __ns_ldap_freeEntry(ep);
 770                                                 ep = NULL;
 771                                                 return (NS_LDAP_MEMORY);
 772                                         }
 773 
 774                                         ap[nAttrs]->value_count = 1;
 775                                         ep->attr_count = nAttrs + 1;
 776 
 777                                 } else {
 778                                         char    *tmp = NULL;
 779 
 780                                         /*
 781                                          * realloc to add "," and
 782                                          * ap[k]->attrvalue[0]
 783                                          */
 784                                         tmp = (char *)realloc(
 785                                             ap[nAttrs]->attrvalue[0],
 786                                             strlen(ap[nAttrs]->
 787                                             attrvalue[0]) +
 788                                             strlen(ap[k]->
 789                                             attrvalue[0]) + 2);
 790                                         if (tmp == NULL) {
 791                                                 __ns_ldap_freeEntry(ep);
 792                                                 ep = NULL;
 793                                                 return (NS_LDAP_MEMORY);
 794                                         }
 795                                         ap[nAttrs]->attrvalue[0] = tmp;
 796                                         (void) strcat(ap[nAttrs]->attrvalue[0],
 797                                             ",");
 798                                         (void) strcat(ap[nAttrs]->attrvalue[0],
 799                                             ap[k]->attrvalue[0]);
 800                                 }
 801                         }
 802                 }
 803         }
 804 
 805         *ret = ep;
 806         return (NS_LDAP_SUCCESS);
 807 }
 808 
 809 static int
 810 __s_api_getEntry(ns_ldap_cookie_t *cookie)
 811 {
 812         ns_ldap_entry_t *curEntry = NULL;
 813         int             ret;
 814 
 815 #ifdef DEBUG
 816         (void) fprintf(stderr, "__s_api_getEntry START\n");
 817 #endif
 818 
 819         if (cookie->resultMsg == NULL) {
 820                 return (NS_LDAP_INVALID_PARAM);
 821         }
 822         ret = __s_api_cvtEntry(cookie->conn->ld, cookie->service,
 823             cookie->resultMsg, cookie->i_flags,
 824             &curEntry, &cookie->errorp);
 825         if (ret != NS_LDAP_SUCCESS) {
 826                 return (ret);
 827         }
 828 
 829         if (cookie->result == NULL) {
 830                 cookie->result = (ns_ldap_result_t *)
 831                     calloc(1, sizeof (ns_ldap_result_t));
 832                 if (cookie->result == NULL) {
 833                         __ns_ldap_freeEntry(curEntry);
 834                         curEntry = NULL;
 835                         return (NS_LDAP_MEMORY);
 836                 }
 837                 cookie->result->entry = curEntry;
 838                 cookie->nextEntry = curEntry;
 839         } else {
 840                 cookie->nextEntry->next = curEntry;
 841                 cookie->nextEntry = curEntry;
 842         }
 843         cookie->result->entries_count++;
 844 
 845         return (NS_LDAP_SUCCESS);
 846 }
 847 
 848 static int
 849 __s_api_get_cachemgr_data(const char *type, const char *from, char **to)
 850 {
 851         union {
 852                 ldap_data_t     s_d;
 853                 char            s_b[DOORBUFFERSIZE];
 854         } space;
 855         ldap_data_t     *sptr;
 856         int             ndata;
 857         int             adata;
 858         int             rc;
 859 
 860 #ifdef DEBUG
 861         (void) fprintf(stderr, "__s_api_get_cachemgr_data START\n");
 862 #endif
 863         /*
 864          * We are not going to perform DN to domain mapping
 865          * in the Standalone mode
 866          */
 867         if (__s_api_isStandalone()) {
 868                 return (-1);
 869         }
 870 
 871         if (from == NULL || from[0] == '\0' || to == NULL)
 872                 return (-1);
 873 
 874         *to = NULL;
 875         (void) memset(space.s_b, 0, DOORBUFFERSIZE);
 876 
 877         space.s_d.ldap_call.ldap_callnumber = GETCACHE;
 878         (void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
 879             DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
 880             "%s%s%s",
 881             type,
 882             DOORLINESEP,
 883             from);
 884         ndata = sizeof (space);
 885         adata = sizeof (ldap_call_t) +
 886             strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
 887         sptr = &space.s_d;
 888 
 889         rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
 890         if (rc != NS_CACHE_SUCCESS)
 891                 return (-1);
 892         else
 893                 *to = strdup(sptr->ldap_ret.ldap_u.buff);
 894         return (NS_LDAP_SUCCESS);
 895 }
 896 
 897 static int
 898 __s_api_set_cachemgr_data(const char *type, const char *from, const char *to)
 899 {
 900         union {
 901                 ldap_data_t     s_d;
 902                 char            s_b[DOORBUFFERSIZE];
 903         } space;
 904         ldap_data_t     *sptr;
 905         int             ndata;
 906         int             adata;
 907         int             rc;
 908 
 909 #ifdef DEBUG
 910         (void) fprintf(stderr, "__s_api_set_cachemgr_data START\n");
 911 #endif
 912         /*
 913          * We are not going to perform DN to domain mapping
 914          * in the Standalone mode
 915          */
 916         if (__s_api_isStandalone()) {
 917                 return (-1);
 918         }
 919 
 920         if ((from == NULL) || (from[0] == '\0') ||
 921             (to == NULL) || (to[0] == '\0'))
 922                 return (-1);
 923 
 924         (void) memset(space.s_b, 0, DOORBUFFERSIZE);
 925 
 926         space.s_d.ldap_call.ldap_callnumber = SETCACHE;
 927         (void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
 928             DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
 929             "%s%s%s%s%s",
 930             type,
 931             DOORLINESEP,
 932             from,
 933             DOORLINESEP,
 934             to);
 935 
 936         ndata = sizeof (space);
 937         adata = sizeof (ldap_call_t) +
 938             strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
 939         sptr = &space.s_d;
 940 
 941         rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
 942         if (rc != NS_CACHE_SUCCESS)
 943                 return (-1);
 944 
 945         return (NS_LDAP_SUCCESS);
 946 }
 947 
 948 
 949 static char *
 950 __s_api_remove_rdn_space(char *rdn)
 951 {
 952         char    *tf, *tl, *vf, *vl, *eqsign;
 953 
 954         /* if no space(s) to remove, return */
 955         if (strchr(rdn, SPACETOK) == NULL)
 956                 return (rdn);
 957 
 958         /* if no '=' separator, return */
 959         eqsign = strchr(rdn, '=');
 960         if (eqsign == NULL)
 961                 return (rdn);
 962 
 963         tf = rdn;
 964         tl = eqsign - 1;
 965         vf = eqsign + 1;
 966         vl = rdn + strlen(rdn) - 1;
 967 
 968         /* now two strings, type and value */
 969         *eqsign = '\0';
 970 
 971         /* remove type's leading spaces */
 972         while (tf < tl && *tf == SPACETOK)
 973                 tf++;
 974         /* remove type's trailing spaces */
 975         while (tf < tl && *tl == SPACETOK)
 976                 tl--;
 977         /* add '=' separator back */
 978         *(++tl) = '=';
 979         /* remove value's leading spaces */
 980         while (vf < vl && *vf == SPACETOK)
 981                 vf++;
 982         /* remove value's trailing spaces */
 983         while (vf < vl && *vl == SPACETOK)
 984                 *vl-- = '\0';
 985 
 986         /* move value up if necessary */
 987         if (vf != tl + 1)
 988                 (void) strcpy(tl + 1, vf);
 989 
 990         return (tf);
 991 }
 992 
 993 static
 994 ns_ldap_cookie_t *
 995 init_search_state_machine()
 996 {
 997         ns_ldap_cookie_t        *cookie;
 998         ns_config_t             *cfg;
 999 
1000         cookie = (ns_ldap_cookie_t *)calloc(1, sizeof (ns_ldap_cookie_t));
1001         if (cookie == NULL)
1002                 return (NULL);
1003         cookie->state = INIT;
1004         /* assign other state variables */
1005         cfg = __s_api_loadrefresh_config();
1006         cookie->connectionId = -1;
1007         if (cfg == NULL ||
1008             cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_ptype == NS_UNKNOWN) {
1009                 cookie->search_timeout.tv_sec = NS_DEFAULT_SEARCH_TIMEOUT;
1010         } else {
1011                 cookie->search_timeout.tv_sec =
1012                     cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_i;
1013         }
1014         if (cfg != NULL)
1015                 __s_api_release_config(cfg);
1016         cookie->search_timeout.tv_usec = 0;
1017 
1018         return (cookie);
1019 }
1020 
1021 static void
1022 delete_search_cookie(ns_ldap_cookie_t *cookie)
1023 {
1024         if (cookie == NULL)
1025                 return;
1026         if (cookie->connectionId > -1)
1027                 DropConnection(cookie->connectionId, cookie->i_flags);
1028         if (cookie->filter)
1029                 free(cookie->filter);
1030         if (cookie->i_filter)
1031                 free(cookie->i_filter);
1032         if (cookie->service)
1033                 free(cookie->service);
1034         if (cookie->sdlist)
1035                 (void) __ns_ldap_freeSearchDescriptors(&(cookie->sdlist));
1036         if (cookie->result)
1037                 (void) __ns_ldap_freeResult(&cookie->result);
1038         if (cookie->attribute)
1039                 __s_api_free2dArray(cookie->attribute);
1040         if (cookie->errorp)
1041                 (void) __ns_ldap_freeError(&cookie->errorp);
1042         if (cookie->reflist)
1043                 __s_api_deleteRefInfo(cookie->reflist);
1044         if (cookie->basedn)
1045                 free(cookie->basedn);
1046         if (cookie->ctrlCookie)
1047                 ber_bvfree(cookie->ctrlCookie);
1048         _freeControlList(&cookie->p_serverctrls);
1049         if (cookie->resultctrl)
1050                 ldap_controls_free(cookie->resultctrl);
1051         free(cookie);
1052 }
1053 
1054 static int
1055 get_mapped_filter(ns_ldap_cookie_t *cookie, char **new_filter)
1056 {
1057 
1058         typedef struct  filter_mapping_info {
1059                 char    oc_or_attr;
1060                 char    *name_start;
1061                 char    *name_end;
1062                 char    *veq_pos;
1063                 char    *from_name;
1064                 char    *to_name;
1065                 char    **mapping;
1066         } filter_mapping_info_t;
1067 
1068         char                    *c, *last_copied;
1069         char                    *filter_c, *filter_c_next;
1070         char                    *key, *tail, *head;
1071         char                    errstr[MAXERROR];
1072         int                     num_eq = 0, num_veq = 0;
1073         boolean_t               in_quote = B_FALSE;
1074         boolean_t               is_value = B_FALSE;
1075         int                     i, j, oc_len, len;
1076         boolean_t               at_least_one = B_FALSE;
1077         filter_mapping_info_t   **info, *info1;
1078         char                    **mapping;
1079         char                    *service, *filter, *err;
1080         boolean_t               auto_service = B_FALSE;
1081 
1082         if (cookie == NULL || new_filter == NULL)
1083                 return (NS_LDAP_INVALID_PARAM);
1084 
1085         *new_filter = NULL;
1086         service = cookie->service;
1087         filter = cookie->filter;
1088 
1089         /*
1090          * count the number of '=' char
1091          */
1092         for (c = filter; *c; c++) {
1093                 if (*c == TOKENSEPARATOR)
1094                         num_eq++;
1095         }
1096 
1097         if (service != NULL && strncasecmp(service, "auto_", 5) == 0)
1098                 auto_service = TRUE;
1099 
1100         /*
1101          * See if schema mapping existed for the given service.
1102          * If not, just return success.
1103          */
1104         mapping = __ns_ldap_getOrigAttribute(service,
1105             NS_HASH_SCHEMA_MAPPING_EXISTED);
1106 
1107         if (mapping == NULL && auto_service)
1108                 /*
1109                  * if service == auto_* and no
1110                  * schema mapping found
1111                  * then try automount
1112                  */
1113                 mapping = __ns_ldap_getOrigAttribute(
1114                     "automount", NS_HASH_SCHEMA_MAPPING_EXISTED);
1115 
1116         if (mapping)
1117                 __s_api_free2dArray(mapping);
1118         else
1119                 return (NS_LDAP_SUCCESS);
1120 
1121         /*
1122          * no '=' sign, just say OK and return nothing
1123          */
1124         if (num_eq == 0)
1125                 return (NS_LDAP_SUCCESS);
1126 
1127         /*
1128          * Make a copy of the filter string
1129          * for saving the name of the objectclasses or
1130          * attributes that need to be passed to the
1131          * objectclass or attribute mapping functions.
1132          * pointer "info->from_name" points to the locations
1133          * within this string.
1134          *
1135          * The input filter string, filter, will be used
1136          * to indicate where these names start and end.
1137          * pointers "info->name_start" and "info->name_end"
1138          * point to locations within the input filter string,
1139          * and are used at the end of this function to
1140          * merge the original filter data with the
1141          * mapped objectclass or attribute names.
1142          */
1143         filter_c = strdup(filter);
1144         if (filter_c == NULL)
1145                 return (NS_LDAP_MEMORY);
1146         filter_c_next = filter_c;
1147 
1148         /*
1149          * get memory for info arrays
1150          */
1151         info = (filter_mapping_info_t **)calloc(num_eq + 1,
1152             sizeof (filter_mapping_info_t *));
1153 
1154         if (info == NULL) {
1155                 free(filter_c);
1156                 return (NS_LDAP_MEMORY);
1157         }
1158 
1159         /*
1160          * find valid '=' for further processing,
1161          * ignore the "escaped =" (.i.e. "\="), or
1162          * "=" in quoted string
1163          */
1164         for (c = filter_c; *c; c++) {
1165 
1166                 switch (*c) {
1167                 case TOKENSEPARATOR:
1168                         if (!in_quote && !is_value) {
1169                                 info1 = (filter_mapping_info_t *)calloc(1,
1170                                     sizeof (filter_mapping_info_t));
1171                                 if (info1 == NULL) {
1172                                         free(filter_c);
1173                                         for (i = 0; i < num_veq; i++)
1174                                                 free(info[i]);
1175                                         free(info);
1176                                         return (NS_LDAP_MEMORY);
1177                                 }
1178                                 info[num_veq] = info1;
1179 
1180                                 /*
1181                                  * remember the location of this "="
1182                                  */
1183                                 info[num_veq++]->veq_pos = c;
1184 
1185                                 /*
1186                                  * skip until the end of the attribute value
1187                                  */
1188                                 is_value = B_TRUE;
1189                         }
1190                         break;
1191                 case CPARATOK:
1192                         /*
1193                          * mark the end of the attribute value
1194                          */
1195                         if (!in_quote)
1196                                 is_value = B_FALSE;
1197                         break;
1198                 case QUOTETOK:
1199                         /*
1200                          * switch on/off the in_quote mode
1201                          */
1202                         in_quote = (in_quote == B_FALSE);
1203                         break;
1204                 case '\\':
1205                         /*
1206                          * ignore escape characters
1207                          * don't skip if next char is '\0'
1208                          */
1209                         if (!in_quote)
1210                                 if (*(++c) == '\0')
1211                                         c--;
1212                         break;
1213                 }
1214 
1215         }
1216 
1217         /*
1218          * for each valid "=" found, get the name to
1219          * be mapped
1220          */
1221         oc_len = strlen("objectclass");
1222         for (i = 0; i < num_veq; i++) {
1223 
1224                 /*
1225                  * look at the left side of "=" to see
1226                  * if assertion is "objectclass=<ocname>"
1227                  * or "<attribute name>=<attribute value>"
1228                  *
1229                  * first skip spaces before "=".
1230                  * Note that filter_c_next may not point to the
1231                  * start of the filter string. For i > 0,
1232                  * it points to the end of the last name processed + 2
1233                  */
1234                 for (tail = info[i]->veq_pos; (tail > filter_c_next) &&
1235                     (*(tail - 1) == SPACETOK); tail--)
1236                         ;
1237 
1238                 /*
1239                  * mark the end of the left side string (the key)
1240                  */
1241                 *tail = '\0';
1242                 info[i]->name_end = tail - filter_c - 1 + filter;
1243 
1244                 /*
1245                  * find the start of the key
1246                  */
1247                 key = filter_c_next;
1248                 for (c = tail; filter_c_next <= c; c--) {
1249                         /* OPARATOK is '(' */
1250                         if (*c == OPARATOK ||
1251                             *c == SPACETOK) {
1252                                 key = c + 1;
1253                                 break;
1254                         }
1255                 }
1256                 info[i]->name_start = key - filter_c + filter;
1257 
1258                 if ((key + oc_len) <= tail) {
1259                         if (strncasecmp(key, "objectclass",
1260                             oc_len) == 0) {
1261                                 /*
1262                                  * assertion is "objectclass=ocname",
1263                                  * ocname is the one needs to be mapped
1264                                  *
1265                                  * skip spaces after "=" to find start
1266                                  * of the ocname
1267                                  */
1268                                 head = info[i]->veq_pos;
1269                                 for (head = info[i]->veq_pos + 1;
1270                                     *head && *head == SPACETOK; head++)
1271                                         ;
1272 
1273                                 /* ignore empty ocname */
1274                                 if (!(*head))
1275                                         continue;
1276 
1277                                 info[i]->name_start = head - filter_c +
1278                                     filter;
1279 
1280                                 /*
1281                                  * now find the end of the ocname
1282                                  */
1283                                 for (c = head; ; c++) {
1284                                         /* CPARATOK is ')' */
1285                                         if (*c == CPARATOK ||
1286                                             *c == '\0' ||
1287                                             *c == SPACETOK) {
1288                                                 *c = '\0';
1289                                                 info[i]->name_end =
1290                                                     c - filter_c - 1 +
1291                                                     filter;
1292                                                 filter_c_next = c + 1;
1293                                                 info[i]->oc_or_attr = 'o';
1294                                                 info[i]->from_name = head;
1295                                                 break;
1296                                         }
1297                                 }
1298                         }
1299                 }
1300 
1301                 /*
1302                  * assertion is not "objectclass=ocname",
1303                  * assume assertion is "<key> = <value>",
1304                  * <key> is the one needs to be mapped
1305                  */
1306                 if (info[i]->from_name == NULL && strlen(key) > 0) {
1307                         info[i]->oc_or_attr = 'a';
1308                         info[i]->from_name = key;
1309                 }
1310         }
1311 
1312         /* perform schema mapping */
1313         for (i = 0; i < num_veq; i++) {
1314                 if (info[i]->from_name == NULL)
1315                         continue;
1316 
1317                 if (info[i]->oc_or_attr == 'a')
1318                         info[i]->mapping =
1319                             __ns_ldap_getMappedAttributes(service,
1320                             info[i]->from_name);
1321                 else
1322                         info[i]->mapping =
1323                             __ns_ldap_getMappedObjectClass(service,
1324                             info[i]->from_name);
1325 
1326                 if (info[i]->mapping == NULL && auto_service)  {
1327                         /*
1328                          * If no mapped attribute/objectclass is found
1329                          * and service == auto*
1330                          * try to find automount's
1331                          * mapped attribute/objectclass
1332                          */
1333                         if (info[i]->oc_or_attr == 'a')
1334                                 info[i]->mapping =
1335                                     __ns_ldap_getMappedAttributes("automount",
1336                                     info[i]->from_name);
1337                         else
1338                                 info[i]->mapping =
1339                                     __ns_ldap_getMappedObjectClass("automount",
1340                                     info[i]->from_name);
1341                 }
1342 
1343                 if (info[i]->mapping == NULL ||
1344                     info[i]->mapping[0] == NULL) {
1345                         info[i]->to_name = NULL;
1346                 } else if (info[i]->mapping[1] == NULL) {
1347                         info[i]->to_name = info[i]->mapping[0];
1348                         at_least_one = TRUE;
1349                 } else {
1350                         __s_api_free2dArray(info[i]->mapping);
1351                         /*
1352                          * multiple mapping
1353                          * not allowed
1354                          */
1355                         (void) sprintf(errstr,
1356                             gettext(
1357                             "Multiple attribute or objectclass "
1358                             "mapping for '%s' in filter "
1359                             "'%s' not allowed."),
1360                             info[i]->from_name, filter);
1361                         err = strdup(errstr);
1362                         if (err) {
1363                                 MKERROR(LOG_WARNING, cookie->errorp,
1364                                     NS_CONFIG_SYNTAX,
1365                                     err, NS_LDAP_MEMORY);
1366                         }
1367 
1368                         free(filter_c);
1369                         for (j = 0; j < num_veq; j++) {
1370                                 if (info[j]->mapping)
1371                                         __s_api_free2dArray(
1372                                             info[j]->mapping);
1373                                 free(info[j]);
1374                         }
1375                         free(info);
1376                         return (NS_LDAP_CONFIG);
1377                 }
1378         }
1379 
1380 
1381         if (at_least_one) {
1382 
1383                 len = strlen(filter);
1384                 last_copied = filter - 1;
1385 
1386                 for (i = 0; i < num_veq; i++) {
1387                         if (info[i]->to_name)
1388                                 len += strlen(info[i]->to_name);
1389                 }
1390 
1391                 *new_filter = (char *)calloc(1, len);
1392                 if (*new_filter == NULL) {
1393                         free(filter_c);
1394                         for (j = 0; j < num_veq; j++) {
1395                                 if (info[j]->mapping)
1396                                         __s_api_free2dArray(
1397                                             info[j]->mapping);
1398                                 free(info[j]);
1399                         }
1400                         free(info);
1401                         return (NS_LDAP_MEMORY);
1402                 }
1403 
1404                 for (i = 0; i < num_veq; i++) {
1405                         if (info[i]->to_name != NULL &&
1406                             info[i]->to_name != NULL) {
1407 
1408                                 /*
1409                                  * copy the original filter data
1410                                  * between the last name and current
1411                                  * name
1412                                  */
1413                                 if ((last_copied + 1) != info[i]->name_start)
1414                                         (void) strncat(*new_filter,
1415                                             last_copied + 1,
1416                                             info[i]->name_start -
1417                                             last_copied - 1);
1418 
1419                                 /* the data is copied */
1420                                 last_copied = info[i]->name_end;
1421 
1422                                 /*
1423                                  * replace the name with
1424                                  * the mapped name
1425                                  */
1426                                 (void) strcat(*new_filter, info[i]->to_name);
1427                         }
1428 
1429                         /* copy the filter data after the last name */
1430                         if (i == (num_veq -1) &&
1431                             info[i]->name_end <
1432                             (filter + strlen(filter)))
1433                                 (void) strncat(*new_filter, last_copied + 1,
1434                                     filter + strlen(filter) -
1435                                     last_copied - 1);
1436                 }
1437 
1438         }
1439 
1440         /* free memory */
1441         free(filter_c);
1442         for (j = 0; j < num_veq; j++) {
1443                 if (info[j]->mapping)
1444                         __s_api_free2dArray(info[j]->mapping);
1445                 free(info[j]);
1446         }
1447         free(info);
1448 
1449         return (NS_LDAP_SUCCESS);
1450 }
1451 
1452 static int
1453 setup_next_search(ns_ldap_cookie_t *cookie)
1454 {
1455         ns_ldap_search_desc_t   *dptr;
1456         int                     scope;
1457         char                    *filter, *str;
1458         int                     baselen;
1459         int                     rc;
1460         void                    **param;
1461 
1462         dptr = *cookie->sdpos;
1463         scope = cookie->i_flags & (NS_LDAP_SCOPE_BASE |
1464             NS_LDAP_SCOPE_ONELEVEL |
1465             NS_LDAP_SCOPE_SUBTREE);
1466         if (scope)
1467                 cookie->scope = scope;
1468         else
1469                 cookie->scope = dptr->scope;
1470         switch (cookie->scope) {
1471         case NS_LDAP_SCOPE_BASE:
1472                 cookie->scope = LDAP_SCOPE_BASE;
1473                 break;
1474         case NS_LDAP_SCOPE_ONELEVEL:
1475                 cookie->scope = LDAP_SCOPE_ONELEVEL;
1476                 break;
1477         case NS_LDAP_SCOPE_SUBTREE:
1478                 cookie->scope = LDAP_SCOPE_SUBTREE;
1479                 break;
1480         }
1481 
1482         filter = NULL;
1483         if (cookie->use_filtercb && cookie->init_filter_cb &&
1484             dptr->filter && strlen(dptr->filter) > 0) {
1485                 (*cookie->init_filter_cb)(dptr, &filter,
1486                     cookie->userdata);
1487         }
1488         if (filter == NULL) {
1489                 if (cookie->i_filter == NULL) {
1490                         cookie->err_rc = NS_LDAP_INVALID_PARAM;
1491                         return (-1);
1492                 } else {
1493                         if (cookie->filter)
1494                                 free(cookie->filter);
1495                         cookie->filter = strdup(cookie->i_filter);
1496                         if (cookie->filter == NULL) {
1497                                 cookie->err_rc = NS_LDAP_MEMORY;
1498                                 return (-1);
1499                         }
1500                 }
1501         } else {
1502                 if (cookie->filter)
1503                         free(cookie->filter);
1504                 cookie->filter = strdup(filter);
1505                 free(filter);
1506                 if (cookie->filter == NULL) {
1507                         cookie->err_rc = NS_LDAP_MEMORY;
1508                         return (-1);
1509                 }
1510         }
1511 
1512         /*
1513          * perform attribute/objectclass mapping on filter
1514          */
1515         filter = NULL;
1516 
1517         if (cookie->service) {
1518                 rc = get_mapped_filter(cookie, &filter);
1519                 if (rc != NS_LDAP_SUCCESS) {
1520                         cookie->err_rc = rc;
1521                         return (-1);
1522                 } else {
1523                         /*
1524                          * get_mapped_filter returns
1525                          * NULL filter pointer, if
1526                          * no mapping was done
1527                          */
1528                         if (filter) {
1529                                 free(cookie->filter);
1530                                 cookie->filter = filter;
1531                         }
1532                 }
1533         }
1534 
1535         /*
1536          * validate filter to make sure it's legal
1537          * [remove redundant ()'s]
1538          */
1539         rc = validate_filter(cookie);
1540         if (rc != NS_LDAP_SUCCESS) {
1541                 cookie->err_rc = rc;
1542                 return (-1);
1543         }
1544 
1545         baselen = strlen(dptr->basedn);
1546         if (baselen > 0 && dptr->basedn[baselen-1] == COMMATOK) {
1547                 rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
1548                     (void ***)&param, &cookie->errorp);
1549                 if (rc != NS_LDAP_SUCCESS) {
1550                         cookie->err_rc = rc;
1551                         return (-1);
1552                 }
1553                 str = ((char **)param)[0];
1554                 baselen += strlen(str)+1;
1555                 if (cookie->basedn)
1556                         free(cookie->basedn);
1557                 cookie->basedn = (char *)malloc(baselen);
1558                 if (cookie->basedn == NULL) {
1559                         cookie->err_rc = NS_LDAP_MEMORY;
1560                         return (-1);
1561                 }
1562                 (void) strcpy(cookie->basedn, dptr->basedn);
1563                 (void) strcat(cookie->basedn, str);
1564                 (void) __ns_ldap_freeParam(&param);
1565         } else {
1566                 if (cookie->basedn)
1567                         free(cookie->basedn);
1568                 cookie->basedn = strdup(dptr->basedn);
1569         }
1570         return (0);
1571 }
1572 
1573 static int
1574 setup_referral_search(ns_ldap_cookie_t *cookie)
1575 {
1576         ns_referral_info_t      *ref;
1577 
1578         ref = cookie->refpos;
1579         cookie->scope = ref->refScope;
1580         if (cookie->filter) {
1581                 free(cookie->filter);
1582         }
1583         cookie->filter = strdup(ref->refFilter);
1584         if (cookie->basedn) {
1585                 free(cookie->basedn);
1586         }
1587         cookie->basedn = strdup(ref->refDN);
1588         if (cookie->filter == NULL || cookie->basedn == NULL) {
1589                 cookie->err_rc = NS_LDAP_MEMORY;
1590                 return (-1);
1591         }
1592         return (0);
1593 }
1594 
1595 static int
1596 get_current_session(ns_ldap_cookie_t *cookie)
1597 {
1598         ConnectionID    connectionId = -1;
1599         Connection      *conp = NULL;
1600         int             rc;
1601         int             fail_if_new_pwd_reqd = 1;
1602 
1603         rc = __s_api_getConnection(NULL, cookie->i_flags,
1604             cookie->i_auth, &connectionId, &conp,
1605             &cookie->errorp, fail_if_new_pwd_reqd,
1606             cookie->nopasswd_acct_mgmt, cookie->conn_user);
1607 
1608         /*
1609          * If password control attached in *cookie->errorp,
1610          * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1611          * free the error structure (we do not need
1612          * the sec_to_expired info).
1613          * Reset rc to NS_LDAP_SUCCESS.
1614          */
1615         if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1616                 (void) __ns_ldap_freeError(
1617                     &cookie->errorp);
1618                 cookie->errorp = NULL;
1619                 rc = NS_LDAP_SUCCESS;
1620         }
1621 
1622         if (rc != NS_LDAP_SUCCESS) {
1623                 cookie->err_rc = rc;
1624                 return (-1);
1625         }
1626         cookie->conn = conp;
1627         cookie->connectionId = connectionId;
1628 
1629         return (0);
1630 }
1631 
1632 static int
1633 get_next_session(ns_ldap_cookie_t *cookie)
1634 {
1635         ConnectionID    connectionId = -1;
1636         Connection      *conp = NULL;
1637         int             rc;
1638         int             fail_if_new_pwd_reqd = 1;
1639 
1640         if (cookie->connectionId > -1) {
1641                 DropConnection(cookie->connectionId, cookie->i_flags);
1642                 cookie->connectionId = -1;
1643         }
1644 
1645         /* If using a MT connection, return it. */
1646         if (cookie->conn_user != NULL &&
1647             cookie->conn_user->conn_mt != NULL)
1648                 __s_api_conn_mt_return(cookie->conn_user);
1649 
1650         rc = __s_api_getConnection(NULL, cookie->i_flags,
1651             cookie->i_auth, &connectionId, &conp,
1652             &cookie->errorp, fail_if_new_pwd_reqd,
1653             cookie->nopasswd_acct_mgmt, cookie->conn_user);
1654 
1655         /*
1656          * If password control attached in *cookie->errorp,
1657          * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1658          * free the error structure (we do not need
1659          * the sec_to_expired info).
1660          * Reset rc to NS_LDAP_SUCCESS.
1661          */
1662         if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1663                 (void) __ns_ldap_freeError(
1664                     &cookie->errorp);
1665                 cookie->errorp = NULL;
1666                 rc = NS_LDAP_SUCCESS;
1667         }
1668 
1669         if (rc != NS_LDAP_SUCCESS) {
1670                 cookie->err_rc = rc;
1671                 return (-1);
1672         }
1673         cookie->conn = conp;
1674         cookie->connectionId = connectionId;
1675         return (0);
1676 }
1677 
1678 static int
1679 get_referral_session(ns_ldap_cookie_t *cookie)
1680 {
1681         ConnectionID    connectionId = -1;
1682         Connection      *conp = NULL;
1683         int             rc;
1684         int             fail_if_new_pwd_reqd = 1;
1685 
1686         if (cookie->connectionId > -1) {
1687                 DropConnection(cookie->connectionId, cookie->i_flags);
1688                 cookie->connectionId = -1;
1689         }
1690 
1691         /* set it up to use a connection opened for referral */
1692         if (cookie->conn_user != NULL) {
1693                 /* If using a MT connection, return it. */
1694                 if (cookie->conn_user->conn_mt != NULL)
1695                         __s_api_conn_mt_return(cookie->conn_user);
1696                 cookie->conn_user->referral = B_TRUE;
1697         }
1698 
1699         rc = __s_api_getConnection(cookie->refpos->refHost, 0,
1700             cookie->i_auth, &connectionId, &conp,
1701             &cookie->errorp, fail_if_new_pwd_reqd,
1702             cookie->nopasswd_acct_mgmt, cookie->conn_user);
1703 
1704         /*
1705          * If password control attached in *cookie->errorp,
1706          * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1707          * free the error structure (we do not need
1708          * the sec_to_expired info).
1709          * Reset rc to NS_LDAP_SUCCESS.
1710          */
1711         if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1712                 (void) __ns_ldap_freeError(
1713                     &cookie->errorp);
1714                 cookie->errorp = NULL;
1715                 rc = NS_LDAP_SUCCESS;
1716         }
1717 
1718         if (rc != NS_LDAP_SUCCESS) {
1719                 cookie->err_rc = rc;
1720                 return (-1);
1721         }
1722         cookie->conn = conp;
1723         cookie->connectionId = connectionId;
1724         return (0);
1725 }
1726 
1727 static int
1728 paging_supported(ns_ldap_cookie_t *cookie)
1729 {
1730         int             rc;
1731 
1732         cookie->listType = 0;
1733         rc = __s_api_isCtrlSupported(cookie->conn,
1734             LDAP_CONTROL_VLVREQUEST);
1735         if (rc == NS_LDAP_SUCCESS) {
1736                 cookie->listType = VLVCTRLFLAG;
1737                 return (1);
1738         }
1739         rc = __s_api_isCtrlSupported(cookie->conn,
1740             LDAP_CONTROL_SIMPLE_PAGE);
1741         if (rc == NS_LDAP_SUCCESS) {
1742                 cookie->listType = SIMPLEPAGECTRLFLAG;
1743                 return (1);
1744         }
1745         return (0);
1746 }
1747 
1748 typedef struct servicesorttype {
1749         char *service;
1750         ns_srvsidesort_t type;
1751 } servicesorttype_t;
1752 
1753 static servicesorttype_t *sort_type = NULL;
1754 static int sort_type_size = 0;
1755 static int sort_type_hwm = 0;
1756 static mutex_t sort_type_mutex = DEFAULTMUTEX;
1757 
1758 
1759 static ns_srvsidesort_t
1760 get_srvsidesort_type(char *service)
1761 {
1762         int i;
1763         ns_srvsidesort_t type = SSS_UNKNOWN;
1764 
1765         if (service == NULL)
1766                 return (type);
1767 
1768         (void) mutex_lock(&sort_type_mutex);
1769         if (sort_type != NULL) {
1770                 for (i = 0; i < sort_type_hwm; i++) {
1771                         if (strcmp(sort_type[i].service, service) == 0) {
1772                                 type = sort_type[i].type;
1773                                 break;
1774                         }
1775                 }
1776         }
1777         (void) mutex_unlock(&sort_type_mutex);
1778         return (type);
1779 }
1780 
1781 static void
1782 update_srvsidesort_type(char *service, ns_srvsidesort_t type)
1783 {
1784         int i, size;
1785         servicesorttype_t *tmp;
1786 
1787         if (service == NULL)
1788                 return;
1789 
1790         (void) mutex_lock(&sort_type_mutex);
1791 
1792         for (i = 0; i < sort_type_hwm; i++) {
1793                 if (strcmp(sort_type[i].service, service) == 0) {
1794                         sort_type[i].type = type;
1795                         (void) mutex_unlock(&sort_type_mutex);
1796                         return;
1797                 }
1798         }
1799         if (sort_type == NULL) {
1800                 size = 10;
1801                 tmp = malloc(size * sizeof (servicesorttype_t));
1802                 if (tmp == NULL) {
1803                         (void) mutex_unlock(&sort_type_mutex);
1804                         return;
1805                 }
1806                 sort_type = tmp;
1807                 sort_type_size = size;
1808         } else if (sort_type_hwm >= sort_type_size) {
1809                 size = sort_type_size + 10;
1810                 tmp = realloc(sort_type, size * sizeof (servicesorttype_t));
1811                 if (tmp == NULL) {
1812                         (void) mutex_unlock(&sort_type_mutex);
1813                         return;
1814                 }
1815                 sort_type = tmp;
1816                 sort_type_size = size;
1817         }
1818         sort_type[sort_type_hwm].service = strdup(service);
1819         if (sort_type[sort_type_hwm].service == NULL) {
1820                 (void) mutex_unlock(&sort_type_mutex);
1821                 return;
1822         }
1823         sort_type[sort_type_hwm].type = type;
1824         sort_type_hwm++;
1825 
1826         (void) mutex_unlock(&sort_type_mutex);
1827 }
1828 
1829 static int
1830 setup_vlv_params(ns_ldap_cookie_t *cookie)
1831 {
1832         LDAPControl     **ctrls;
1833         LDAPsortkey     **sortkeylist;
1834         LDAPControl     *sortctrl = NULL;
1835         LDAPControl     *vlvctrl = NULL;
1836         LDAPVirtualList vlist;
1837         char            *sortattr;
1838         int             rc;
1839         int             free_sort = FALSE;
1840 
1841         _freeControlList(&cookie->p_serverctrls);
1842 
1843         if (cookie->sortTypeTry == SSS_UNKNOWN)
1844                 cookie->sortTypeTry = get_srvsidesort_type(cookie->service);
1845         if (cookie->sortTypeTry == SSS_UNKNOWN)
1846                 cookie->sortTypeTry = SSS_SINGLE_ATTR;
1847 
1848         if (cookie->sortTypeTry == SSS_SINGLE_ATTR) {
1849                 if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
1850                     cookie->i_sortattr) {
1851                         sortattr =  __ns_ldap_mapAttribute(cookie->service,
1852                             cookie->i_sortattr);
1853                         free_sort = TRUE;
1854                 } else if (cookie->i_sortattr) {
1855                         sortattr = (char *)cookie->i_sortattr;
1856                 } else {
1857                         sortattr = "cn";
1858                 }
1859         } else {
1860                 sortattr = "cn uid";
1861         }
1862 
1863         rc = ldap_create_sort_keylist(&sortkeylist, sortattr);
1864         if (free_sort)
1865                 free(sortattr);
1866         if (rc != LDAP_SUCCESS) {
1867                 (void) ldap_get_option(cookie->conn->ld,
1868                     LDAP_OPT_ERROR_NUMBER, &rc);
1869                 return (rc);
1870         }
1871         rc = ldap_create_sort_control(cookie->conn->ld,
1872             sortkeylist, 1, &sortctrl);
1873         ldap_free_sort_keylist(sortkeylist);
1874         if (rc != LDAP_SUCCESS) {
1875                 (void) ldap_get_option(cookie->conn->ld,
1876                     LDAP_OPT_ERROR_NUMBER, &rc);
1877                 return (rc);
1878         }
1879 
1880         vlist.ldvlist_index = cookie->index;
1881         vlist.ldvlist_size = 0;
1882 
1883         vlist.ldvlist_before_count = 0;
1884         vlist.ldvlist_after_count = LISTPAGESIZE-1;
1885         vlist.ldvlist_attrvalue = NULL;
1886         vlist.ldvlist_extradata = NULL;
1887 
1888         rc = ldap_create_virtuallist_control(cookie->conn->ld,
1889             &vlist, &vlvctrl);
1890         if (rc != LDAP_SUCCESS) {
1891                 ldap_control_free(sortctrl);
1892                 (void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1893                     &rc);
1894                 return (rc);
1895         }
1896 
1897         ctrls = (LDAPControl **)calloc(3, sizeof (LDAPControl *));
1898         if (ctrls == NULL) {
1899                 ldap_control_free(sortctrl);
1900                 ldap_control_free(vlvctrl);
1901                 return (LDAP_NO_MEMORY);
1902         }
1903 
1904         ctrls[0] = sortctrl;
1905         ctrls[1] = vlvctrl;
1906 
1907         cookie->p_serverctrls = ctrls;
1908         return (LDAP_SUCCESS);
1909 }
1910 
1911 static int
1912 setup_simplepg_params(ns_ldap_cookie_t *cookie)
1913 {
1914         LDAPControl     **ctrls;
1915         LDAPControl     *pgctrl = NULL;
1916         int             rc;
1917 
1918         _freeControlList(&cookie->p_serverctrls);
1919 
1920         rc = ldap_create_page_control(cookie->conn->ld, LISTPAGESIZE,
1921             cookie->ctrlCookie, (char)0, &pgctrl);
1922         if (rc != LDAP_SUCCESS) {
1923                 (void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1924                     &rc);
1925                 return (rc);
1926         }
1927 
1928         ctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
1929         if (ctrls == NULL) {
1930                 ldap_control_free(pgctrl);
1931                 return (LDAP_NO_MEMORY);
1932         }
1933         ctrls[0] = pgctrl;
1934         cookie->p_serverctrls = ctrls;
1935         return (LDAP_SUCCESS);
1936 }
1937 
1938 static void
1939 proc_result_referrals(ns_ldap_cookie_t *cookie)
1940 {
1941         int             errCode, i, rc;
1942         char            **referrals = NULL;
1943 
1944         /*
1945          * Only follow one level of referrals, i.e.
1946          * if already in referral mode, do nothing
1947          */
1948         if (cookie->refpos == NULL) {
1949                 cookie->new_state = END_RESULT;
1950                 rc = ldap_parse_result(cookie->conn->ld,
1951                     cookie->resultMsg,
1952                     &errCode, NULL,
1953                     NULL, &referrals,
1954                     NULL, 0);
1955                 if (rc != NS_LDAP_SUCCESS) {
1956                         (void) ldap_get_option(cookie->conn->ld,
1957                             LDAP_OPT_ERROR_NUMBER,
1958                             &cookie->err_rc);
1959                         cookie->new_state = LDAP_ERROR;
1960                         return;
1961                 }
1962                 if (errCode == LDAP_REFERRAL) {
1963                         for (i = 0; referrals[i] != NULL;
1964                             i++) {
1965                                 /* add to referral list */
1966                                 rc = __s_api_addRefInfo(
1967                                     &cookie->reflist,
1968                                     referrals[i],
1969                                     cookie->basedn,
1970                                     &cookie->scope,
1971                                     cookie->filter,
1972                                     cookie->conn->ld);
1973                                 if (rc != NS_LDAP_SUCCESS) {
1974                                         cookie->new_state =
1975                                             ERROR;
1976                                         break;
1977                                 }
1978                         }
1979                         ldap_value_free(referrals);
1980                 }
1981         }
1982 }
1983 
1984 static void
1985 proc_search_references(ns_ldap_cookie_t *cookie)
1986 {
1987         char            **refurls = NULL;
1988         int             i, rc;
1989 
1990         /*
1991          * Only follow one level of referrals, i.e.
1992          * if already in referral mode, do nothing
1993          */
1994         if (cookie->refpos == NULL) {
1995                 refurls = ldap_get_reference_urls(
1996                     cookie->conn->ld,
1997                     cookie->resultMsg);
1998                 if (refurls == NULL) {
1999                         (void) ldap_get_option(cookie->conn->ld,
2000                             LDAP_OPT_ERROR_NUMBER,
2001                             &cookie->err_rc);
2002                         cookie->new_state = LDAP_ERROR;
2003                         return;
2004                 }
2005                 for (i = 0; refurls[i] != NULL; i++) {
2006                         /* add to referral list */
2007                         rc = __s_api_addRefInfo(
2008                             &cookie->reflist,
2009                             refurls[i],
2010                             cookie->basedn,
2011                             &cookie->scope,
2012                             cookie->filter,
2013                             cookie->conn->ld);
2014                         if (rc != NS_LDAP_SUCCESS) {
2015                                 cookie->new_state =
2016                                     ERROR;
2017                                 break;
2018                         }
2019                 }
2020                 /* free allocated storage */
2021                 for (i = 0; refurls[i] != NULL; i++)
2022                         free(refurls[i]);
2023         }
2024 }
2025 
2026 static ns_state_t
2027 multi_result(ns_ldap_cookie_t *cookie)
2028 {
2029         char            errstr[MAXERROR];
2030         char            *err;
2031         ns_ldap_error_t **errorp = NULL;
2032         LDAPControl     **retCtrls = NULL;
2033         int             i, rc;
2034         int             errCode;
2035         boolean_t       finished = B_FALSE;
2036         unsigned long   target_posp = 0;
2037         unsigned long   list_size = 0;
2038         unsigned int    count = 0;
2039         char            **referrals = NULL;
2040 
2041         if (cookie->listType == VLVCTRLFLAG) {
2042                 rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2043                     &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2044                 if (rc != LDAP_SUCCESS) {
2045                         (void) ldap_get_option(cookie->conn->ld,
2046                             LDAP_OPT_ERROR_NUMBER,
2047                             &cookie->err_rc);
2048                         (void) sprintf(errstr,
2049                             gettext("LDAP ERROR (%d): %s.\n"),
2050                             cookie->err_rc,
2051                             gettext(ldap_err2string(cookie->err_rc)));
2052                         err = strdup(errstr);
2053                         MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2054                             NS_LDAP_MEMORY);
2055                         cookie->err_rc = NS_LDAP_INTERNAL;
2056                         cookie->errorp = *errorp;
2057                         return (LDAP_ERROR);
2058                 }
2059                 if (errCode == LDAP_REFERRAL) {
2060                         for (i = 0; referrals[i] != NULL;
2061                             i++) {
2062                                 /* add to referral list */
2063                                 rc = __s_api_addRefInfo(
2064                                     &cookie->reflist,
2065                                     referrals[i],
2066                                     cookie->basedn,
2067                                     &cookie->scope,
2068                                     cookie->filter,
2069                                     cookie->conn->ld);
2070                                 if (rc != NS_LDAP_SUCCESS) {
2071                                         ldap_value_free(
2072                                             referrals);
2073                                         if (retCtrls)
2074                                                 ldap_controls_free(
2075                                                     retCtrls);
2076                                         return (ERROR);
2077                                 }
2078                         }
2079                         ldap_value_free(referrals);
2080                         if (retCtrls)
2081                                 ldap_controls_free(retCtrls);
2082                         return (END_RESULT);
2083                 }
2084                 if (retCtrls) {
2085                         rc = ldap_parse_virtuallist_control(
2086                             cookie->conn->ld, retCtrls,
2087                             &target_posp, &list_size, &errCode);
2088                         if (rc == LDAP_SUCCESS) {
2089                                 /*
2090                                  * AD does not return valid target_posp
2091                                  * and list_size
2092                                  */
2093                                 if (target_posp != 0 && list_size != 0) {
2094                                         cookie->index =
2095                                             target_posp + LISTPAGESIZE;
2096                                         if (cookie->index > list_size)
2097                                                 finished = B_TRUE;
2098                                 } else {
2099                                         if (cookie->entryCount < LISTPAGESIZE)
2100                                                 finished = B_TRUE;
2101                                         else
2102                                                 cookie->index +=
2103                                                     cookie->entryCount;
2104                                 }
2105                         }
2106                         ldap_controls_free(retCtrls);
2107                         retCtrls = NULL;
2108                 } else {
2109                         finished = B_TRUE;
2110                 }
2111         } else if (cookie->listType == SIMPLEPAGECTRLFLAG) {
2112                 rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2113                     &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2114                 if (rc != LDAP_SUCCESS) {
2115                         (void) ldap_get_option(cookie->conn->ld,
2116                             LDAP_OPT_ERROR_NUMBER,
2117                             &cookie->err_rc);
2118                         (void) sprintf(errstr,
2119                             gettext("LDAP ERROR (%d): %s.\n"),
2120                             cookie->err_rc,
2121                             gettext(ldap_err2string(cookie->err_rc)));
2122                         err = strdup(errstr);
2123                         MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2124                             NS_LDAP_MEMORY);
2125                         cookie->err_rc = NS_LDAP_INTERNAL;
2126                         cookie->errorp = *errorp;
2127                         return (LDAP_ERROR);
2128                 }
2129                 if (errCode == LDAP_REFERRAL) {
2130                         for (i = 0; referrals[i] != NULL;
2131                             i++) {
2132                                 /* add to referral list */
2133                                 rc = __s_api_addRefInfo(
2134                                     &cookie->reflist,
2135                                     referrals[i],
2136                                     cookie->basedn,
2137                                     &cookie->scope,
2138                                     cookie->filter,
2139                                     cookie->conn->ld);
2140                                 if (rc != NS_LDAP_SUCCESS) {
2141                                         ldap_value_free(
2142                                             referrals);
2143                                         if (retCtrls)
2144                                                 ldap_controls_free(
2145                                                     retCtrls);
2146                                         return (ERROR);
2147                                 }
2148                         }
2149                         ldap_value_free(referrals);
2150                         if (retCtrls)
2151                                 ldap_controls_free(retCtrls);
2152                         return (END_RESULT);
2153                 }
2154                 if (retCtrls) {
2155                         if (cookie->ctrlCookie)
2156                                 ber_bvfree(cookie->ctrlCookie);
2157                         cookie->ctrlCookie = NULL;
2158                         rc = ldap_parse_page_control(
2159                             cookie->conn->ld, retCtrls,
2160                             &count, &cookie->ctrlCookie);
2161                         if (rc == LDAP_SUCCESS) {
2162                                 if ((cookie->ctrlCookie == NULL) ||
2163                                     (cookie->ctrlCookie->bv_val == NULL) ||
2164                                     (cookie->ctrlCookie->bv_len == 0))
2165                                         finished = B_TRUE;
2166                         }
2167                         ldap_controls_free(retCtrls);
2168                         retCtrls = NULL;
2169                 } else {
2170                         finished = B_TRUE;
2171                 }
2172         }
2173         if (!finished && cookie->listType == VLVCTRLFLAG)
2174                 return (NEXT_VLV);
2175         if (!finished && cookie->listType == SIMPLEPAGECTRLFLAG)
2176                 return (NEXT_PAGE);
2177         if (finished)
2178                 return (END_RESULT);
2179         return (ERROR);
2180 }
2181 
2182 /*
2183  * clear_results(ns_ldap_cookie_t):
2184  *
2185  * Attempt to obtain remnants of ldap responses and free them.  If remnants are
2186  * not obtained within a certain time period tell the server we wish to abandon
2187  * the request.
2188  *
2189  * Note that we do not initially tell the server to abandon the request as that
2190  * can be an expensive operation for the server, while it is cheap for us to
2191  * just flush the input.
2192  *
2193  * If something was to remain in libldap queue as a result of some error then
2194  * it would be freed later during drop connection call or when no other
2195  * requests share the connection.
2196  */
2197 static void
2198 clear_results(ns_ldap_cookie_t *cookie)
2199 {
2200         int rc;
2201         if (cookie->conn != NULL && cookie->conn->ld != NULL &&
2202             (cookie->connectionId != -1 ||
2203             (cookie->conn_user != NULL &&
2204             cookie->conn_user->conn_mt != NULL)) &&
2205             cookie->msgId != 0) {
2206                 /*
2207                  * We need to cleanup the rest of response (if there is such)
2208                  * and LDAP abandon is too heavy for LDAP servers, so we will
2209                  * wait for the rest of response till timeout and "process" it.
2210                  */
2211                 rc = ldap_result(cookie->conn->ld, cookie->msgId, LDAP_MSG_ALL,
2212                     (struct timeval *)&cookie->search_timeout,
2213                     &cookie->resultMsg);
2214                 if (rc != -1 && rc != 0 && cookie->resultMsg != NULL) {
2215                         (void) ldap_msgfree(cookie->resultMsg);
2216                         cookie->resultMsg = NULL;
2217                 }
2218 
2219                 /*
2220                  * If there was timeout then we will send  ABANDON request to
2221                  * LDAP server to decrease load.
2222                  */
2223                 if (rc == 0)
2224                         (void) ldap_abandon_ext(cookie->conn->ld, cookie->msgId,
2225                             NULL, NULL);
2226                 /* Disassociate cookie with msgId */
2227                 cookie->msgId = 0;
2228         }
2229 }
2230 
2231 /*
2232  * This state machine performs one or more LDAP searches to a given
2233  * directory server using service search descriptors and schema
2234  * mapping as appropriate.  The approximate pseudocode for
2235  * this routine is the following:
2236  *    Given the current configuration [set/reset connection etc.]
2237  *    and the current service search descriptor list
2238  *        or default search filter parameters
2239  *    foreach (service search filter) {
2240  *        initialize the filter [via filter_init if appropriate]
2241  *                get a valid session/connection (preferably the current one)
2242  *                                      Recover if the connection is lost
2243  *        perform the search
2244  *        foreach (result entry) {
2245  *            process result [via callback if appropriate]
2246  *                save result for caller if accepted.
2247  *                exit and return all collected if allResults found;
2248  *        }
2249  *    }
2250  *    return collected results and exit
2251  */
2252 
2253 static
2254 ns_state_t
2255 search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
2256 {
2257         char            errstr[MAXERROR];
2258         char            *err;
2259         int             rc, ret;
2260         int             rc_save;
2261         ns_ldap_entry_t *nextEntry;
2262         ns_ldap_error_t *error = NULL;
2263         ns_ldap_error_t **errorp;
2264         struct timeval  tv;
2265 
2266         errorp = &error;
2267         cookie->state = state;
2268         errstr[0] = '\0';
2269 
2270         for (;;) {
2271                 switch (cookie->state) {
2272                 case CLEAR_RESULTS:
2273                         clear_results(cookie);
2274                         cookie->new_state = EXIT;
2275                         break;
2276                 case GET_ACCT_MGMT_INFO:
2277                         /*
2278                          * Set the flag to get ldap account management controls.
2279                          */
2280                         cookie->nopasswd_acct_mgmt = 1;
2281                         cookie->new_state = INIT;
2282                         break;
2283                 case EXIT:
2284                         /* state engine/connection cleaned up in delete */
2285                         if (cookie->attribute) {
2286                                 __s_api_free2dArray(cookie->attribute);
2287                                 cookie->attribute = NULL;
2288                         }
2289                         if (cookie->reflist) {
2290                                 __s_api_deleteRefInfo(cookie->reflist);
2291                                 cookie->reflist = NULL;
2292                         }
2293                         return (EXIT);
2294                 case INIT:
2295                         cookie->sdpos = NULL;
2296                         cookie->new_state = NEXT_SEARCH_DESCRIPTOR;
2297                         if (cookie->attribute) {
2298                                 __s_api_free2dArray(cookie->attribute);
2299                                 cookie->attribute = NULL;
2300                         }
2301                         if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
2302                             cookie->i_attr) {
2303                                 cookie->attribute =
2304                                     __ns_ldap_mapAttributeList(
2305                                     cookie->service,
2306                                     cookie->i_attr);
2307                         }
2308                         break;
2309                 case REINIT:
2310                         /* Check if we've reached MAX retries. */
2311                         cookie->retries++;
2312                         if (cookie->retries > NS_LIST_TRY_MAX - 1) {
2313                                 cookie->new_state = LDAP_ERROR;
2314                                 break;
2315                         }
2316 
2317                         /*
2318                          * Even if we still have retries left, check
2319                          * if retry is possible.
2320                          */
2321                         if (cookie->conn_user != NULL) {
2322                                 int             retry;
2323                                 ns_conn_mgmt_t  *cmg;
2324                                 cmg = cookie->conn_user->conn_mgmt;
2325                                 retry = cookie->conn_user->retry;
2326                                 if (cmg != NULL && cmg->cfg_reloaded == 1)
2327                                         retry = 1;
2328                                 if (retry == 0) {
2329                                         cookie->new_state = LDAP_ERROR;
2330                                         break;
2331                                 }
2332                         }
2333                         /*
2334                          * Free results if any, reset to the first
2335                          * search descriptor and start a new session.
2336                          */
2337                         if (cookie->resultMsg != NULL) {
2338                                 (void) ldap_msgfree(cookie->resultMsg);
2339                                 cookie->resultMsg = NULL;
2340                         }
2341                         (void) __ns_ldap_freeError(&cookie->errorp);
2342                         (void) __ns_ldap_freeResult(&cookie->result);
2343                         cookie->sdpos = cookie->sdlist;
2344                         cookie->err_from_result = 0;
2345                         cookie->err_rc = 0;
2346                         cookie->new_state = NEXT_SESSION;
2347                         break;
2348                 case NEXT_SEARCH_DESCRIPTOR:
2349                         /* get next search descriptor */
2350                         if (cookie->sdpos == NULL) {
2351                                 cookie->sdpos = cookie->sdlist;
2352                                 cookie->new_state = GET_SESSION;
2353                         } else {
2354                                 cookie->sdpos++;
2355                                 cookie->new_state = NEXT_SEARCH;
2356                         }
2357                         if (*cookie->sdpos == NULL)
2358                                 cookie->new_state = EXIT;
2359                         break;
2360                 case GET_SESSION:
2361                         if (get_current_session(cookie) < 0)
2362                                 cookie->new_state = NEXT_SESSION;
2363                         else
2364                                 cookie->new_state = NEXT_SEARCH;
2365                         break;
2366                 case NEXT_SESSION:
2367                         if (get_next_session(cookie) < 0)
2368                                 cookie->new_state = RESTART_SESSION;
2369                         else
2370                                 cookie->new_state = NEXT_SEARCH;
2371                         break;
2372                 case RESTART_SESSION:
2373                         if (cookie->i_flags & NS_LDAP_HARD) {
2374                                 cookie->new_state = NEXT_SESSION;
2375                                 break;
2376                         }
2377                         (void) sprintf(errstr,
2378                             gettext("Session error no available conn.\n"),
2379                             state);
2380                         err = strdup(errstr);
2381                         MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2382                             NS_LDAP_MEMORY);
2383                         cookie->err_rc = NS_LDAP_INTERNAL;
2384                         cookie->errorp = *errorp;
2385                         cookie->new_state = EXIT;
2386                         break;
2387                 case NEXT_SEARCH:
2388                         /* setup referrals search if necessary */
2389                         if (cookie->refpos) {
2390                                 if (setup_referral_search(cookie) < 0) {
2391                                         cookie->new_state = EXIT;
2392                                         break;
2393                                 }
2394                         } else if (setup_next_search(cookie) < 0) {
2395                                 cookie->new_state = EXIT;
2396                                 break;
2397                         }
2398                         /* only do VLV/PAGE on scopes onelevel/subtree */
2399                         if (paging_supported(cookie)) {
2400                                 if (cookie->use_paging &&
2401                                     (cookie->scope != LDAP_SCOPE_BASE)) {
2402                                         cookie->index = 1;
2403                                         if (cookie->listType == VLVCTRLFLAG)
2404                                                 cookie->new_state = NEXT_VLV;
2405                                         else
2406                                                 cookie->new_state = NEXT_PAGE;
2407                                         break;
2408                                 }
2409                         }
2410                         cookie->new_state = ONE_SEARCH;
2411                         break;
2412                 case NEXT_VLV:
2413                         rc = setup_vlv_params(cookie);
2414                         if (rc != LDAP_SUCCESS) {
2415                                 cookie->err_rc = rc;
2416                                 cookie->new_state = LDAP_ERROR;
2417                                 break;
2418                         }
2419                         cookie->next_state = MULTI_RESULT;
2420                         cookie->new_state = DO_SEARCH;
2421                         break;
2422                 case NEXT_PAGE:
2423                         rc = setup_simplepg_params(cookie);
2424                         if (rc != LDAP_SUCCESS) {
2425                                 cookie->err_rc = rc;
2426                                 cookie->new_state = LDAP_ERROR;
2427                                 break;
2428                         }
2429                         cookie->next_state = MULTI_RESULT;
2430                         cookie->new_state = DO_SEARCH;
2431                         break;
2432                 case ONE_SEARCH:
2433                         cookie->next_state = NEXT_RESULT;
2434                         cookie->new_state = DO_SEARCH;
2435                         break;
2436                 case DO_SEARCH:
2437                         cookie->entryCount = 0;
2438                         rc = ldap_search_ext(cookie->conn->ld,
2439                             cookie->basedn,
2440                             cookie->scope,
2441                             cookie->filter,
2442                             cookie->attribute,
2443                             0,
2444                             cookie->p_serverctrls,
2445                             NULL,
2446                             &cookie->search_timeout, 0,
2447                             &cookie->msgId);
2448                         if (rc != LDAP_SUCCESS) {
2449                                 if (rc == LDAP_BUSY ||
2450                                     rc == LDAP_UNAVAILABLE ||
2451                                     rc == LDAP_UNWILLING_TO_PERFORM ||
2452                                     rc == LDAP_CONNECT_ERROR ||
2453                                     rc == LDAP_SERVER_DOWN) {
2454 
2455                                         if (cookie->reinit_on_retriable_err) {
2456                                                 cookie->err_rc = rc;
2457                                                 cookie->new_state = REINIT;
2458                                         } else {
2459                                                 cookie->new_state =
2460                                                     NEXT_SESSION;
2461                                         }
2462 
2463                                         /*
2464                                          * If not able to reach the
2465                                          * server, inform the ldap
2466                                          * cache manager that the
2467                                          * server should be removed
2468                                          * from it's server list.
2469                                          * Thus, the manager will not
2470                                          * return this server on the next
2471                                          * get-server request and will
2472                                          * also reduce the server list
2473                                          * refresh TTL, so that it will
2474                                          * find out sooner when the server
2475                                          * is up again.
2476                                          */
2477                                         if ((rc == LDAP_CONNECT_ERROR ||
2478                                             rc == LDAP_SERVER_DOWN) &&
2479                                             (cookie->conn_user == NULL ||
2480                                             cookie->conn_user->conn_mt ==
2481                                             NULL)) {
2482                                                 ret = __s_api_removeServer(
2483                                                     cookie->conn->serverAddr);
2484                                                 if (ret == NS_CACHE_NOSERVER &&
2485                                                     cookie->conn_auth_type
2486                                                     == NS_LDAP_AUTH_NONE) {
2487                                                         /*
2488                                                          * Couldn't remove
2489                                                          * server from server
2490                                                          * list.
2491                                                          * Exit to avoid
2492                                                          * potential infinite
2493                                                          * loop.
2494                                                          */
2495                                                         cookie->err_rc = rc;
2496                                                         cookie->new_state =
2497                                                             LDAP_ERROR;
2498                                                 }
2499                                                 if (cookie->connectionId > -1) {
2500                                                         /*
2501                                                          * NS_LDAP_NEW_CONN
2502                                                          * indicates that the
2503                                                          * connection should
2504                                                          * be deleted, not
2505                                                          * kept alive
2506                                                          */
2507                                                         DropConnection(
2508                                                             cookie->
2509                                                             connectionId,
2510                                                             NS_LDAP_NEW_CONN);
2511                                                         cookie->connectionId =
2512                                                             -1;
2513                                                 }
2514                                         } else if ((rc == LDAP_CONNECT_ERROR ||
2515                                             rc == LDAP_SERVER_DOWN) &&
2516                                             cookie->conn_user != NULL) {
2517                                                 if (cookie->
2518                                                     reinit_on_retriable_err) {
2519                                                         /*
2520                                                          * MT connection not
2521                                                          * usable, close it
2522                                                          * before REINIT.
2523                                                          * rc has already
2524                                                          * been saved in
2525                                                          * cookie->err_rc above.
2526                                                          */
2527                                                         __s_api_conn_mt_close(
2528                                                             cookie->conn_user,
2529                                                             rc,
2530                                                             &cookie->errorp);
2531                                                 } else {
2532                                                         /*
2533                                                          * MT connection not
2534                                                          * usable, close it in
2535                                                          * the LDAP_ERROR state.
2536                                                          * A retry will be done
2537                                                          * next if allowed.
2538                                                          */
2539                                                         cookie->err_rc = rc;
2540                                                         cookie->new_state =
2541                                                             LDAP_ERROR;
2542                                                 }
2543                                         }
2544                                         break;
2545                                 }
2546                                 cookie->err_rc = rc;
2547                                 cookie->new_state = LDAP_ERROR;
2548                                 break;
2549                         }
2550                         cookie->new_state = cookie->next_state;
2551                         break;
2552                 case NEXT_RESULT:
2553                         /*
2554                          * Caller (e.g. __ns_ldap_list_batch_add)
2555                          * does not want to block on ldap_result().
2556                          * Therefore we execute ldap_result() with
2557                          * a zeroed timeval.
2558                          */
2559                         if (cookie->no_wait == B_TRUE)
2560                                 (void) memset(&tv, 0, sizeof (tv));
2561                         else
2562                                 tv = cookie->search_timeout;
2563                         rc = ldap_result(cookie->conn->ld, cookie->msgId,
2564                             LDAP_MSG_ONE,
2565                             &tv,
2566                             &cookie->resultMsg);
2567                         if (rc == LDAP_RES_SEARCH_RESULT) {
2568                                 cookie->new_state = END_RESULT;
2569                                 /* check and process referrals info */
2570                                 if (cookie->followRef)
2571                                         proc_result_referrals(
2572                                             cookie);
2573                                 (void) ldap_msgfree(cookie->resultMsg);
2574                                 cookie->resultMsg = NULL;
2575                                 break;
2576                         }
2577                         /* handle referrals if necessary */
2578                         if (rc == LDAP_RES_SEARCH_REFERENCE) {
2579                                 if (cookie->followRef)
2580                                         proc_search_references(cookie);
2581                                 (void) ldap_msgfree(cookie->resultMsg);
2582                                 cookie->resultMsg = NULL;
2583                                 break;
2584                         }
2585                         if (rc != LDAP_RES_SEARCH_ENTRY) {
2586                                 switch (rc) {
2587                                 case 0:
2588                                         if (cookie->no_wait == B_TRUE) {
2589                                                 (void) ldap_msgfree(
2590                                                     cookie->resultMsg);
2591                                                 cookie->resultMsg = NULL;
2592                                                 return (cookie->new_state);
2593                                         }
2594                                         rc = LDAP_TIMEOUT;
2595                                         break;
2596                                 case -1:
2597                                         rc = ldap_get_lderrno(cookie->conn->ld,
2598                                             NULL, NULL);
2599                                         break;
2600                                 default:
2601                                         rc = ldap_result2error(cookie->conn->ld,
2602                                             cookie->resultMsg, 1);
2603                                         break;
2604                                 }
2605                                 if ((rc == LDAP_TIMEOUT ||
2606                                     rc == LDAP_SERVER_DOWN) &&
2607                                     (cookie->conn_user == NULL ||
2608                                     cookie->conn_user->conn_mt == NULL)) {
2609                                         if (rc == LDAP_TIMEOUT)
2610                                                 (void) __s_api_removeServer(
2611                                                     cookie->conn->serverAddr);
2612                                         if (cookie->connectionId > -1) {
2613                                                 DropConnection(
2614                                                     cookie->connectionId,
2615                                                     NS_LDAP_NEW_CONN);
2616                                                 cookie->connectionId = -1;
2617                                         }
2618                                         cookie->err_from_result = 1;
2619                                 }
2620                                 (void) ldap_msgfree(cookie->resultMsg);
2621                                 cookie->resultMsg = NULL;
2622                                 if (rc == LDAP_BUSY ||
2623                                     rc == LDAP_UNAVAILABLE ||
2624                                     rc == LDAP_UNWILLING_TO_PERFORM) {
2625                                         if (cookie->reinit_on_retriable_err) {
2626                                                 cookie->err_rc = rc;
2627                                                 cookie->err_from_result = 1;
2628                                                 cookie->new_state = REINIT;
2629                                         } else {
2630                                                 cookie->new_state =
2631                                                     NEXT_SESSION;
2632                                         }
2633                                         break;
2634                                 }
2635                                 if ((rc == LDAP_CONNECT_ERROR ||
2636                                     rc == LDAP_SERVER_DOWN) &&
2637                                     cookie->reinit_on_retriable_err) {
2638                                         ns_ldap_error_t *errorp = NULL;
2639                                         cookie->err_rc = rc;
2640                                         cookie->err_from_result = 1;
2641                                         cookie->new_state = REINIT;
2642                                         if (cookie->conn_user != NULL)
2643                                                 __s_api_conn_mt_close(
2644                                                     cookie->conn_user,
2645                                                     rc, &errorp);
2646                                         if (errorp != NULL) {
2647                                                 (void) __ns_ldap_freeError(
2648                                                     &cookie->errorp);
2649                                                 cookie->errorp = errorp;
2650                                         }
2651                                         break;
2652                                 }
2653                                 cookie->err_rc = rc;
2654                                 cookie->new_state = LDAP_ERROR;
2655                                 break;
2656                         }
2657                         /* else LDAP_RES_SEARCH_ENTRY */
2658                         /* get account management response control */
2659                         if (cookie->nopasswd_acct_mgmt == 1) {
2660                                 rc = ldap_get_entry_controls(cookie->conn->ld,
2661                                     cookie->resultMsg,
2662                                     &(cookie->resultctrl));
2663                                 if (rc != LDAP_SUCCESS) {
2664                                         cookie->new_state = LDAP_ERROR;
2665                                         cookie->err_rc = rc;
2666                                         break;
2667                                 }
2668                         }
2669                         rc = __s_api_getEntry(cookie);
2670                         (void) ldap_msgfree(cookie->resultMsg);
2671                         cookie->resultMsg = NULL;
2672                         if (rc != NS_LDAP_SUCCESS) {
2673                                 cookie->new_state = LDAP_ERROR;
2674                                 break;
2675                         }
2676                         cookie->new_state = PROCESS_RESULT;
2677                         cookie->next_state = NEXT_RESULT;
2678                         break;
2679                 case MULTI_RESULT:
2680                         if (cookie->no_wait == B_TRUE)
2681                                 (void) memset(&tv, 0, sizeof (tv));
2682                         else
2683                                 tv = cookie->search_timeout;
2684                         rc = ldap_result(cookie->conn->ld, cookie->msgId,
2685                             LDAP_MSG_ONE,
2686                             &tv,
2687                             &cookie->resultMsg);
2688                         if (rc == LDAP_RES_SEARCH_RESULT) {
2689                                 rc = ldap_result2error(cookie->conn->ld,
2690                                     cookie->resultMsg, 0);
2691                                 if (rc == LDAP_ADMINLIMIT_EXCEEDED &&
2692                                     cookie->listType == VLVCTRLFLAG &&
2693                                     cookie->sortTypeTry == SSS_SINGLE_ATTR) {
2694                                         /* Try old "cn uid" server side sort */
2695                                         cookie->sortTypeTry = SSS_CN_UID_ATTRS;
2696                                         cookie->new_state = NEXT_VLV;
2697                                         (void) ldap_msgfree(cookie->resultMsg);
2698                                         cookie->resultMsg = NULL;
2699                                         break;
2700                                 }
2701                                 if (rc != LDAP_SUCCESS) {
2702                                         cookie->err_rc = rc;
2703                                         cookie->new_state = LDAP_ERROR;
2704                                         (void) ldap_msgfree(cookie->resultMsg);
2705                                         cookie->resultMsg = NULL;
2706                                         break;
2707                                 }
2708                                 cookie->new_state = multi_result(cookie);
2709                                 (void) ldap_msgfree(cookie->resultMsg);
2710                                 cookie->resultMsg = NULL;
2711                                 break;
2712                         }
2713                         /* handle referrals if necessary */
2714                         if (rc == LDAP_RES_SEARCH_REFERENCE &&
2715                             cookie->followRef) {
2716                                 proc_search_references(cookie);
2717                                 (void) ldap_msgfree(cookie->resultMsg);
2718                                 cookie->resultMsg = NULL;
2719                                 break;
2720                         }
2721                         if (rc != LDAP_RES_SEARCH_ENTRY) {
2722                                 switch (rc) {
2723                                 case 0:
2724                                         if (cookie->no_wait == B_TRUE) {
2725                                                 (void) ldap_msgfree(
2726                                                     cookie->resultMsg);
2727                                                 cookie->resultMsg = NULL;
2728                                                 return (cookie->new_state);
2729                                         }
2730                                         rc = LDAP_TIMEOUT;
2731                                         break;
2732                                 case -1:
2733                                         rc = ldap_get_lderrno(cookie->conn->ld,
2734                                             NULL, NULL);
2735                                         break;
2736                                 default:
2737                                         rc = ldap_result2error(cookie->conn->ld,
2738                                             cookie->resultMsg, 1);
2739                                         break;
2740                                 }
2741                                 if ((rc == LDAP_TIMEOUT ||
2742                                     rc == LDAP_SERVER_DOWN) &&
2743                                     (cookie->conn_user == NULL ||
2744                                     cookie->conn_user->conn_mt == NULL)) {
2745                                         if (rc == LDAP_TIMEOUT)
2746                                                 (void) __s_api_removeServer(
2747                                                     cookie->conn->serverAddr);
2748                                         if (cookie->connectionId > -1) {
2749                                                 DropConnection(
2750                                                     cookie->connectionId,
2751                                                     NS_LDAP_NEW_CONN);
2752                                                 cookie->connectionId = -1;
2753                                         }
2754                                         cookie->err_from_result = 1;
2755                                 }
2756                                 (void) ldap_msgfree(cookie->resultMsg);
2757                                 cookie->resultMsg = NULL;
2758                                 if (rc == LDAP_BUSY ||
2759                                     rc == LDAP_UNAVAILABLE ||
2760                                     rc == LDAP_UNWILLING_TO_PERFORM) {
2761                                         if (cookie->reinit_on_retriable_err) {
2762                                                 cookie->err_rc = rc;
2763                                                 cookie->err_from_result = 1;
2764                                                 cookie->new_state = REINIT;
2765                                         } else {
2766                                                 cookie->new_state =
2767                                                     NEXT_SESSION;
2768                                         }
2769                                         break;
2770                                 }
2771 
2772                                 if ((rc == LDAP_CONNECT_ERROR ||
2773                                     rc == LDAP_SERVER_DOWN) &&
2774                                     cookie->reinit_on_retriable_err) {
2775                                         ns_ldap_error_t *errorp = NULL;
2776                                         cookie->err_rc = rc;
2777                                         cookie->err_from_result = 1;
2778                                         cookie->new_state = REINIT;
2779                                         if (cookie->conn_user != NULL)
2780                                                 __s_api_conn_mt_close(
2781                                                     cookie->conn_user,
2782                                                     rc, &errorp);
2783                                         if (errorp != NULL) {
2784                                                 (void) __ns_ldap_freeError(
2785                                                     &cookie->errorp);
2786                                                 cookie->errorp = errorp;
2787                                         }
2788                                         break;
2789                                 }
2790                                 cookie->err_rc = rc;
2791                                 cookie->new_state = LDAP_ERROR;
2792                                 break;
2793                         }
2794                         /* else LDAP_RES_SEARCH_ENTRY */
2795                         cookie->entryCount++;
2796                         rc = __s_api_getEntry(cookie);
2797                         (void) ldap_msgfree(cookie->resultMsg);
2798                         cookie->resultMsg = NULL;
2799                         if (rc != NS_LDAP_SUCCESS) {
2800                                 cookie->new_state = LDAP_ERROR;
2801                                 break;
2802                         }
2803                         /*
2804                          * If VLV search was successfull save the server
2805                          * side sort type tried.
2806                          */
2807                         if (cookie->listType == VLVCTRLFLAG)
2808                                 update_srvsidesort_type(cookie->service,
2809                                     cookie->sortTypeTry);
2810 
2811                         cookie->new_state = PROCESS_RESULT;
2812                         cookie->next_state = MULTI_RESULT;
2813                         break;
2814                 case PROCESS_RESULT:
2815                         /* NOTE THIS STATE MAY BE PROCESSED BY CALLER */
2816                         if (cookie->use_usercb && cookie->callback) {
2817                                 rc = 0;
2818                                 for (nextEntry = cookie->result->entry;
2819                                     nextEntry != NULL;
2820                                     nextEntry = nextEntry->next) {
2821                                         rc = (*cookie->callback)(nextEntry,
2822                                             cookie->userdata);
2823 
2824                                         if (rc == NS_LDAP_CB_DONE) {
2825                                         /* cb doesn't want any more data */
2826                                                 rc = NS_LDAP_PARTIAL;
2827                                                 cookie->err_rc = rc;
2828                                                 break;
2829                                         } else if (rc != NS_LDAP_CB_NEXT) {
2830                                         /* invalid return code */
2831                                                 rc = NS_LDAP_OP_FAILED;
2832                                                 cookie->err_rc = rc;
2833                                                 break;
2834                                         }
2835                                 }
2836                                 (void) __ns_ldap_freeResult(&cookie->result);
2837                                 cookie->result = NULL;
2838                         }
2839                         if (rc != 0) {
2840                                 cookie->new_state = EXIT;
2841                                 break;
2842                         }
2843                         /* NOTE PREVIOUS STATE SPECIFIES NEXT STATE */
2844                         cookie->new_state = cookie->next_state;
2845                         break;
2846                 case END_PROCESS_RESULT:
2847                         cookie->new_state = cookie->next_state;
2848                         break;
2849                 case END_RESULT:
2850                         /*
2851                          * XXX DO WE NEED THIS CASE?
2852                          * if (search is complete) {
2853                          *      cookie->new_state = EXIT;
2854                          * } else
2855                          */
2856                                 /*
2857                                  * entering referral mode if necessary
2858                                  */
2859                                 if (cookie->followRef && cookie->reflist)
2860                                         cookie->new_state =
2861                                             NEXT_REFERRAL;
2862                                 else
2863                                         cookie->new_state =
2864                                             NEXT_SEARCH_DESCRIPTOR;
2865                         break;
2866                 case NEXT_REFERRAL:
2867                         /* get next referral info */
2868                         if (cookie->refpos == NULL)
2869                                 cookie->refpos =
2870                                     cookie->reflist;
2871                         else
2872                                 cookie->refpos =
2873                                     cookie->refpos->next;
2874                         /* check see if done with all referrals */
2875                         if (cookie->refpos != NULL) {
2876                                 cookie->new_state =
2877                                     GET_REFERRAL_SESSION;
2878                         } else {
2879                                 __s_api_deleteRefInfo(cookie->reflist);
2880                                 cookie->reflist = NULL;
2881                                 cookie->new_state =
2882                                     NEXT_SEARCH_DESCRIPTOR;
2883                                 if (cookie->conn_user != NULL)
2884                                         cookie->conn_user->referral = B_FALSE;
2885                         }
2886                         break;
2887                 case GET_REFERRAL_SESSION:
2888                         if (get_referral_session(cookie) < 0) {
2889                                 cookie->new_state = EXIT;
2890                         } else {
2891                                 cookie->new_state = NEXT_SEARCH;
2892                         }
2893                         break;
2894                 case LDAP_ERROR:
2895                         rc_save = cookie->err_rc;
2896                         if (cookie->err_from_result) {
2897                                 if (cookie->err_rc == LDAP_SERVER_DOWN) {
2898                                         (void) sprintf(errstr,
2899                                             gettext("LDAP ERROR (%d): "
2900                                             "Error occurred during"
2901                                             " receiving results. "
2902                                             "Connection to server lost."),
2903                                             cookie->err_rc);
2904                                 } else if (cookie->err_rc == LDAP_TIMEOUT) {
2905                                         (void) sprintf(errstr,
2906                                             gettext("LDAP ERROR (%d): "
2907                                             "Error occurred during"
2908                                             " receiving results. %s"
2909                                             "."), cookie->err_rc,
2910                                             ldap_err2string(
2911                                             cookie->err_rc));
2912                                 }
2913                         } else {
2914                                 (void) sprintf(errstr,
2915                                     gettext("LDAP ERROR (%d): %s."),
2916                                     cookie->err_rc,
2917                                     ldap_err2string(cookie->err_rc));
2918                         }
2919                         err = strdup(errstr);
2920                         if (cookie->err_from_result) {
2921                                 if (cookie->err_rc == LDAP_SERVER_DOWN) {
2922                                         MKERROR(LOG_INFO, *errorp,
2923                                             cookie->err_rc, err,
2924                                             NS_LDAP_MEMORY);
2925                                 } else {
2926                                         MKERROR(LOG_WARNING, *errorp,
2927                                             cookie->err_rc, err,
2928                                             NS_LDAP_MEMORY);
2929                                 }
2930                         } else {
2931                                 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2932                                     err, NS_LDAP_MEMORY);
2933                         }
2934                         cookie->err_rc = NS_LDAP_INTERNAL;
2935                         cookie->errorp = *errorp;
2936                         if (cookie->conn_user != NULL)  {
2937                                 if (rc_save == LDAP_SERVER_DOWN ||
2938                                     rc_save == LDAP_CONNECT_ERROR) {
2939                                         /*
2940                                          * MT connection is not usable,
2941                                          * close it.
2942                                          */
2943                                         __s_api_conn_mt_close(cookie->conn_user,
2944                                             rc_save, &cookie->errorp);
2945                                         return (ERROR);
2946                                 }
2947                         }
2948                         return (ERROR);
2949                 default:
2950                 case ERROR:
2951                         (void) sprintf(errstr,
2952                             gettext("Internal State machine exit (%d).\n"),
2953                             cookie->state);
2954                         err = strdup(errstr);
2955                         MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2956                             NS_LDAP_MEMORY);
2957                         cookie->err_rc = NS_LDAP_INTERNAL;
2958                         cookie->errorp = *errorp;
2959                         return (ERROR);
2960                 }
2961 
2962                 if (cookie->conn_user != NULL &&
2963                     cookie->conn_user->bad_mt_conn ==  B_TRUE) {
2964                         __s_api_conn_mt_close(cookie->conn_user, 0, NULL);
2965                         cookie->err_rc = cookie->conn_user->ns_rc;
2966                         cookie->errorp = cookie->conn_user->ns_error;
2967                         cookie->conn_user->ns_error = NULL;
2968                         return (ERROR);
2969                 }
2970 
2971                 if (cycle == ONE_STEP) {
2972                         return (cookie->new_state);
2973                 }
2974                 cookie->state = cookie->new_state;
2975         }
2976         /*NOTREACHED*/
2977 #if 0
2978         (void) sprintf(errstr,
2979             gettext("Unexpected State machine error.\n"));
2980         err = strdup(errstr);
2981         MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, NS_LDAP_MEMORY);
2982         cookie->err_rc = NS_LDAP_INTERNAL;
2983         cookie->errorp = *errorp;
2984         return (ERROR);
2985 #endif
2986 }
2987 
2988 /*
2989  * For a lookup of shadow data, if shadow update is enabled,
2990  * check the calling process' privilege to ensure it's
2991  * allowed to perform such operation.
2992  */
2993 static int
2994 check_shadow(ns_ldap_cookie_t *cookie, const char *service)
2995 {
2996         char errstr[MAXERROR];
2997         char *err;
2998         boolean_t priv;
2999         /* caller */
3000         priv_set_t *ps;
3001         /* zone */
3002         priv_set_t *zs;
3003 
3004         /*
3005          * If service is "shadow", we may need
3006          * to use privilege credentials.
3007          */
3008         if ((strcmp(service, "shadow") == 0) &&
3009             __ns_ldap_is_shadow_update_enabled()) {
3010                 /*
3011                  * Since we release admin credentials after
3012                  * connection is closed and we do not cache
3013                  * them, we allow any root or all zone
3014                  * privilege process to read shadow data.
3015                  */
3016                 priv = (geteuid() == 0);
3017                 if (!priv) {
3018                         /* caller */
3019                         ps = priv_allocset();
3020 
3021                         (void) getppriv(PRIV_EFFECTIVE, ps);
3022                         zs = priv_str_to_set("zone", ",", NULL);
3023                         priv = priv_isequalset(ps, zs);
3024                         priv_freeset(ps);
3025                         priv_freeset(zs);
3026                 }
3027                 if (!priv) {
3028                         (void) sprintf(errstr,
3029                             gettext("Permission denied"));
3030                         err = strdup(errstr);
3031                         if (err == NULL)
3032                                 return (NS_LDAP_MEMORY);
3033                         MKERROR(LOG_INFO, cookie->errorp, NS_LDAP_INTERNAL, err,
3034                             NS_LDAP_MEMORY);
3035                         return (NS_LDAP_INTERNAL);
3036                 }
3037                 cookie->i_flags |= NS_LDAP_READ_SHADOW;
3038                 /*
3039                  * We do not want to reuse connection (hence
3040                  * keep it open) with admin credentials.
3041                  * If NS_LDAP_KEEP_CONN is set, reject the
3042                  * request.
3043                  */
3044                 if (cookie->i_flags & NS_LDAP_KEEP_CONN)
3045                         return (NS_LDAP_INVALID_PARAM);
3046                 cookie->i_flags |= NS_LDAP_NEW_CONN;
3047         }
3048 
3049         return (NS_LDAP_SUCCESS);
3050 }
3051 
3052 /*
3053  * internal function for __ns_ldap_list
3054  */
3055 static int
3056 ldap_list(
3057         ns_ldap_list_batch_t *batch,
3058         const char *service,
3059         const char *filter,
3060         const char *sortattr,
3061         int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3062         char **realfilter, const void *userdata),
3063         const char * const *attribute,
3064         const ns_cred_t *auth,
3065         const int flags,
3066         ns_ldap_result_t **rResult, /* return result entries */
3067         ns_ldap_error_t **errorp,
3068         int *rcp,
3069         int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3070         const void *userdata, ns_conn_user_t *conn_user)
3071 {
3072         ns_ldap_cookie_t        *cookie;
3073         ns_ldap_search_desc_t   **sdlist = NULL;
3074         ns_ldap_search_desc_t   *dptr;
3075         ns_ldap_error_t         *error = NULL;
3076         char                    **dns = NULL;
3077         int                     scope;
3078         int                     rc;
3079         int                     from_result;
3080 
3081         *errorp = NULL;
3082         *rResult = NULL;
3083         *rcp = NS_LDAP_SUCCESS;
3084 
3085         /*
3086          * Sanity check - NS_LDAP_READ_SHADOW is for our
3087          * own internal use.
3088          */
3089         if (flags & NS_LDAP_READ_SHADOW)
3090                 return (NS_LDAP_INVALID_PARAM);
3091 
3092         /* Initialize State machine cookie */
3093         cookie = init_search_state_machine();
3094         if (cookie == NULL) {
3095                 *rcp = NS_LDAP_MEMORY;
3096                 return (NS_LDAP_MEMORY);
3097         }
3098         cookie->conn_user = conn_user;
3099 
3100         /* see if need to follow referrals */
3101         rc = __s_api_toFollowReferrals(flags,
3102             &cookie->followRef, errorp);
3103         if (rc != NS_LDAP_SUCCESS) {
3104                 delete_search_cookie(cookie);
3105                 *rcp = rc;
3106                 return (rc);
3107         }
3108 
3109         /* get the service descriptor - or create a default one */
3110         rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3111             &sdlist, &error);
3112         if (rc != NS_LDAP_SUCCESS) {
3113                 delete_search_cookie(cookie);
3114                 *errorp = error;
3115                 *rcp = rc;
3116                 return (rc);
3117         }
3118 
3119         if (sdlist == NULL) {
3120                 /* Create default service Desc */
3121                 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3122                     sizeof (ns_ldap_search_desc_t *));
3123                 if (sdlist == NULL) {
3124                         delete_search_cookie(cookie);
3125                         cookie = NULL;
3126                         *rcp = NS_LDAP_MEMORY;
3127                         return (NS_LDAP_MEMORY);
3128                 }
3129                 dptr = (ns_ldap_search_desc_t *)
3130                     calloc(1, sizeof (ns_ldap_search_desc_t));
3131                 if (dptr == NULL) {
3132                         free(sdlist);
3133                         delete_search_cookie(cookie);
3134                         cookie = NULL;
3135                         *rcp = NS_LDAP_MEMORY;
3136                         return (NS_LDAP_MEMORY);
3137                 }
3138                 sdlist[0] = dptr;
3139 
3140                 /* default base */
3141                 rc = __s_api_getDNs(&dns, service, &cookie->errorp);
3142                 if (rc != NS_LDAP_SUCCESS) {
3143                         if (dns) {
3144                                 __s_api_free2dArray(dns);
3145                                 dns = NULL;
3146                         }
3147                         *errorp = cookie->errorp;
3148                         cookie->errorp = NULL;
3149                         delete_search_cookie(cookie);
3150                         cookie = NULL;
3151                         *rcp = rc;
3152                         return (rc);
3153                 }
3154                 dptr->basedn = strdup(dns[0]);
3155                 __s_api_free2dArray(dns);
3156                 dns = NULL;
3157 
3158                 /* default scope */
3159                 scope = 0;
3160                 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3161                 dptr->scope = scope;
3162         }
3163 
3164         cookie->sdlist = sdlist;
3165 
3166         /*
3167          * use VLV/PAGE control only if NS_LDAP_PAGE_CTRL is set
3168          */
3169         if (flags & NS_LDAP_PAGE_CTRL)
3170                 cookie->use_paging = TRUE;
3171         else
3172                 cookie->use_paging = FALSE;
3173 
3174         /* Set up other arguments */
3175         cookie->userdata = userdata;
3176         if (init_filter_cb != NULL) {
3177                 cookie->init_filter_cb = init_filter_cb;
3178                 cookie->use_filtercb = 1;
3179         }
3180         if (callback != NULL) {
3181                 cookie->callback = callback;
3182                 cookie->use_usercb = 1;
3183         }
3184 
3185         /* check_shadow() may add extra value to cookie->i_flags */
3186         cookie->i_flags = flags;
3187         if (service) {
3188                 cookie->service = strdup(service);
3189                 if (cookie->service == NULL) {
3190                         delete_search_cookie(cookie);
3191                         cookie = NULL;
3192                         *rcp = NS_LDAP_MEMORY;
3193                         return (NS_LDAP_MEMORY);
3194                 }
3195 
3196                 /*
3197                  * If given, use the credential given by the caller, and
3198                  * skip the credential check required for shadow update.
3199                  */
3200                 if (auth == NULL) {
3201                         rc = check_shadow(cookie, service);
3202                         if (rc != NS_LDAP_SUCCESS) {
3203                                 *errorp = cookie->errorp;
3204                                 cookie->errorp = NULL;
3205                                 delete_search_cookie(cookie);
3206                                 cookie = NULL;
3207                                 *rcp = rc;
3208                                 return (rc);
3209                         }
3210                 }
3211         }
3212 
3213         cookie->i_filter = strdup(filter);
3214         cookie->i_attr = attribute;
3215         cookie->i_auth = auth;
3216         cookie->i_sortattr = sortattr;
3217 
3218         if (batch != NULL) {
3219                 cookie->batch = batch;
3220                 cookie->reinit_on_retriable_err = B_TRUE;
3221                 cookie->no_wait = B_TRUE;
3222                 (void) search_state_machine(cookie, INIT, 0);
3223                 cookie->no_wait = B_FALSE;
3224                 rc = cookie->err_rc;
3225 
3226                 if (rc == NS_LDAP_SUCCESS) {
3227                         /*
3228                          * Here rc == NS_LDAP_SUCCESS means that the state
3229                          * machine init'ed successfully. The actual status
3230                          * of the search will be determined by
3231                          * __ns_ldap_list_batch_end(). Add the cookie to our
3232                          * batch.
3233                          */
3234                         cookie->caller_result = rResult;
3235                         cookie->caller_errorp = errorp;
3236                         cookie->caller_rc = rcp;
3237                         cookie->next_cookie_in_batch = batch->cookie_list;
3238                         batch->cookie_list = cookie;
3239                         batch->nactive++;
3240                         return (rc);
3241                 }
3242                 /*
3243                  * If state machine init failed then copy error to the caller
3244                  * and delete the cookie.
3245                  */
3246         } else {
3247                 (void) search_state_machine(cookie, INIT, 0);
3248         }
3249 
3250         /* Copy results back to user */
3251         rc = cookie->err_rc;
3252         if (rc != NS_LDAP_SUCCESS) {
3253                 if (conn_user != NULL && conn_user->ns_error != NULL) {
3254                         *errorp = conn_user->ns_error;
3255                         conn_user->ns_error = NULL;
3256                 } else {
3257                         *errorp = cookie->errorp;
3258                 }
3259         }
3260         *rResult = cookie->result;
3261         from_result = cookie->err_from_result;
3262 
3263         cookie->errorp = NULL;
3264         cookie->result = NULL;
3265         delete_search_cookie(cookie);
3266         cookie = NULL;
3267 
3268         if (from_result == 0 && *rResult == NULL)
3269                 rc = NS_LDAP_NOTFOUND;
3270         *rcp = rc;
3271         return (rc);
3272 }
3273 
3274 
3275 /*
3276  * __ns_ldap_list performs one or more LDAP searches to a given
3277  * directory server using service search descriptors and schema
3278  * mapping as appropriate. The operation may be retried a
3279  * couple of times in error situations.
3280  */
3281 int
3282 __ns_ldap_list(
3283         const char *service,
3284         const char *filter,
3285         int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3286         char **realfilter, const void *userdata),
3287         const char * const *attribute,
3288         const ns_cred_t *auth,
3289         const int flags,
3290         ns_ldap_result_t **rResult, /* return result entries */
3291         ns_ldap_error_t **errorp,
3292         int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3293         const void *userdata)
3294 {
3295         int mod_flags;
3296         /*
3297          * Strip the NS_LDAP_PAGE_CTRL option as this interface does not
3298          * support this. If you want to use this option call the API
3299          * __ns_ldap_list_sort() with has the sort attribute.
3300          */
3301         mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3302 
3303         return (__ns_ldap_list_sort(service, filter, NULL, init_filter_cb,
3304             attribute, auth, mod_flags, rResult, errorp,
3305             callback, userdata));
3306 }
3307 
3308 /*
3309  * __ns_ldap_list_sort performs one or more LDAP searches to a given
3310  * directory server using service search descriptors and schema
3311  * mapping as appropriate. The operation may be retried a
3312  * couple of times in error situations.
3313  */
3314 int
3315 __ns_ldap_list_sort(
3316         const char *service,
3317         const char *filter,
3318         const char *sortattr,
3319         int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3320         char **realfilter, const void *userdata),
3321         const char * const *attribute,
3322         const ns_cred_t *auth,
3323         const int flags,
3324         ns_ldap_result_t **rResult, /* return result entries */
3325         ns_ldap_error_t **errorp,
3326         int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3327         const void *userdata)
3328 {
3329         ns_conn_user_t  *cu = NULL;
3330         int             try_cnt = 0;
3331         int             rc = NS_LDAP_SUCCESS, trc;
3332 
3333         for (;;) {
3334                 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3335                     &try_cnt, &rc, errorp) == 0)
3336                         break;
3337                 rc = ldap_list(NULL, service, filter, sortattr, init_filter_cb,
3338                     attribute, auth, flags, rResult, errorp, &trc, callback,
3339                     userdata, cu);
3340         }
3341 
3342         return (rc);
3343 }
3344 
3345 /*
3346  * Create and initialize batch for native LDAP lookups
3347  */
3348 int
3349 __ns_ldap_list_batch_start(ns_ldap_list_batch_t **batch)
3350 {
3351         *batch = calloc(1, sizeof (ns_ldap_list_batch_t));
3352         if (*batch == NULL)
3353                 return (NS_LDAP_MEMORY);
3354         return (NS_LDAP_SUCCESS);
3355 }
3356 
3357 
3358 /*
3359  * Add a LDAP search request to the batch.
3360  */
3361 int
3362 __ns_ldap_list_batch_add(
3363         ns_ldap_list_batch_t *batch,
3364         const char *service,
3365         const char *filter,
3366         int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3367         char **realfilter, const void *userdata),
3368         const char * const *attribute,
3369         const ns_cred_t *auth,
3370         const int flags,
3371         ns_ldap_result_t **rResult, /* return result entries */
3372         ns_ldap_error_t **errorp,
3373         int *rcp,
3374         int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3375         const void *userdata)
3376 {
3377         ns_conn_user_t  *cu;
3378         int             rc;
3379         int             mod_flags;
3380 
3381         cu =  __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, 0);
3382         if (cu == NULL) {
3383                 if (rcp != NULL)
3384                         *rcp = NS_LDAP_MEMORY;
3385                 return (NS_LDAP_MEMORY);
3386         }
3387 
3388         /*
3389          * Strip the NS_LDAP_PAGE_CTRL option as the batch interface does not
3390          * support this.
3391          */
3392         mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3393 
3394         rc = ldap_list(batch, service, filter, NULL, init_filter_cb, attribute,
3395             auth, mod_flags, rResult, errorp, rcp, callback, userdata, cu);
3396 
3397         /*
3398          * Free the conn_user if the cookie was not batched. If the cookie
3399          * was batched then __ns_ldap_list_batch_end or release will free the
3400          * conn_user. The batch API instructs the search_state_machine
3401          * to reinit and retry (max 3 times) on retriable LDAP errors.
3402          */
3403         if (rc != NS_LDAP_SUCCESS && cu != NULL) {
3404                 if (cu->conn_mt != NULL)
3405                         __s_api_conn_mt_return(cu);
3406                 __s_api_conn_user_free(cu);
3407         }
3408         return (rc);
3409 }
3410 
3411 
3412 /*
3413  * Free batch.
3414  */
3415 void
3416 __ns_ldap_list_batch_release(ns_ldap_list_batch_t *batch)
3417 {
3418         ns_ldap_cookie_t        *c, *next;
3419 
3420         for (c = batch->cookie_list; c != NULL; c = next) {
3421                 next = c->next_cookie_in_batch;
3422                 if (c->conn_user != NULL) {
3423                         if (c->conn_user->conn_mt != NULL)
3424                                 __s_api_conn_mt_return(c->conn_user);
3425                         __s_api_conn_user_free(c->conn_user);
3426                         c->conn_user = NULL;
3427                 }
3428                 delete_search_cookie(c);
3429         }
3430         free(batch);
3431 }
3432 
3433 #define LD_USING_STATE(st) \
3434         ((st == DO_SEARCH) || (st == MULTI_RESULT) || (st == NEXT_RESULT))
3435 
3436 /*
3437  * Process batch. Everytime this function is called it selects an
3438  * active cookie from the batch and single steps through the
3439  * search_state_machine for the selected cookie. If lookup associated
3440  * with the cookie is complete (success or error) then the cookie is
3441  * removed from the batch and its memory freed.
3442  *
3443  * Returns 1 (if batch still has active cookies)
3444  *         0 (if batch has no more active cookies)
3445  *        -1 (on errors, *rcp will contain the error code)
3446  *
3447  * The caller should call this function in a loop as long as it returns 1
3448  * to process all the requests added to the batch. The results (and errors)
3449  * will be available in the locations provided by the caller at the time of
3450  * __ns_ldap_list_batch_add().
3451  */
3452 static
3453 int
3454 __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
3455 {
3456         ns_ldap_cookie_t        *c, *ptr, **prev;
3457         ns_state_t              state;
3458         ns_ldap_error_t         *errorp = NULL;
3459         int                     rc;
3460 
3461         /* Check if are already done */
3462         if (batch->nactive == 0)
3463                 return (0);
3464 
3465         /* Get the next cookie from the batch */
3466         c = (batch->next_cookie == NULL) ?
3467             batch->cookie_list : batch->next_cookie;
3468 
3469         batch->next_cookie = c->next_cookie_in_batch;
3470 
3471         /*
3472          * Checks the status of the cookie's connection if it needs
3473          * to use that connection for ldap_search_ext or ldap_result.
3474          * If the connection is no longer good but worth retrying
3475          * then reinit the search_state_machine for this cookie
3476          * starting from the first search descriptor. REINIT will
3477          * clear any leftover results if max retries have not been
3478          * reached and redo the search (which may also involve
3479          * following referrals again).
3480          *
3481          * Note that each cookie in the batch will make this
3482          * determination when it reaches one of the LD_USING_STATES.
3483          */
3484         if (LD_USING_STATE(c->new_state) && c->conn_user != NULL) {
3485                 rc = __s_api_setup_getnext(c->conn_user, &c->err_rc, &errorp);
3486                 if (rc == LDAP_BUSY || rc == LDAP_UNAVAILABLE ||
3487                     rc == LDAP_UNWILLING_TO_PERFORM) {
3488                         if (errorp != NULL) {
3489                                 (void) __ns_ldap_freeError(&c->errorp);
3490                                 c->errorp = errorp;
3491                         }
3492                         c->new_state = REINIT;
3493                 } else if (rc == LDAP_CONNECT_ERROR ||
3494                     rc == LDAP_SERVER_DOWN) {
3495                         if (errorp != NULL) {
3496                                 (void) __ns_ldap_freeError(&c->errorp);
3497                                 c->errorp = errorp;
3498                         }
3499                         c->new_state = REINIT;
3500                         /*
3501                          * MT connection is not usable,
3502                          * close it before REINIT.
3503                          */
3504                         __s_api_conn_mt_close(
3505                             c->conn_user, rc, NULL);
3506                 } else if (rc != NS_LDAP_SUCCESS) {
3507                         if (rcp != NULL)
3508                                 *rcp = rc;
3509                         *c->caller_result = NULL;
3510                         *c->caller_errorp = errorp;
3511                         *c->caller_rc = rc;
3512                         return (-1);
3513                 }
3514         }
3515 
3516         for (;;) {
3517                 /* Single step through the search_state_machine */
3518                 state = search_state_machine(c, c->new_state, ONE_STEP);
3519                 switch (state) {
3520                 case LDAP_ERROR:
3521                         (void) search_state_machine(c, state, ONE_STEP);
3522                         (void) search_state_machine(c, CLEAR_RESULTS, ONE_STEP);
3523                         /* FALLTHROUGH */
3524                 case ERROR:
3525                 case EXIT:
3526                         *c->caller_result = c->result;
3527                         *c->caller_errorp = c->errorp;
3528                         *c->caller_rc =
3529                             (c->result == NULL && c->err_from_result == 0)
3530                             ? NS_LDAP_NOTFOUND : c->err_rc;
3531                         c->result = NULL;
3532                         c->errorp = NULL;
3533                         /* Remove the cookie from the batch */
3534                         ptr = batch->cookie_list;
3535                         prev = &batch->cookie_list;
3536                         while (ptr != NULL) {
3537                                 if (ptr == c) {
3538                                         *prev = ptr->next_cookie_in_batch;
3539                                         break;
3540                                 }
3541                                 prev = &ptr->next_cookie_in_batch;
3542                                 ptr = ptr->next_cookie_in_batch;
3543                         }
3544                         /* Delete cookie and decrement active cookie count */
3545                         if (c->conn_user != NULL) {
3546                                 if (c->conn_user->conn_mt != NULL)
3547                                         __s_api_conn_mt_return(c->conn_user);
3548                                 __s_api_conn_user_free(c->conn_user);
3549                                 c->conn_user = NULL;
3550                         }
3551                         delete_search_cookie(c);
3552                         batch->nactive--;
3553                         break;
3554                 case NEXT_RESULT:
3555                 case MULTI_RESULT:
3556                         /*
3557                          * This means that search_state_machine needs to do
3558                          * another ldap_result() for the cookie in question.
3559                          * We only do at most one ldap_result() per call in
3560                          * this function and therefore we return. This allows
3561                          * the caller to process results from other cookies
3562                          * in the batch without getting tied up on just one
3563                          * cookie.
3564                          */
3565                         break;
3566                 default:
3567                         /*
3568                          * This includes states that follow NEXT_RESULT or
3569                          * MULTI_RESULT such as PROCESS_RESULT and
3570                          * END_PROCESS_RESULT. We continue processing
3571                          * this cookie till we reach either the error, exit
3572                          * or the result states.
3573                          */
3574                         continue;
3575                 }
3576                 break;
3577         }
3578 
3579         /* Return 0 if no more cookies left otherwise 1 */
3580         return ((batch->nactive > 0) ? 1 : 0);
3581 }
3582 
3583 
3584 /*
3585  * Process all the active cookies in the batch and when none
3586  * remains finalize the batch.
3587  */
3588 int
3589 __ns_ldap_list_batch_end(ns_ldap_list_batch_t *batch)
3590 {
3591         int rc = NS_LDAP_SUCCESS;
3592         while (__ns_ldap_list_batch_process(batch, &rc) > 0)
3593                 ;
3594         __ns_ldap_list_batch_release(batch);
3595         return (rc);
3596 }
3597 
3598 /*
3599  * find_domainname performs one or more LDAP searches to
3600  * find the value of the nisdomain attribute associated with
3601  * the input DN (with no retry).
3602  */
3603 
3604 static int
3605 find_domainname(const char *dn, char **domainname, const ns_cred_t *cred,
3606     ns_ldap_error_t **errorp, ns_conn_user_t *conn_user)
3607 {
3608 
3609         ns_ldap_cookie_t        *cookie;
3610         ns_ldap_search_desc_t   **sdlist;
3611         ns_ldap_search_desc_t   *dptr;
3612         int                     rc;
3613         char                    **value;
3614         int                     flags = 0;
3615 
3616         *domainname = NULL;
3617         *errorp = NULL;
3618 
3619         /* Initialize State machine cookie */
3620         cookie = init_search_state_machine();
3621         if (cookie == NULL) {
3622                 return (NS_LDAP_MEMORY);
3623         }
3624         cookie->conn_user = conn_user;
3625 
3626         /* see if need to follow referrals */
3627         rc = __s_api_toFollowReferrals(flags,
3628             &cookie->followRef, errorp);
3629         if (rc != NS_LDAP_SUCCESS) {
3630                 delete_search_cookie(cookie);
3631                 return (rc);
3632         }
3633 
3634         /* Create default service Desc */
3635         sdlist = (ns_ldap_search_desc_t **)calloc(2,
3636             sizeof (ns_ldap_search_desc_t *));
3637         if (sdlist == NULL) {
3638                 delete_search_cookie(cookie);
3639                 cookie = NULL;
3640                 return (NS_LDAP_MEMORY);
3641         }
3642         dptr = (ns_ldap_search_desc_t *)
3643             calloc(1, sizeof (ns_ldap_search_desc_t));
3644         if (dptr == NULL) {
3645                 free(sdlist);
3646                 delete_search_cookie(cookie);
3647                 cookie = NULL;
3648                 return (NS_LDAP_MEMORY);
3649         }
3650         sdlist[0] = dptr;
3651 
3652         /* search base is dn */
3653         dptr->basedn = strdup(dn);
3654 
3655         /* search scope is base */
3656         dptr->scope = NS_LDAP_SCOPE_BASE;
3657 
3658         /* search filter is "nisdomain=*" */
3659         dptr->filter = strdup(_NIS_FILTER);
3660 
3661         cookie->sdlist = sdlist;
3662         cookie->i_filter = strdup(dptr->filter);
3663         cookie->i_attr = nis_domain_attrs;
3664         cookie->i_auth = cred;
3665         cookie->i_flags = 0;
3666 
3667         /* Process search */
3668         rc = search_state_machine(cookie, INIT, 0);
3669 
3670         /* Copy domain name if found */
3671         rc = cookie->err_rc;
3672         if (rc != NS_LDAP_SUCCESS) {
3673                 if (conn_user != NULL && conn_user->ns_error != NULL) {
3674                         *errorp = conn_user->ns_error;
3675                         conn_user->ns_error = NULL;
3676                 } else {
3677                         *errorp = cookie->errorp;
3678                 }
3679         }
3680         if (cookie->result == NULL)
3681                 rc = NS_LDAP_NOTFOUND;
3682         if (rc == NS_LDAP_SUCCESS) {
3683                 value = __ns_ldap_getAttr(cookie->result->entry,
3684                     _NIS_DOMAIN);
3685                 if (value[0])
3686                         *domainname = strdup(value[0]);
3687                 else
3688                         rc = NS_LDAP_NOTFOUND;
3689         }
3690         if (cookie->result != NULL)
3691                 (void) __ns_ldap_freeResult(&cookie->result);
3692         cookie->errorp = NULL;
3693         delete_search_cookie(cookie);
3694         cookie = NULL;
3695         return (rc);
3696 }
3697 
3698 /*
3699  * __s_api_find_domainname performs one or more LDAP searches to
3700  * find the value of the nisdomain attribute associated with
3701  * the input DN (with retry).
3702  */
3703 
3704 static int
3705 __s_api_find_domainname(const char *dn, char **domainname,
3706     const ns_cred_t *cred, ns_ldap_error_t **errorp)
3707 {
3708         ns_conn_user_t  *cu = NULL;
3709         int             try_cnt = 0;
3710         int             rc = NS_LDAP_SUCCESS;
3711 
3712         for (;;) {
3713                 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3714                     &try_cnt, &rc, errorp) == 0)
3715                         break;
3716                 rc = find_domainname(dn, domainname, cred, errorp, cu);
3717         }
3718 
3719         return (rc);
3720 }
3721 
3722 static int
3723 firstEntry(
3724     const char *service,
3725     const char *filter,
3726     const char *sortattr,
3727     int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3728     char **realfilter, const void *userdata),
3729     const char * const *attribute,
3730     const ns_cred_t *auth,
3731     const int flags,
3732     void **vcookie,
3733     ns_ldap_result_t **result,
3734     ns_ldap_error_t ** errorp,
3735     const void *userdata,
3736     ns_conn_user_t *conn_user)
3737 {
3738         ns_ldap_cookie_t        *cookie = NULL;
3739         ns_ldap_error_t         *error = NULL;
3740         ns_state_t              state;
3741         ns_ldap_search_desc_t   **sdlist;
3742         ns_ldap_search_desc_t   *dptr;
3743         char                    **dns = NULL;
3744         int                     scope;
3745         int                     rc;
3746 
3747         *errorp = NULL;
3748         *result = NULL;
3749 
3750         /*
3751          * Sanity check - NS_LDAP_READ_SHADOW is for our
3752          * own internal use.
3753          */
3754         if (flags & NS_LDAP_READ_SHADOW)
3755                 return (NS_LDAP_INVALID_PARAM);
3756 
3757         /* get the service descriptor - or create a default one */
3758         rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3759             &sdlist, &error);
3760         if (rc != NS_LDAP_SUCCESS) {
3761                 *errorp = error;
3762                 return (rc);
3763         }
3764         if (sdlist == NULL) {
3765                 /* Create default service Desc */
3766                 sdlist = (ns_ldap_search_desc_t **)calloc(2,
3767                     sizeof (ns_ldap_search_desc_t *));
3768                 if (sdlist == NULL) {
3769                         return (NS_LDAP_MEMORY);
3770                 }
3771                 dptr = (ns_ldap_search_desc_t *)
3772                     calloc(1, sizeof (ns_ldap_search_desc_t));
3773                 if (dptr == NULL) {
3774                         free(sdlist);
3775                         return (NS_LDAP_MEMORY);
3776                 }
3777                 sdlist[0] = dptr;
3778 
3779                 /* default base */
3780                 rc = __s_api_getDNs(&dns, service, &error);
3781                 if (rc != NS_LDAP_SUCCESS) {
3782                         if (dns) {
3783                                 __s_api_free2dArray(dns);
3784                                 dns = NULL;
3785                         }
3786                         if (sdlist) {
3787                                 (void) __ns_ldap_freeSearchDescriptors(
3788                                     &sdlist);
3789 
3790                                 sdlist = NULL;
3791                         }
3792                         *errorp = error;
3793                         return (rc);
3794                 }
3795                 dptr->basedn = strdup(dns[0]);
3796                 __s_api_free2dArray(dns);
3797                 dns = NULL;
3798 
3799                 /* default scope */
3800                 scope = 0;
3801                 cookie = init_search_state_machine();
3802                 if (cookie == NULL) {
3803                         if (sdlist) {
3804                                 (void) __ns_ldap_freeSearchDescriptors(&sdlist);
3805                                 sdlist = NULL;
3806                         }
3807                         return (NS_LDAP_MEMORY);
3808                 }
3809                 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3810                 dptr->scope = scope;
3811         }
3812 
3813         /* Initialize State machine cookie */
3814         if (cookie == NULL)
3815                 cookie = init_search_state_machine();
3816         if (cookie == NULL) {
3817                 if (sdlist) {
3818                         (void) __ns_ldap_freeSearchDescriptors(&sdlist);
3819                         sdlist = NULL;
3820                 }
3821                 return (NS_LDAP_MEMORY);
3822         }
3823 
3824         /* identify self as a getent user */
3825         cookie->conn_user = conn_user;
3826 
3827         cookie->sdlist = sdlist;
3828 
3829         /* see if need to follow referrals */
3830         rc = __s_api_toFollowReferrals(flags,
3831             &cookie->followRef, errorp);
3832         if (rc != NS_LDAP_SUCCESS) {
3833                 delete_search_cookie(cookie);
3834                 return (rc);
3835         }
3836 
3837         /*
3838          * use VLV/PAGE control only if NS_LDAP_NO_PAGE_CTRL is not set
3839          */
3840         if (flags & NS_LDAP_NO_PAGE_CTRL)
3841                 cookie->use_paging = FALSE;
3842         else
3843                 cookie->use_paging = TRUE;
3844 
3845         /* Set up other arguments */
3846         cookie->userdata = userdata;
3847         if (init_filter_cb != NULL) {
3848                 cookie->init_filter_cb = init_filter_cb;
3849                 cookie->use_filtercb = 1;
3850         }
3851         cookie->use_usercb = 0;
3852         /* check_shadow() may add extra value to cookie->i_flags */
3853         cookie->i_flags = flags;
3854         if (service) {
3855                 cookie->service = strdup(service);
3856                 if (cookie->service == NULL) {
3857                         delete_search_cookie(cookie);
3858                         return (NS_LDAP_MEMORY);
3859                 }
3860 
3861                 /*
3862                  * If given, use the credential given by the caller, and
3863                  * skip the credential check required for shadow update.
3864                  */
3865                 if (auth == NULL) {
3866                         rc = check_shadow(cookie, service);
3867                         if (rc != NS_LDAP_SUCCESS) {
3868                                 *errorp = cookie->errorp;
3869                                 cookie->errorp = NULL;
3870                                 delete_search_cookie(cookie);
3871                                 cookie = NULL;
3872                                 return (rc);
3873                         }
3874                 }
3875         }
3876 
3877         cookie->i_filter = strdup(filter);
3878         cookie->i_attr = attribute;
3879         cookie->i_sortattr = sortattr;
3880         cookie->i_auth = auth;
3881 
3882         state = INIT;
3883         for (;;) {
3884                 state = search_state_machine(cookie, state, ONE_STEP);
3885                 switch (state) {
3886                 case PROCESS_RESULT:
3887                         *result = cookie->result;
3888                         cookie->result = NULL;
3889                         *vcookie = (void *)cookie;
3890                         return (NS_LDAP_SUCCESS);
3891                 case LDAP_ERROR:
3892                         state = search_state_machine(cookie, state, ONE_STEP);
3893                         state = search_state_machine(cookie, CLEAR_RESULTS,
3894                             ONE_STEP);
3895                         /* FALLTHROUGH */
3896                 case ERROR:
3897                         rc = cookie->err_rc;
3898                         if (conn_user != NULL && conn_user->ns_error != NULL) {
3899                                 *errorp = conn_user->ns_error;
3900                                 conn_user->ns_error = NULL;
3901                         } else {
3902                                 *errorp = cookie->errorp;
3903                                 cookie->errorp = NULL;
3904                         }
3905                         delete_search_cookie(cookie);
3906                         return (rc);
3907                 case EXIT:
3908                         rc = cookie->err_rc;
3909                         if (rc != NS_LDAP_SUCCESS) {
3910                                 *errorp = cookie->errorp;
3911                                 cookie->errorp = NULL;
3912                         } else {
3913                                 rc = NS_LDAP_NOTFOUND;
3914                         }
3915 
3916                         delete_search_cookie(cookie);
3917                         return (rc);
3918 
3919                 default:
3920                         break;
3921                 }
3922         }
3923 }
3924 
3925 int
3926 __ns_ldap_firstEntry(
3927     const char *service,
3928     const char *filter,
3929     const char *vlv_sort,
3930     int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3931     char **realfilter, const void *userdata),
3932     const char * const *attribute,
3933     const ns_cred_t *auth,
3934     const int flags,
3935     void **vcookie,
3936     ns_ldap_result_t **result,
3937     ns_ldap_error_t ** errorp,
3938     const void *userdata)
3939 {
3940         ns_conn_user_t  *cu = NULL;
3941         int             try_cnt = 0;
3942         int             rc = NS_LDAP_SUCCESS;
3943 
3944         for (;;) {
3945                 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_GETENT,
3946                     &try_cnt, &rc, errorp) == 0)
3947                         break;
3948                 rc = firstEntry(service, filter, vlv_sort, init_filter_cb,
3949                     attribute, auth, flags, vcookie, result, errorp, userdata,
3950                     cu);
3951         }
3952         return (rc);
3953 }
3954 
3955 /*ARGSUSED2*/
3956 int
3957 __ns_ldap_nextEntry(void *vcookie, ns_ldap_result_t **result,
3958     ns_ldap_error_t ** errorp)
3959 {
3960         ns_ldap_cookie_t        *cookie;
3961         ns_state_t              state;
3962         int                     rc;
3963 
3964         cookie = (ns_ldap_cookie_t *)vcookie;
3965         cookie->result = NULL;
3966         *result = NULL;
3967 
3968         if (cookie->conn_user != NULL) {
3969                 rc = __s_api_setup_getnext(cookie->conn_user,
3970                     &cookie->err_rc, errorp);
3971                 if (rc != NS_LDAP_SUCCESS)
3972                         return (rc);
3973         }
3974 
3975         state = END_PROCESS_RESULT;
3976         for (;;) {
3977                 state = search_state_machine(cookie, state, ONE_STEP);
3978                 switch (state) {
3979                 case PROCESS_RESULT:
3980                         *result = cookie->result;
3981                         cookie->result = NULL;
3982                         return (NS_LDAP_SUCCESS);
3983                 case LDAP_ERROR:
3984                         state = search_state_machine(cookie, state, ONE_STEP);
3985                         state = search_state_machine(cookie, CLEAR_RESULTS,
3986                             ONE_STEP);
3987                         /* FALLTHROUGH */
3988                 case ERROR:
3989                         rc = cookie->err_rc;
3990                         *errorp = cookie->errorp;
3991                         cookie->errorp = NULL;
3992                         return (rc);
3993                 case EXIT:
3994                         return (NS_LDAP_SUCCESS);
3995                 }
3996         }
3997 }
3998 
3999 int
4000 __ns_ldap_endEntry(
4001         void **vcookie,
4002         ns_ldap_error_t ** errorp)
4003 {
4004         ns_ldap_cookie_t        *cookie;
4005         int                     rc;
4006 
4007         if (*vcookie == NULL)
4008                 return (NS_LDAP_INVALID_PARAM);
4009 
4010         cookie = (ns_ldap_cookie_t *)(*vcookie);
4011         cookie->result = NULL;
4012 
4013         /* Complete search */
4014         rc = search_state_machine(cookie, CLEAR_RESULTS, 0);
4015 
4016         /* Copy results back to user */
4017         rc = cookie->err_rc;
4018         if (rc != NS_LDAP_SUCCESS)
4019                 *errorp = cookie->errorp;
4020 
4021         cookie->errorp = NULL;
4022         if (cookie->conn_user != NULL) {
4023                 if (cookie->conn_user->conn_mt != NULL)
4024                         __s_api_conn_mt_return(cookie->conn_user);
4025                 __s_api_conn_user_free(cookie->conn_user);
4026         }
4027         delete_search_cookie(cookie);
4028         cookie = NULL;
4029         *vcookie = NULL;
4030 
4031         return (rc);
4032 }
4033 
4034 
4035 int
4036 __ns_ldap_freeResult(ns_ldap_result_t **result)
4037 {
4038 
4039         ns_ldap_entry_t *curEntry = NULL;
4040         ns_ldap_entry_t *delEntry = NULL;
4041         int             i;
4042         ns_ldap_result_t        *res = *result;
4043 
4044 #ifdef DEBUG
4045         (void) fprintf(stderr, "__ns_ldap_freeResult START\n");
4046 #endif
4047         if (res == NULL)
4048                 return (NS_LDAP_INVALID_PARAM);
4049 
4050         if (res->entry != NULL)
4051                 curEntry = res->entry;
4052 
4053         for (i = 0; i < res->entries_count; i++) {
4054                 if (curEntry != NULL) {
4055                         delEntry = curEntry;
4056                         curEntry = curEntry->next;
4057                         __ns_ldap_freeEntry(delEntry);
4058                 }
4059         }
4060 
4061         free(res);
4062         *result = NULL;
4063         return (NS_LDAP_SUCCESS);
4064 }
4065 
4066 int
4067 __ns_ldap_auth(const ns_cred_t *auth, const int flags, ns_ldap_error_t **errorp,
4068     LDAPControl **serverctrls __unused, LDAPControl **clientctrls __unused)
4069 {
4070 
4071         ConnectionID    connectionId = -1;
4072         Connection      *conp;
4073         int             rc = 0;
4074         int             do_not_fail_if_new_pwd_reqd = 0;
4075         int             nopasswd_acct_mgmt = 0;
4076         ns_conn_user_t  *conn_user;
4077 
4078 
4079 #ifdef DEBUG
4080         (void) fprintf(stderr, "__ns_ldap_auth START\n");
4081 #endif
4082 
4083         *errorp = NULL;
4084         if (auth == NULL)
4085                 return (NS_LDAP_INVALID_PARAM);
4086 
4087         conn_user = __s_api_conn_user_init(NS_CONN_USER_AUTH,
4088             NULL, B_FALSE);
4089 
4090         rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN,
4091             auth, &connectionId, &conp, errorp,
4092             do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
4093             conn_user);
4094 
4095         if (conn_user != NULL)
4096                 __s_api_conn_user_free(conn_user);
4097 
4098         if (rc == NS_LDAP_OP_FAILED && *errorp)
4099                 (void) __ns_ldap_freeError(errorp);
4100 
4101         if (connectionId > -1)
4102                 DropConnection(connectionId, flags);
4103         return (rc);
4104 }
4105 
4106 char **
4107 __ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname)
4108 {
4109         int     i;
4110 
4111         if (entry == NULL)
4112                 return (NULL);
4113         for (i = 0; i < entry->attr_count; i++) {
4114                 if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == 0)
4115                         return (entry->attr_pair[i]->attrvalue);
4116         }
4117         return (NULL);
4118 }
4119 
4120 ns_ldap_attr_t *
4121 __ns_ldap_getAttrStruct(const ns_ldap_entry_t *entry, const char *attrname)
4122 {
4123         int     i;
4124 
4125         if (entry == NULL)
4126                 return (NULL);
4127         for (i = 0; i < entry->attr_count; i++) {
4128                 if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == 0)
4129                         return (entry->attr_pair[i]);
4130         }
4131         return (NULL);
4132 }
4133 
4134 
4135 int
4136 __ns_ldap_uid2dn(const char *uid, char **userDN, const ns_cred_t *cred,
4137     ns_ldap_error_t **errorp)
4138 {
4139         ns_ldap_result_t        *result = NULL;
4140         char            *filter, *userdata;
4141         char            errstr[MAXERROR];
4142         char            **value;
4143         int             rc = 0;
4144         int             i;
4145         size_t          len;
4146 
4147         *errorp = NULL;
4148         *userDN = NULL;
4149         if ((uid == NULL) || (uid[0] == '\0'))
4150                 return (NS_LDAP_INVALID_PARAM);
4151 
4152         for (i = 0; uid[i] != '\0'; i++) {
4153                 if (uid[i] == '=') {
4154                         *userDN = strdup(uid);
4155                         return (NS_LDAP_SUCCESS);
4156                 }
4157         }
4158         for (i = 0; (uid[i] != '\0') && isdigit(uid[i]); i++)
4159                 ;
4160         if (uid[i] == '\0') {
4161                 len = strlen(UIDNUMFILTER) + strlen(uid) + 1;
4162                 filter = malloc(len);
4163                 if (filter == NULL) {
4164                         *userDN = NULL;
4165                         return (NS_LDAP_MEMORY);
4166                 }
4167                 (void) snprintf(filter, len, UIDNUMFILTER, uid);
4168 
4169                 len = strlen(UIDNUMFILTER_SSD) + strlen(uid) + 1;
4170                 userdata = malloc(len);
4171                 if (userdata == NULL) {
4172                         *userDN = NULL;
4173                         free(filter);
4174                         return (NS_LDAP_MEMORY);
4175                 }
4176                 (void) snprintf(userdata, len, UIDNUMFILTER_SSD, uid);
4177         } else {
4178                 len = strlen(UIDFILTER) + strlen(uid) + 1;
4179                 filter = malloc(len);
4180                 if (filter == NULL) {
4181                         *userDN = NULL;
4182                         return (NS_LDAP_MEMORY);
4183                 }
4184                 (void) snprintf(filter, len, UIDFILTER, uid);
4185 
4186                 len = strlen(UIDFILTER_SSD) + strlen(uid) + 1;
4187                 userdata = malloc(len);
4188                 if (userdata == NULL) {
4189                         *userDN = NULL;
4190                         free(filter);
4191                         return (NS_LDAP_MEMORY);
4192                 }
4193                 (void) snprintf(userdata, len, UIDFILTER_SSD, uid);
4194         }
4195 
4196         /*
4197          * we want to retrieve the DN as it appears in LDAP
4198          * hence the use of NS_LDAP_NOT_CVT_DN in flags
4199          */
4200         rc = __ns_ldap_list("passwd", filter,
4201             __s_api_merge_SSD_filter,
4202             NULL, cred, NS_LDAP_NOT_CVT_DN,
4203             &result, errorp, NULL,
4204             userdata);
4205         free(filter);
4206         filter = NULL;
4207         free(userdata);
4208         userdata = NULL;
4209         if (rc != NS_LDAP_SUCCESS) {
4210                 if (result) {
4211                         (void) __ns_ldap_freeResult(&result);
4212                         result = NULL;
4213                 }
4214                 return (rc);
4215         }
4216         if (result->entries_count > 1) {
4217                 (void) __ns_ldap_freeResult(&result);
4218                 result = NULL;
4219                 *userDN = NULL;
4220                 (void) sprintf(errstr,
4221                     gettext("Too many entries are returned for %s"), uid);
4222                 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4223                     NS_LDAP_MEMORY);
4224                 return (NS_LDAP_INTERNAL);
4225         }
4226 
4227         value = __ns_ldap_getAttr(result->entry, "dn");
4228         *userDN = strdup(value[0]);
4229         (void) __ns_ldap_freeResult(&result);
4230         result = NULL;
4231         return (NS_LDAP_SUCCESS);
4232 }
4233 
4234 #define _P_UID  "uid"
4235 static const char *dn2uid_attrs[] = {
4236         _P_CN,
4237         _P_UID,
4238         (char *)NULL
4239 };
4240 
4241 int
4242 __ns_ldap_dn2uid(const char *dn, char **userID, const ns_cred_t *cred,
4243     ns_ldap_error_t **errorp)
4244 {
4245         ns_ldap_result_t        *result = NULL;
4246         char            *filter, *userdata;
4247         char            errstr[MAXERROR];
4248         char            **value;
4249         int             rc = 0;
4250         size_t          len;
4251 
4252         *errorp = NULL;
4253         *userID = NULL;
4254         if ((dn == NULL) || (dn[0] == '\0'))
4255                 return (NS_LDAP_INVALID_PARAM);
4256 
4257         len = strlen(UIDDNFILTER) + strlen(dn) + 1;
4258         filter = malloc(len);
4259         if (filter == NULL) {
4260                 return (NS_LDAP_MEMORY);
4261         }
4262         (void) snprintf(filter, len, UIDDNFILTER, dn);
4263 
4264         len = strlen(UIDDNFILTER_SSD) + strlen(dn) + 1;
4265         userdata = malloc(len);
4266         if (userdata == NULL) {
4267                 free(filter);
4268                 return (NS_LDAP_MEMORY);
4269         }
4270         (void) snprintf(userdata, len, UIDDNFILTER_SSD, dn);
4271 
4272         /*
4273          * Unlike uid2dn, we DO want attribute mapping, so that
4274          * "uid" is mapped to/from samAccountName, for example.
4275          */
4276         rc = __ns_ldap_list("passwd", filter,
4277             __s_api_merge_SSD_filter,
4278             dn2uid_attrs, cred, 0,
4279             &result, errorp, NULL,
4280             userdata);
4281         free(filter);
4282         filter = NULL;
4283         free(userdata);
4284         userdata = NULL;
4285         if (rc != NS_LDAP_SUCCESS)
4286                 goto out;
4287 
4288         if (result->entries_count > 1) {
4289                 (void) sprintf(errstr,
4290                     gettext("Too many entries are returned for %s"), dn);
4291                 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4292                     NS_LDAP_MEMORY);
4293                 rc = NS_LDAP_INTERNAL;
4294                 goto out;
4295         }
4296 
4297         value = __ns_ldap_getAttr(result->entry, _P_UID);
4298         if (value == NULL || value[0] == NULL) {
4299                 rc = NS_LDAP_NOTFOUND;
4300                 goto out;
4301         }
4302 
4303         *userID = strdup(value[0]);
4304         rc = NS_LDAP_SUCCESS;
4305 
4306 out:
4307         (void) __ns_ldap_freeResult(&result);
4308         result = NULL;
4309         return (rc);
4310 }
4311 
4312 int
4313 __ns_ldap_host2dn(const char *host, const char *domain, char **hostDN,
4314     const ns_cred_t *cred, ns_ldap_error_t **errorp)
4315 {
4316         ns_ldap_result_t        *result = NULL;
4317         char            *filter, *userdata;
4318         char            errstr[MAXERROR];
4319         char            **value;
4320         int             rc;
4321         size_t          len;
4322 
4323         /*
4324          * XXX
4325          * the domain parameter needs to be used in case domain is not local,
4326          * if this routine is to support multi domain setups, it needs lots
4327          * of work...
4328          */
4329         *errorp = NULL;
4330         *hostDN = NULL;
4331         if ((host == NULL) || (host[0] == '\0'))
4332                 return (NS_LDAP_INVALID_PARAM);
4333 
4334         len = strlen(HOSTFILTER) + strlen(host) + 1;
4335         filter = malloc(len);
4336         if (filter == NULL) {
4337                 return (NS_LDAP_MEMORY);
4338         }
4339         (void) snprintf(filter, len, HOSTFILTER, host);
4340 
4341         len = strlen(HOSTFILTER_SSD) + strlen(host) + 1;
4342         userdata = malloc(len);
4343         if (userdata == NULL) {
4344                 free(filter);
4345                 return (NS_LDAP_MEMORY);
4346         }
4347         (void) snprintf(userdata, len, HOSTFILTER_SSD, host);
4348 
4349         /*
4350          * we want to retrieve the DN as it appears in LDAP
4351          * hence the use of NS_LDAP_NOT_CVT_DN in flags
4352          */
4353         rc = __ns_ldap_list("hosts", filter,
4354             __s_api_merge_SSD_filter,
4355             NULL, cred, NS_LDAP_NOT_CVT_DN, &result,
4356             errorp, NULL,
4357             userdata);
4358         free(filter);
4359         filter = NULL;
4360         free(userdata);
4361         userdata = NULL;
4362         if (rc != NS_LDAP_SUCCESS) {
4363                 if (result) {
4364                         (void) __ns_ldap_freeResult(&result);
4365                         result = NULL;
4366                 }
4367                 return (rc);
4368         }
4369 
4370         if (result->entries_count > 1) {
4371                 (void) __ns_ldap_freeResult(&result);
4372                 result = NULL;
4373                 *hostDN = NULL;
4374                 (void) sprintf(errstr,
4375                     gettext("Too many entries are returned for %s"), host);
4376                 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4377                     NS_LDAP_MEMORY);
4378                 return (NS_LDAP_INTERNAL);
4379         }
4380 
4381         value = __ns_ldap_getAttr(result->entry, "dn");
4382         *hostDN = strdup(value[0]);
4383         (void) __ns_ldap_freeResult(&result);
4384         result = NULL;
4385         return (NS_LDAP_SUCCESS);
4386 }
4387 
4388 int
4389 __ns_ldap_dn2domain(const char *dn, char **domain, const ns_cred_t *cred,
4390     ns_ldap_error_t **errorp)
4391 {
4392         int             rc, pnum, i, j, len = 0;
4393         char            *newdn, **rdns = NULL;
4394         char            **dns, *dn1;
4395 
4396         *errorp = NULL;
4397 
4398         if (domain == NULL)
4399                 return (NS_LDAP_INVALID_PARAM);
4400         else
4401                 *domain = NULL;
4402 
4403         if ((dn == NULL) || (dn[0] == '\0'))
4404                 return (NS_LDAP_INVALID_PARAM);
4405 
4406         /*
4407          * break dn into rdns
4408          */
4409         dn1 = strdup(dn);
4410         if (dn1 == NULL)
4411                 return (NS_LDAP_MEMORY);
4412         rdns = ldap_explode_dn(dn1, 0);
4413         free(dn1);
4414         if (rdns == NULL || *rdns == NULL)
4415                 return (NS_LDAP_INVALID_PARAM);
4416 
4417         for (i = 0; rdns[i]; i++)
4418                 len += strlen(rdns[i]) + 1;
4419         pnum = i;
4420 
4421         newdn = (char *)malloc(len + 1);
4422         dns = (char **)calloc(pnum, sizeof (char *));
4423         if (newdn == NULL || dns == NULL) {
4424                 if (newdn)
4425                         free(newdn);
4426                 ldap_value_free(rdns);
4427                 return (NS_LDAP_MEMORY);
4428         }
4429 
4430         /* construct a semi-normalized dn, newdn */
4431         *newdn = '\0';
4432         for (i = 0; rdns[i]; i++) {
4433                 dns[i] = newdn + strlen(newdn);
4434                 (void) strcat(newdn,
4435                     __s_api_remove_rdn_space(rdns[i]));
4436                 (void) strcat(newdn, ",");
4437         }
4438         /* remove the last ',' */
4439         newdn[strlen(newdn) - 1] = '\0';
4440         ldap_value_free(rdns);
4441 
4442         /*
4443          * loop and find the domain name associated with newdn,
4444          * removing rdn one by one from left to right
4445          */
4446         for (i = 0; i < pnum; i++) {
4447 
4448                 if (*errorp)
4449                         (void) __ns_ldap_freeError(errorp);
4450 
4451                 /*
4452                  *  try cache manager first
4453                  */
4454                 rc = __s_api_get_cachemgr_data(NS_CACHE_DN2DOMAIN,
4455                     dns[i], domain);
4456                 if (rc != NS_LDAP_SUCCESS) {
4457                         /*
4458                          *  try ldap server second
4459                          */
4460                         rc = __s_api_find_domainname(dns[i], domain,
4461                             cred, errorp);
4462                 } else {
4463                         /*
4464                          * skip the last one,
4465                          * since it is already cached by ldap_cachemgr
4466                          */
4467                         i--;
4468                 }
4469                 if (rc == NS_LDAP_SUCCESS) {
4470                         if (__s_api_nscd_proc()) {
4471                                 /*
4472                                  * If it's nscd, ask cache manager to save the
4473                                  * dn to domain mapping(s)
4474                                  */
4475                                 for (j = 0; j <= i; j++) {
4476                                         (void) __s_api_set_cachemgr_data(
4477                                             NS_CACHE_DN2DOMAIN,
4478                                             dns[j],
4479                                             *domain);
4480                                 }
4481                         }
4482                         break;
4483                 }
4484         }
4485 
4486         free(dns);
4487         free(newdn);
4488         if (rc != NS_LDAP_SUCCESS)
4489                 rc = NS_LDAP_NOTFOUND;
4490         return (rc);
4491 }
4492 
4493 int
4494 __ns_ldap_getServiceAuthMethods(const char *service, ns_auth_t ***auth,
4495     ns_ldap_error_t **errorp)
4496 {
4497         char            errstr[MAXERROR];
4498         int             rc, i;
4499         boolean_t       done = B_FALSE;
4500         int             slen;
4501         void            **param;
4502         char            **sam, *srv, *send;
4503         ns_auth_t       **authpp = NULL, *ap;
4504         int             cnt, max;
4505         ns_config_t     *cfg;
4506         ns_ldap_error_t *error = NULL;
4507 
4508         if (errorp == NULL)
4509                 return (NS_LDAP_INVALID_PARAM);
4510         *errorp = NULL;
4511 
4512         if ((service == NULL) || (service[0] == '\0') ||
4513             (auth == NULL))
4514                 return (NS_LDAP_INVALID_PARAM);
4515 
4516         *auth = NULL;
4517         rc = __ns_ldap_getParam(NS_LDAP_SERVICE_AUTH_METHOD_P, &param, &error);
4518         if (rc != NS_LDAP_SUCCESS || param == NULL) {
4519                 *errorp = error;
4520                 return (rc);
4521         }
4522         sam = (char **)param;
4523 
4524         cfg = __s_api_get_default_config();
4525         cnt = 0;
4526 
4527         slen = strlen(service);
4528 
4529         for (; *sam; sam++) {
4530                 srv = *sam;
4531                 if (strncasecmp(service, srv, slen) != 0)
4532                         continue;
4533                 srv += slen;
4534                 if (*srv != COLONTOK)
4535                         continue;
4536                 send = srv;
4537                 srv++;
4538                 for (max = 1; (send = strchr(++send, SEMITOK)) != NULL; max++)
4539                         ;
4540                 authpp = (ns_auth_t **)calloc(++max, sizeof (ns_auth_t *));
4541                 if (authpp == NULL) {
4542                         (void) __ns_ldap_freeParam(&param);
4543                         __s_api_release_config(cfg);
4544                         return (NS_LDAP_MEMORY);
4545                 }
4546                 while (!done) {
4547                         send = strchr(srv, SEMITOK);
4548                         if (send != NULL) {
4549                                 *send = '\0';
4550                                 send++;
4551                         }
4552                         i = __s_get_enum_value(cfg, srv, NS_LDAP_AUTH_P);
4553                         if (i == -1) {
4554                                 (void) __ns_ldap_freeParam(&param);
4555                                 (void) sprintf(errstr,
4556                                 gettext("Unsupported "
4557                                     "serviceAuthenticationMethod: %s.\n"), srv);
4558                                 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
4559                                     strdup(errstr), NS_LDAP_MEMORY);
4560                                 __s_api_release_config(cfg);
4561                                 return (NS_LDAP_CONFIG);
4562                         }
4563                         ap = __s_api_AuthEnumtoStruct((EnumAuthType_t)i);
4564                         if (ap == NULL) {
4565                                 (void) __ns_ldap_freeParam(&param);
4566                                 __s_api_release_config(cfg);
4567                                 return (NS_LDAP_MEMORY);
4568                         }
4569                         authpp[cnt++] = ap;
4570                         if (send == NULL)
4571                                 done = B_TRUE;
4572                         else
4573                                 srv = send;
4574                 }
4575         }
4576 
4577         *auth = authpp;
4578         (void) __ns_ldap_freeParam(&param);
4579         __s_api_release_config(cfg);
4580         return (NS_LDAP_SUCCESS);
4581 }
4582 
4583 /*
4584  * This routine is called when certain scenario occurs
4585  * e.g.
4586  * service == auto_home
4587  * SSD = automount: ou = mytest,
4588  * NS_LDAP_MAPATTRIBUTE= auto_home: automountMapName=AAA
4589  * NS_LDAP_OBJECTCLASSMAP= auto_home:automountMap=MynisMap
4590  * NS_LDAP_OBJECTCLASSMAP= auto_home:automount=MynisObject
4591  *
4592  * The automountMapName is prepended implicitely but is mapped
4593  * to AAA. So dn could appers as
4594  * dn: AAA=auto_home,ou=bar,dc=foo,dc=com
4595  * dn: automountKey=user_01,AAA=auto_home,ou=bar,dc=foo,dc=com
4596  * dn: automountKey=user_02,AAA=auto_home,ou=bar,dc=foo,dc=com
4597  * in the directory.
4598  * This function is called to covert the mapped attr back to
4599  * orig attr when the entries are searched and returned
4600  */
4601 
4602 int
4603 __s_api_convert_automountmapname(const char *service, char **dn,
4604     ns_ldap_error_t **errp)
4605 {
4606 
4607         char    **mapping = NULL;
4608         char    *mapped_attr = NULL;
4609         char    *automountmapname = "automountMapName";
4610         char    *buffer = NULL;
4611         int     rc = NS_LDAP_SUCCESS;
4612         char    errstr[MAXERROR];
4613 
4614         /*
4615          * dn is an input/out parameter, check it first
4616          */
4617 
4618         if (service == NULL || dn == NULL || *dn == NULL)
4619                 return (NS_LDAP_INVALID_PARAM);
4620 
4621         /*
4622          * Check to see if there is a mapped attribute for auto_xxx
4623          */
4624 
4625         mapping = __ns_ldap_getMappedAttributes(service, automountmapname);
4626 
4627         /*
4628          * if no mapped attribute for auto_xxx, try automount
4629          */
4630 
4631         if (mapping == NULL) {
4632                 mapping = __ns_ldap_getMappedAttributes(
4633                     "automount", automountmapname);
4634         }
4635 
4636         /*
4637          * if no mapped attribute is found, return SUCCESS (no op)
4638          */
4639 
4640         if (mapping == NULL)
4641                 return (NS_LDAP_SUCCESS);
4642 
4643         /*
4644          * if the mapped attribute is found and attr is not empty,
4645          * copy it
4646          */
4647 
4648         if (mapping[0] != NULL) {
4649                 mapped_attr = strdup(mapping[0]);
4650                 __s_api_free2dArray(mapping);
4651                 if (mapped_attr == NULL) {
4652                         return (NS_LDAP_MEMORY);
4653                 }
4654         } else {
4655                 __s_api_free2dArray(mapping);
4656 
4657                 (void) snprintf(errstr, (2 * MAXERROR),
4658                     gettext("Attribute nisMapName is mapped to an "
4659                     "empty string.\n"));
4660 
4661                 MKERROR(LOG_ERR, *errp, NS_CONFIG_SYNTAX,
4662                     strdup(errstr), NS_LDAP_MEMORY);
4663 
4664                 return (NS_LDAP_CONFIG);
4665         }
4666 
4667         /*
4668          * Locate the mapped attribute in the dn
4669          * and replace it if it exists
4670          */
4671 
4672         rc = __s_api_replace_mapped_attr_in_dn(
4673             (const char *) automountmapname, (const char *) mapped_attr,
4674             (const char *) *dn, &buffer);
4675 
4676         /* clean up */
4677 
4678         free(mapped_attr);
4679 
4680         /*
4681          * If mapped attr is found(buffer != NULL)
4682          *      a new dn is returned
4683          * If no mapped attribute is in dn,
4684          *      return NS_LDAP_SUCCESS (no op)
4685          * If no memory,
4686          *      return NS_LDAP_MEMORY (no op)
4687          */
4688 
4689         if (buffer != NULL) {
4690                 free(*dn);
4691                 *dn = buffer;
4692         }
4693 
4694         return (rc);
4695 }
4696 
4697 /*
4698  * If the mapped attr is found in the dn,
4699  *      return NS_LDAP_SUCCESS and a new_dn.
4700  * If no mapped attr is found,
4701  *      return NS_LDAP_SUCCESS and *new_dn == NULL
4702  * If there is not enough memory,
4703  *      return NS_LDAP_MEMORY and *new_dn == NULL
4704  */
4705 
4706 int
4707 __s_api_replace_mapped_attr_in_dn(const char *orig_attr,
4708     const char *mapped_attr, const char *dn, char **new_dn)
4709 {
4710 
4711         char    **dnArray = NULL;
4712         char    *cur = NULL, *start = NULL;
4713         int     i = 0;
4714         boolean_t found = B_FALSE;
4715         int     len = 0, orig_len = 0, mapped_len = 0;
4716         int     dn_len = 0, tmp_len = 0;
4717 
4718         *new_dn = NULL;
4719 
4720         /*
4721          * seperate dn into individual componets
4722          * e.g.
4723          * "automountKey=user_01" , "automountMapName_test=auto_home", ...
4724          */
4725         dnArray = ldap_explode_dn(dn, 0);
4726 
4727         /*
4728          * This will find "mapped attr=value" in dn.
4729          * It won't find match if mapped attr appears
4730          * in the value.
4731          */
4732         for (i = 0; dnArray[i] != NULL; i++) {
4733                 /*
4734                  * This function is called when reading from
4735                  * the directory so assume each component has "=".
4736                  * Any ill formatted dn should be rejected
4737                  * before adding to the directory
4738                  */
4739                 cur = strchr(dnArray[i], '=');
4740                 *cur = '\0';
4741                 if (strcasecmp(mapped_attr, dnArray[i]) == 0)
4742                         found = B_TRUE;
4743                 *cur = '=';
4744                 if (found)
4745                         break;
4746         }
4747 
4748         if (!found) {
4749                 __s_api_free2dArray(dnArray);
4750                 *new_dn = NULL;
4751                 return (NS_LDAP_SUCCESS);
4752         }
4753         /*
4754          * The new length is *dn length + (difference between
4755          * orig attr and mapped attr) + 1 ;
4756          * e.g.
4757          * automountKey=aa,automountMapName_test=auto_home,dc=foo,dc=com
4758          * ==>
4759          * automountKey=aa,automountMapName=auto_home,dc=foo,dc=com
4760          */
4761         mapped_len = strlen(mapped_attr);
4762         orig_len = strlen(orig_attr);
4763         dn_len = strlen(dn);
4764         len = dn_len + orig_len - mapped_len + 1;
4765         *new_dn = (char *)calloc(1, len);
4766         if (*new_dn == NULL) {
4767                 __s_api_free2dArray(dnArray);
4768                 return (NS_LDAP_MEMORY);
4769         }
4770 
4771         /*
4772          * Locate the mapped attr in the dn.
4773          * Use dnArray[i] instead of mapped_attr
4774          * because mapped_attr could appear in
4775          * the value
4776          */
4777 
4778         cur = strstr(dn, dnArray[i]);
4779         __s_api_free2dArray(dnArray);
4780         /* copy the portion before mapped attr in dn  */
4781         start = *new_dn;
4782         tmp_len = cur - dn;
4783         (void) memcpy(start, dn, tmp_len);
4784 
4785         /*
4786          * Copy the orig_attr. e.g. automountMapName
4787          * This replaces mapped attr with orig attr
4788          */
4789         start = start + (cur - dn); /* move cursor in buffer */
4790         (void) memcpy(start, orig_attr, orig_len);
4791 
4792         /*
4793          * Copy the portion after mapped attr in dn
4794          */
4795         cur = cur + mapped_len; /* move cursor in  dn  */
4796         start = start + orig_len; /* move cursor in buffer */
4797         (void) strcpy(start, cur);
4798 
4799         return (NS_LDAP_SUCCESS);
4800 }
4801 
4802 /*
4803  * Validate Filter functions
4804  */
4805 
4806 /* ***** Start of modified libldap.so.5 filter parser ***** */
4807 
4808 /* filter parsing routine forward references */
4809 static int adj_filter_list(char *str);
4810 static int adj_simple_filter(char *str);
4811 static int unescape_filterval(char *val);
4812 static int hexchar2int(char c);
4813 static int adj_substring_filter(char *val);
4814 
4815 
4816 /*
4817  * assumes string manipulation is in-line
4818  * and all strings are sufficient in size
4819  * return value is the position after 'c'
4820  */
4821 
4822 static char *
4823 resync_str(char *str, char *next, char c)
4824 {
4825         char    *ret;
4826 
4827         ret = str + strlen(str);
4828         *next = c;
4829         if (ret == next)
4830                 return (ret);
4831         (void) strcat(str, next);
4832         return (ret);
4833 }
4834 
4835 static char *
4836 find_right_paren(char *s)
4837 {
4838         int balance;
4839         boolean_t escape;
4840 
4841         balance = 1;
4842         escape = B_FALSE;
4843         while (*s && balance) {
4844                 if (escape == B_FALSE) {
4845                         if (*s == '(')
4846                                 balance++;
4847                         else if (*s == ')')
4848                                 balance--;
4849                 }
4850                 if (*s == '\\' && !escape)
4851                         escape = B_TRUE;
4852                 else
4853                         escape = B_FALSE;
4854                 if (balance)
4855                         s++;
4856         }
4857 
4858         return (*s ? s : NULL);
4859 }
4860 
4861 static char *
4862 adj_complex_filter(char *str)
4863 {
4864         char    *next;
4865 
4866         /*
4867          * We have (x(filter)...) with str sitting on
4868          * the x.  We have to find the paren matching
4869          * the one before the x and put the intervening
4870          * filters by calling adj_filter_list().
4871          */
4872 
4873         str++;
4874         if ((next = find_right_paren(str)) == NULL)
4875                 return (NULL);
4876 
4877         *next = '\0';
4878         if (adj_filter_list(str) == -1)
4879                 return (NULL);
4880         next = resync_str(str, next, ')');
4881         next++;
4882 
4883         return (next);
4884 }
4885 
4886 static int
4887 adj_filter(char *str)
4888 {
4889         char *next;
4890         int parens, balance;
4891         boolean_t escape;
4892         char *np, *cp,  *dp;
4893 
4894         parens = 0;
4895         while (*str) {
4896                 switch (*str) {
4897                 case '(':
4898                         str++;
4899                         parens++;
4900                         switch (*str) {
4901                         case '&':
4902                                 if ((str = adj_complex_filter(str)) == NULL)
4903                                         return (-1);
4904 
4905                                 parens--;
4906                                 break;
4907 
4908                         case '|':
4909                                 if ((str = adj_complex_filter(str)) == NULL)
4910                                         return (-1);
4911 
4912                                 parens--;
4913                                 break;
4914 
4915                         case '!':
4916                                 if ((str = adj_complex_filter(str)) == NULL)
4917                                         return (-1);
4918 
4919                                 parens--;
4920                                 break;
4921 
4922                         case '(':
4923                                 /* illegal ((case - generated by conversion */
4924 
4925                                 /* find missing close) */
4926                                 np = find_right_paren(str+1);
4927 
4928                                 /* error if not found */
4929                                 if (np == NULL)
4930                                         return (-1);
4931 
4932                                 /* remove redundant (and) */
4933                                 for (dp = str, cp = str+1; cp < np; ) {
4934                                         *dp++ = *cp++;
4935                                 }
4936                                 cp++;
4937                                 while (*cp)
4938                                         *dp++ = *cp++;
4939                                 *dp = '\0';
4940 
4941                                 /* re-start test at original ( */
4942                                 parens--;
4943                                 str--;
4944                                 break;
4945 
4946                         default:
4947                                 balance = 1;
4948                                 escape = B_FALSE;
4949                                 next = str;
4950                                 while (*next && balance) {
4951                                         if (escape == B_FALSE) {
4952                                                 if (*next == '(')
4953                                                         balance++;
4954                                                 else if (*next == ')')
4955                                                         balance--;
4956                                         }
4957                                         if (*next == '\\' && !escape)
4958                                                 escape = B_TRUE;
4959                                         else
4960                                                 escape = B_FALSE;
4961                                         if (balance)
4962                                                 next++;
4963                                 }
4964                                 if (balance != 0)
4965                                         return (-1);
4966 
4967                                 *next = '\0';
4968                                 if (adj_simple_filter(str) == -1) {
4969                                         return (-1);
4970                                 }
4971                                 next = resync_str(str, next, ')');
4972                                 next++;
4973                                 str = next;
4974                                 parens--;
4975                                 break;
4976                         }
4977                         break;
4978 
4979                 case ')':
4980                         str++;
4981                         parens--;
4982                         break;
4983 
4984                 case ' ':
4985                         str++;
4986                         break;
4987 
4988                 default:        /* assume it's a simple type=value filter */
4989                         next = strchr(str, '\0');
4990                         if (adj_simple_filter(str) == -1) {
4991                                 return (-1);
4992                         }
4993                         str = next;
4994                         break;
4995                 }
4996         }
4997 
4998         return (parens ? -1 : 0);
4999 }
5000 
5001 
5002 /*
5003  * Put a list of filters like this "(filter1)(filter2)..."
5004  */
5005 
5006 static int
5007 adj_filter_list(char *str)
5008 {
5009         char    *next;
5010         char    save;
5011 
5012         while (*str) {
5013                 while (*str && isspace(*str))
5014                         str++;
5015                 if (*str == '\0')
5016                         break;
5017 
5018                 if ((next = find_right_paren(str + 1)) == NULL)
5019                         return (-1);
5020                 save = *++next;
5021 
5022                 /* now we have "(filter)" with str pointing to it */
5023                 *next = '\0';
5024                 if (adj_filter(str) == -1)
5025                         return (-1);
5026                 next = resync_str(str, next, save);
5027 
5028                 str = next;
5029         }
5030 
5031         return (0);
5032 }
5033 
5034 
5035 /*
5036  * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
5037  * of a filter expression, 0 otherwise.  A valid string may contain only
5038  * letters, numbers, hyphens, semi-colons, colons and periods. examples:
5039  *      cn
5040  *      cn;lang-fr
5041  *      1.2.3.4;binary;dynamic
5042  *      mail;dynamic
5043  *      cn:dn:1.2.3.4
5044  *
5045  * For compatibility with older servers, we also allow underscores in
5046  * attribute types, even through they are not allowed by the LDAPv3 RFCs.
5047  */
5048 static int
5049 is_valid_attr(char *a)
5050 {
5051         for (; *a; a++) {
5052                 if (!isascii(*a)) {
5053                         return (0);
5054                 } else if (!isalnum(*a)) {
5055                         switch (*a) {
5056                         case '-':
5057                         case '.':
5058                         case ';':
5059                         case ':':
5060                         case '_':
5061                                 break; /* valid */
5062                         default:
5063                                 return (0);
5064                         }
5065                 }
5066         }
5067         return (1);
5068 }
5069 
5070 static char *
5071 find_star(char *s)
5072 {
5073         for (; *s; ++s) {
5074                 switch (*s) {
5075                 case '*':
5076                         return (s);
5077                 case '\\':
5078                         ++s;
5079                         if (hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0)
5080                                 ++s;
5081                 default:
5082                         break;
5083                 }
5084         }
5085         return (NULL);
5086 }
5087 
5088 static int
5089 adj_simple_filter(char *str)
5090 {
5091         char            *s, *s2, *s3, filterop;
5092         char            *value;
5093         int             ftype = 0;
5094         int             rc;
5095 
5096         rc = -1;        /* pessimistic */
5097 
5098         if ((str = strdup(str)) == NULL) {
5099                 return (rc);
5100         }
5101 
5102         if ((s = strchr(str, '=')) == NULL) {
5103                 goto free_and_return;
5104         }
5105         value = s + 1;
5106         *s-- = '\0';
5107         filterop = *s;
5108         if (filterop == '<' || filterop == '>' || filterop == '~' ||
5109             filterop == ':') {
5110                 *s = '\0';
5111         }
5112 
5113         if (!is_valid_attr(str)) {
5114                 goto free_and_return;
5115         }
5116 
5117         switch (filterop) {
5118         case '<': /* LDAP_FILTER_LE */
5119         case '>': /* LDAP_FILTER_GE */
5120         case '~': /* LDAP_FILTER_APPROX */
5121                 break;
5122         case ':':       /* extended filter - v3 only */
5123                 /*
5124                  * extended filter looks like this:
5125                  *
5126                  *      [type][':dn'][':'oid]':='value
5127                  *
5128                  * where one of type or :oid is required.
5129                  *
5130                  */
5131                 s2 = s3 = NULL;
5132                 if ((s2 = strrchr(str, ':')) == NULL) {
5133                         goto free_and_return;
5134                 }
5135                 if (strcasecmp(s2, ":dn") == 0) {
5136                         *s2 = '\0';
5137                 } else {
5138                         *s2 = '\0';
5139                         if ((s3 = strrchr(str, ':')) != NULL) {
5140                                 if (strcasecmp(s3, ":dn") != 0) {
5141                                         goto free_and_return;
5142                                 }
5143                                 *s3 = '\0';
5144                         }
5145                 }
5146                 if (unescape_filterval(value) < 0) {
5147                         goto free_and_return;
5148                 }
5149                 rc = 0;
5150                 goto free_and_return;
5151                 /* break; */
5152         default:
5153                 if (find_star(value) == NULL) {
5154                         ftype = 0; /* LDAP_FILTER_EQUALITY */
5155                 } else if (strcmp(value, "*") == 0) {
5156                         ftype = 1; /* LDAP_FILTER_PRESENT */
5157                 } else {
5158                         rc = adj_substring_filter(value);
5159                         goto free_and_return;
5160                 }
5161                 break;
5162         }
5163 
5164         if (ftype != 0) {       /* == LDAP_FILTER_PRESENT */
5165                 rc = 0;
5166         } else if (unescape_filterval(value) >= 0) {
5167                 rc = 0;
5168         }
5169         if (rc != -1) {
5170                 rc = 0;
5171         }
5172 
5173 free_and_return:
5174         free(str);
5175         return (rc);
5176 }
5177 
5178 
5179 /*
5180  * Check in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
5181  * sequences within the null-terminated string 'val'.
5182  *
5183  * If 'val' contains invalid escape sequences we return -1.
5184  * Otherwise return 1
5185  */
5186 static int
5187 unescape_filterval(char *val)
5188 {
5189         boolean_t escape, firstdigit;
5190         char *s;
5191 
5192         firstdigit = B_FALSE;
5193         escape = B_FALSE;
5194         for (s = val; *s; s++) {
5195                 if (escape) {
5196                         /*
5197                          * first try LDAPv3 escape (hexadecimal) sequence
5198                          */
5199                         if (hexchar2int(*s) < 0) {
5200                                 if (firstdigit) {
5201                                         /*
5202                                          * LDAPv2 (RFC1960) escape sequence
5203                                          */
5204                                         escape = B_FALSE;
5205                                 } else {
5206                                         return (-1);
5207                                 }
5208                         }
5209                         if (firstdigit) {
5210                                 firstdigit = B_FALSE;
5211                         } else {
5212                                 escape = B_FALSE;
5213                         }
5214 
5215                 } else if (*s != '\\') {
5216                         escape = B_FALSE;
5217 
5218                 } else {
5219                         escape = B_TRUE;
5220                         firstdigit = B_TRUE;
5221                 }
5222         }
5223 
5224         return (1);
5225 }
5226 
5227 
5228 /*
5229  * convert character 'c' that represents a hexadecimal digit to an integer.
5230  * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
5231  * otherwise the converted value is returned.
5232  */
5233 static int
5234 hexchar2int(char c)
5235 {
5236         if (c >= '0' && c <= '9') {
5237                 return (c - '0');
5238         }
5239         if (c >= 'A' && c <= 'F') {
5240                 return (c - 'A' + 10);
5241         }
5242         if (c >= 'a' && c <= 'f') {
5243                 return (c - 'a' + 10);
5244         }
5245         return (-1);
5246 }
5247 
5248 static int
5249 adj_substring_filter(char *val)
5250 {
5251         char            *nextstar;
5252 
5253         for (; val != NULL; val = nextstar) {
5254                 if ((nextstar = find_star(val)) != NULL) {
5255                         *nextstar++ = '\0';
5256                 }
5257 
5258                 if (*val != '\0') {
5259                         if (unescape_filterval(val) < 0) {
5260                                 return (-1);
5261                         }
5262                 }
5263         }
5264 
5265         return (0);
5266 }
5267 
5268 /* ***** End of modified libldap.so.5 filter parser ***** */
5269 
5270 
5271 /*
5272  * Walk filter, remove redundant parentheses in-line
5273  * verify that the filter is reasonable
5274  */
5275 static int
5276 validate_filter(ns_ldap_cookie_t *cookie)
5277 {
5278         char                    *filter = cookie->filter;
5279         int                     rc;
5280 
5281         /* Parse filter looking for illegal values */
5282 
5283         rc = adj_filter(filter);
5284         if (rc != 0) {
5285                 return (NS_LDAP_OP_FAILED);
5286         }
5287 
5288         /* end of filter checking */
5289 
5290         return (NS_LDAP_SUCCESS);
5291 }
5292 
5293 /*
5294  * Set the account management request control that needs to be sent to server.
5295  * This control is required to get the account management information of
5296  * a user to do local account checking.
5297  */
5298 static int
5299 setup_acctmgmt_params(ns_ldap_cookie_t *cookie)
5300 {
5301         LDAPControl     *req, **requestctrls;
5302 
5303         req = calloc(1, sizeof (LDAPControl));
5304 
5305         if (req == NULL)
5306                 return (NS_LDAP_MEMORY);
5307 
5308         /* fill in the fields of this new control */
5309         req->ldctl_iscritical = 1;
5310         req->ldctl_oid = strdup(NS_LDAP_ACCOUNT_USABLE_CONTROL);
5311         if (req->ldctl_oid == NULL) {
5312                 free(req);
5313                 return (NS_LDAP_MEMORY);
5314         }
5315 
5316         requestctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
5317         if (requestctrls == NULL) {
5318                 ldap_control_free(req);
5319                 return (NS_LDAP_MEMORY);
5320         }
5321 
5322         requestctrls[0] = req;
5323 
5324         cookie->p_serverctrls = requestctrls;
5325 
5326         return (NS_LDAP_SUCCESS);
5327 }
5328 
5329 /*
5330  * int get_new_acct_more_info(BerElement *ber,
5331  *     AcctUsableResponse_t *acctResp)
5332  *
5333  * Decode the more_info data from an Account Management control response,
5334  * when the account is not usable and when code style is from recent LDAP
5335  * servers (see below comments for parse_acct_cont_resp_msg() to get more
5336  * details on coding styles and ASN1 description).
5337  *
5338  * Expected BER encoding: {tbtbtbtiti}
5339  *      +t: tag is 0
5340  *      +b: TRUE if inactive due to account inactivation
5341  *      +t: tag is 1
5342  *      +b: TRUE if password has been reset
5343  *      +t: tag is 2
5344  *      +b: TRUE if password is expired
5345  *      +t: tag is 3
5346  *      +i: contains num of remaining grace, 0 means no grace
5347  *      +t: tag is 4
5348  *      +i: contains num of seconds before auto-unlock. -1 means acct is locked
5349  *              forever (i.e. until reset)
5350  *
5351  * Asumptions:
5352  * - ber is not null
5353  * - acctResp is not null and is initialized with default values for the
5354  *   fields in its AcctUsableResp.more_info structure
5355  * - the ber stream is received in the correct order, per the ASN1 description.
5356  *   We do not check this order and make the asumption that it is correct.
5357  *   Note that the ber stream may not (and will not in most cases) contain
5358  *   all fields.
5359  */
5360 static int
5361 get_new_acct_more_info(BerElement *ber, AcctUsableResponse_t *acctResp)
5362 {
5363         int             rc = NS_LDAP_SUCCESS;
5364         char            errstr[MAXERROR];
5365         ber_tag_t       rTag = LBER_DEFAULT;
5366         ber_len_t       rLen = 0;
5367         ber_int_t       rValue;
5368         char            *last;
5369         int             berRC = 0;
5370 
5371         /*
5372          * Look at what more_info BER element is/are left to be decoded.
5373          * look at each of them 1 by 1, without checking on their order
5374          * and possible multi values.
5375          */
5376         for (rTag = ber_first_element(ber, &rLen, &last);
5377             rTag != LBER_END_OF_SEQORSET;
5378             rTag = ber_next_element(ber, &rLen, last)) {
5379 
5380                 berRC = 0;
5381                 switch (rTag) {
5382                 case 0 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5383                         /* inactive */
5384                         berRC = ber_scanf(ber, "b", &rValue);
5385                         if (berRC != LBER_ERROR) {
5386                                 (acctResp->AcctUsableResp).more_info.
5387                                     inactive = (rValue != 0) ? 1 : 0;
5388                         }
5389                         break;
5390 
5391                 case 1 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5392                         /* reset */
5393                         berRC = ber_scanf(ber, "b", &rValue);
5394                         if (berRC != LBER_ERROR) {
5395                                 (acctResp->AcctUsableResp).more_info.reset
5396                                     = (rValue != 0) ? 1 : 0;
5397                         }
5398                         break;
5399 
5400                 case 2 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5401                         /* expired */
5402                         berRC = ber_scanf(ber, "b", &rValue);
5403                         if (berRC != LBER_ERROR) {
5404                                 (acctResp->AcctUsableResp).more_info.expired
5405                                     = (rValue != 0) ? 1 : 0;
5406                         }
5407                         break;
5408 
5409                 case 3 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5410                         /* remaining grace */
5411                         berRC = ber_scanf(ber, "i", &rValue);
5412                         if (berRC != LBER_ERROR) {
5413                                 (acctResp->AcctUsableResp).more_info.rem_grace
5414                                     = rValue;
5415                         }
5416                         break;
5417 
5418                 case 4 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5419                         /* seconds before unlock */
5420                         berRC = ber_scanf(ber, "i", &rValue);
5421                         if (berRC != LBER_ERROR) {
5422                                 (acctResp->AcctUsableResp).more_info.
5423                                     sec_b4_unlock = rValue;
5424                         }
5425                         break;
5426 
5427                 default :
5428                         (void) sprintf(errstr,
5429                             gettext("invalid reason tag 0x%x"), rTag);
5430                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5431                         rc = NS_LDAP_INTERNAL;
5432                         break;
5433                 }
5434                 if (berRC == LBER_ERROR) {
5435                         (void) sprintf(errstr,
5436                             gettext("error 0x%x decoding value for "
5437                             "tag 0x%x"), berRC, rTag);
5438                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5439                         rc = NS_LDAP_INTERNAL;
5440                 }
5441                 if (rc != NS_LDAP_SUCCESS) {
5442                         /* exit the for loop */
5443                         break;
5444                 }
5445         }
5446 
5447         return (rc);
5448 }
5449 
5450 /*
5451  * int get_old_acct_opt_more_info(BerElement *ber,
5452  *     AcctUsableResponse_t *acctResp)
5453  *
5454  * Decode the optional more_info data from an Account Management control
5455  * response, when the account is not usable and when code style is from LDAP
5456  * server 5.2p4 (see below comments for parse_acct_cont_resp_msg() to get more
5457  * details on coding styles and ASN1 description).
5458  *
5459  * Expected BER encoding: titi}
5460  *      +t: tag is 2
5461  *      +i: contains num of remaining grace, 0 means no grace
5462  *      +t: tag is 3
5463  *      +i: contains num of seconds before auto-unlock. -1 means acct is locked
5464  *              forever (i.e. until reset)
5465  *
5466  * Asumptions:
5467  * - ber is a valid BER element
5468  * - acctResp is initialized for the fields in its AcctUsableResp.more_info
5469  *   structure
5470  */
5471 static int
5472 get_old_acct_opt_more_info(ber_tag_t tag, BerElement *ber,
5473     AcctUsableResponse_t *acctResp)
5474 {
5475         int             rc = NS_LDAP_SUCCESS;
5476         char            errstr[MAXERROR];
5477         ber_len_t       len;
5478         int             rem_grace, sec_b4_unlock;
5479 
5480         switch (tag) {
5481         case 2:
5482                 /* decode and maybe 3 is following */
5483                 if ((tag = ber_scanf(ber, "i", &rem_grace)) == LBER_ERROR) {
5484                         (void) sprintf(errstr, gettext("Can not get "
5485                             "rem_grace"));
5486                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5487                         rc = NS_LDAP_INTERNAL;
5488                         break;
5489                 }
5490                 (acctResp->AcctUsableResp).more_info.rem_grace = rem_grace;
5491 
5492                 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5493                         /* this is a success case, break to exit */
5494                         (void) sprintf(errstr, gettext("No more "
5495                             "optional data"));
5496                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5497                         break;
5498                 }
5499 
5500                 if (tag == 3) {
5501                         if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5502                                 (void) sprintf(errstr,
5503                                     gettext("Can not get sec_b4_unlock "
5504                                     "- 1st case"));
5505                                 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5506                                 rc = NS_LDAP_INTERNAL;
5507                                 break;
5508                         }
5509                         (acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5510                             sec_b4_unlock;
5511                 } else { /* unknown tag */
5512                         (void) sprintf(errstr, gettext("Unknown tag "
5513                             "- 1st case"));
5514                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5515                         rc = NS_LDAP_INTERNAL;
5516                         break;
5517                 }
5518                 break;
5519 
5520         case 3:
5521                 if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5522                         (void) sprintf(errstr, gettext("Can not get "
5523                             "sec_b4_unlock - 2nd case"));
5524                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5525                         rc = NS_LDAP_INTERNAL;
5526                         break;
5527                 }
5528                 (acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5529                     sec_b4_unlock;
5530                 break;
5531 
5532         default: /* unknown tag */
5533                 (void) sprintf(errstr, gettext("Unknown tag - 2nd case"));
5534                 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5535                 rc = NS_LDAP_INTERNAL;
5536                 break;
5537         }
5538 
5539         return (rc);
5540 }
5541 
5542 /*
5543  * **** This function needs to be moved to libldap library ****
5544  * parse_acct_cont_resp_msg() parses the message received by server according to
5545  * following format (ASN1 notation):
5546  *
5547  *      ACCOUNT_USABLE_RESPONSE::= CHOICE {
5548  *              is_available            [0] INTEGER,
5549  *                              ** seconds before expiration **
5550  *              is_not_available        [1] more_info
5551  *      }
5552  *      more_info::= SEQUENCE {
5553  *              inactive                [0] BOOLEAN DEFAULT FALSE,
5554  *              reset                   [1] BOOLEAN DEFAULT FALSE,
5555  *              expired                 [2] BOOLEAN DEFAULT FALSE,
5556  *              remaining_grace         [3] INTEGER OPTIONAL,
5557  *              seconds_before_unlock   [4] INTEGER OPTIONAL
5558  *      }
5559  */
5560 /*
5561  * #define used to make the difference between coding style as done
5562  * by LDAP server 5.2p4 and newer LDAP servers. There are 4 values:
5563  * - DS52p4_USABLE: 5.2p4 coding style, account is usable
5564  * - DS52p4_NOT_USABLE: 5.2p4 coding style, account is not usable
5565  * - NEW_USABLE: newer LDAP servers coding style, account is usable
5566  * - NEW_NOT_USABLE: newer LDAP servers coding style, account is not usable
5567  *
5568  * An account would be considered not usable if for instance:
5569  * - it's been made inactive in the LDAP server
5570  * - or its password was reset in the LDAP server database
5571  * - or its password expired
5572  * - or the account has been locked, possibly forever
5573  */
5574 #define DS52p4_USABLE           0x00
5575 #define DS52p4_NOT_USABLE       0x01
5576 #define NEW_USABLE              0x00 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE
5577 #define NEW_NOT_USABLE          0x01 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED
5578 static int
5579 parse_acct_cont_resp_msg(LDAPControl **ectrls, AcctUsableResponse_t *acctResp)
5580 {
5581         int             rc = NS_LDAP_SUCCESS;
5582         BerElement      *ber;
5583         ber_tag_t       tag;
5584         ber_len_t       len;
5585         int             i;
5586         char            errstr[MAXERROR];
5587         /* used for any coding style when account is usable */
5588         int             seconds_before_expiry;
5589         /* used for 5.2p4 coding style when account is not usable */
5590         int             inactive, reset, expired;
5591 
5592         if (ectrls == NULL) {
5593                 (void) sprintf(errstr, gettext("Invalid ectrls parameter"));
5594                 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5595                 return (NS_LDAP_INVALID_PARAM);
5596         }
5597 
5598         for (i = 0; ectrls[i] != NULL; i++) {
5599                 if (strcmp(ectrls[i]->ldctl_oid, NS_LDAP_ACCOUNT_USABLE_CONTROL)
5600                     == 0) {
5601                         break;
5602                 }
5603         }
5604 
5605         if (ectrls[i] == NULL) {
5606                 /* Ldap control is not found */
5607                 (void) sprintf(errstr, gettext("Account Usable Control "
5608                     "not found"));
5609                 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5610                 return (NS_LDAP_NOTFOUND);
5611         }
5612 
5613         /* Allocate a BER element from the control value and parse it. */
5614         if ((ber = ber_init(&ectrls[i]->ldctl_value)) == NULL)
5615                 return (NS_LDAP_MEMORY);
5616 
5617         if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5618                 /* Ldap decoding error */
5619                 (void) sprintf(errstr, gettext("Error decoding 1st tag"));
5620                 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5621                 ber_free(ber, 1);
5622                 return (NS_LDAP_INTERNAL);
5623         }
5624 
5625         switch (tag) {
5626         case DS52p4_USABLE:
5627         case NEW_USABLE:
5628                 acctResp->choice = 0;
5629                 if (ber_scanf(ber, "i", &seconds_before_expiry)
5630                     == LBER_ERROR) {
5631                         /* Ldap decoding error */
5632                         (void) sprintf(errstr, gettext("Can not get "
5633                             "seconds_before_expiry"));
5634                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5635                         rc = NS_LDAP_INTERNAL;
5636                         break;
5637                 }
5638                 /* ber_scanf() succeeded */
5639                 (acctResp->AcctUsableResp).seconds_before_expiry =
5640                     seconds_before_expiry;
5641                 break;
5642 
5643         case DS52p4_NOT_USABLE:
5644                 acctResp->choice = 1;
5645                 if (ber_scanf(ber, "{bbb", &inactive, &reset, &expired)
5646                     == LBER_ERROR) {
5647                         /* Ldap decoding error */
5648                         (void) sprintf(errstr, gettext("Can not get "
5649                             "inactive/reset/expired"));
5650                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5651                         rc = NS_LDAP_INTERNAL;
5652                         break;
5653                 }
5654                 /* ber_scanf() succeeded */
5655                 (acctResp->AcctUsableResp).more_info.inactive =
5656                     ((inactive == 0) ? 0 : 1);
5657                 (acctResp->AcctUsableResp).more_info.reset =
5658                     ((reset == 0) ? 0 : 1);
5659                 (acctResp->AcctUsableResp).more_info.expired =
5660                     ((expired == 0) ? 0 : 1);
5661                 (acctResp->AcctUsableResp).more_info.rem_grace = 0;
5662                 (acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5663 
5664                 if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5665                         /* this is a success case, break to exit */
5666                         (void) sprintf(errstr, gettext("No optional data"));
5667                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5668                         break;
5669                 }
5670 
5671                 /*
5672                  * Look at what optional more_info BER element is/are
5673                  * left to be decoded.
5674                  */
5675                 rc = get_old_acct_opt_more_info(tag, ber, acctResp);
5676                 break;
5677 
5678         case NEW_NOT_USABLE:
5679                 acctResp->choice = 1;
5680                 /*
5681                  * Recent LDAP servers won't code more_info data for default
5682                  * values (see above comments on ASN1 description for what
5683                  * fields have default values & what fields are optional).
5684                  */
5685                 (acctResp->AcctUsableResp).more_info.inactive = 0;
5686                 (acctResp->AcctUsableResp).more_info.reset = 0;
5687                 (acctResp->AcctUsableResp).more_info.expired = 0;
5688                 (acctResp->AcctUsableResp).more_info.rem_grace = 0;
5689                 (acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5690 
5691                 if (len == 0) {
5692                         /*
5693                          * Nothing else to decode; this is valid and we
5694                          * use default values set above.
5695                          */
5696                         (void) sprintf(errstr, gettext("more_info is "
5697                             "empty, using default values"));
5698                         syslog(LOG_DEBUG, "libsldap: %s", errstr);
5699                         break;
5700                 }
5701 
5702                 /*
5703                  * Look at what more_info BER element is/are left to
5704                  * be decoded.
5705                  */
5706                 rc = get_new_acct_more_info(ber, acctResp);
5707                 break;
5708 
5709         default:
5710                 (void) sprintf(errstr, gettext("unknwon coding style "
5711                     "(tag: 0x%x)"), tag);
5712                 syslog(LOG_DEBUG, "libsldap: %s", errstr);
5713                 rc = NS_LDAP_INTERNAL;
5714                 break;
5715         }
5716 
5717         ber_free(ber, 1);
5718         return (rc);
5719 }
5720 
5721 /*
5722  * internal function for __ns_ldap_getAcctMgmt()
5723  */
5724 static int
5725 getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp,
5726     ns_conn_user_t *conn_user)
5727 {
5728         int             scope, rc;
5729         ns_ldap_cookie_t        *cookie;
5730         ns_ldap_search_desc_t   **sdlist = NULL;
5731         ns_ldap_search_desc_t   *dptr;
5732         ns_ldap_error_t         *error = NULL;
5733         char                    **dns = NULL;
5734         char            service[] = "shadow";
5735 
5736         if (user == NULL || acctResp == NULL)
5737                 return (NS_LDAP_INVALID_PARAM);
5738 
5739         /* Initialize State machine cookie */
5740         cookie = init_search_state_machine();
5741         if (cookie == NULL)
5742                 return (NS_LDAP_MEMORY);
5743         cookie->conn_user = conn_user;
5744 
5745         /* see if need to follow referrals */
5746         rc = __s_api_toFollowReferrals(0,
5747             &cookie->followRef, &error);
5748         if (rc != NS_LDAP_SUCCESS) {
5749                 (void) __ns_ldap_freeError(&error);
5750                 goto out;
5751         }
5752 
5753         /* get the service descriptor - or create a default one */
5754         rc = __s_api_get_SSD_from_SSDtoUse_service(service,
5755             &sdlist, &error);
5756         if (rc != NS_LDAP_SUCCESS) {
5757                 (void) __ns_ldap_freeError(&error);
5758                 goto out;
5759         }
5760 
5761         if (sdlist == NULL) {
5762                 /* Create default service Desc */
5763                 sdlist = (ns_ldap_search_desc_t **)calloc(2,
5764                     sizeof (ns_ldap_search_desc_t *));
5765                 if (sdlist == NULL) {
5766                         rc = NS_LDAP_MEMORY;
5767                         goto out;
5768                 }
5769                 dptr = (ns_ldap_search_desc_t *)
5770                     calloc(1, sizeof (ns_ldap_search_desc_t));
5771                 if (dptr == NULL) {
5772                         free(sdlist);
5773                         rc = NS_LDAP_MEMORY;
5774                         goto out;
5775                 }
5776                 sdlist[0] = dptr;
5777 
5778                 /* default base */
5779                 rc = __s_api_getDNs(&dns, service, &cookie->errorp);
5780                 if (rc != NS_LDAP_SUCCESS) {
5781                         if (dns) {
5782                                 __s_api_free2dArray(dns);
5783                                 dns = NULL;
5784                         }
5785                         (void) __ns_ldap_freeError(&(cookie->errorp));
5786                         cookie->errorp = NULL;
5787                         goto out;
5788                 }
5789                 dptr->basedn = strdup(dns[0]);
5790                 if (dptr->basedn == NULL) {
5791                         free(sdlist);
5792                         free(dptr);
5793                         if (dns) {
5794                                 __s_api_free2dArray(dns);
5795                                 dns = NULL;
5796                         }
5797                         rc = NS_LDAP_MEMORY;
5798                         goto out;
5799                 }
5800                 __s_api_free2dArray(dns);
5801                 dns = NULL;
5802 
5803                 /* default scope */
5804                 scope = 0;
5805                 rc = __s_api_getSearchScope(&scope, &cookie->errorp);
5806                 dptr->scope = scope;
5807         }
5808 
5809         cookie->sdlist = sdlist;
5810 
5811         cookie->service = strdup(service);
5812         if (cookie->service == NULL) {
5813                 rc = NS_LDAP_MEMORY;
5814                 goto out;
5815         }
5816 
5817         /* search for entries for this particular uid */
5818         (void) asprintf(&cookie->i_filter, "(uid=%s)", user);
5819         if (cookie->i_filter == NULL) {
5820                 rc = NS_LDAP_MEMORY;
5821                 goto out;
5822         }
5823 
5824         /* create the control request */
5825         if ((rc = setup_acctmgmt_params(cookie)) != NS_LDAP_SUCCESS)
5826                 goto out;
5827 
5828         /* Process search */
5829         rc = search_state_machine(cookie, GET_ACCT_MGMT_INFO, 0);
5830 
5831         /* Copy results back to user */
5832         rc = cookie->err_rc;
5833         if (rc != NS_LDAP_SUCCESS)
5834                         (void) __ns_ldap_freeError(&(cookie->errorp));
5835 
5836         if (cookie->result == NULL)
5837                         goto out;
5838 
5839         if ((rc = parse_acct_cont_resp_msg(cookie->resultctrl, acctResp))
5840             != NS_LDAP_SUCCESS)
5841                 goto out;
5842 
5843         rc = NS_LDAP_SUCCESS;
5844 
5845 out:
5846         delete_search_cookie(cookie);
5847 
5848         return (rc);
5849 }
5850 
5851 /*
5852  * __ns_ldap_getAcctMgmt() is called from pam account management stack
5853  * for retrieving accounting information of users with no user password -
5854  * eg. rlogin, rsh, etc. This function uses the account management control
5855  * request to do a search on the server for the user in question. The
5856  * response control returned from the server is got from the cookie.
5857  * Input params: username of whose account mgmt information is to be got
5858  *               pointer to hold the parsed account management information
5859  * Return values: NS_LDAP_SUCCESS on success or appropriate error
5860  *              code on failure
5861  */
5862 int
5863 __ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
5864 {
5865         ns_conn_user_t  *cu = NULL;
5866         int             try_cnt = 0;
5867         int             rc = NS_LDAP_SUCCESS;
5868         ns_ldap_error_t *error = NULL;
5869 
5870         for (;;) {
5871                 if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
5872                     &try_cnt, &rc, &error) == 0)
5873                         break;
5874                 rc = getAcctMgmt(user, acctResp, cu);
5875         }
5876         return (rc);
5877 }