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