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