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