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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <synch.h>
  27 #include <strings.h>
  28 #include <sys/time.h>
  29 #include <ctype.h>
  30 
  31 #include "ldap_op.h"
  32 #include "ldap_util.h"
  33 #include "ldap_structs.h"
  34 #include "ldap_ruleval.h"
  35 #include "ldap_attr.h"
  36 #include "ldap_print.h"
  37 #include "ldap_glob.h"
  38 
  39 #include "nis_parse_ldap_conf.h"
  40 
  41 #ifndef LDAPS_PORT
  42 #define LDAPS_PORT      636
  43 #endif
  44 
  45 static int setupConList(char *serverList, char *who,
  46                         char *cred, auth_method_t method);
  47 
  48 
  49 /*
  50  * Build one of our internal LDAP search structures, containing copies of
  51  * the supplied input. return NULL in case of error.
  52  *
  53  * If 'filter' is NULL, build an AND-filter using the filter components.
  54  */
  55 __nis_ldap_search_t *
  56 buildLdapSearch(char *base, int scope, int numFilterComps, char **filterComp,
  57                 char *filter, char **attrs, int attrsonly, int isDN) {
  58         __nis_ldap_search_t     *ls;
  59         char                    **a;
  60         int                     i, na, err = 0;
  61         char                    *myself = "buildLdapSearch";
  62 
  63         ls = am(myself, sizeof (*ls));
  64         if (ls == 0)
  65                 return (0);
  66 
  67         ls->base = sdup(myself, T, base);
  68         if (ls->base == 0 && base != 0)
  69                 err++;
  70         ls->scope = scope;
  71 
  72         if (filterComp != 0 && numFilterComps > 0) {
  73                 ls->filterComp = am(myself, numFilterComps *
  74                                         sizeof (ls->filterComp[0]));
  75                 if (ls->filterComp == 0) {
  76                         err++;
  77                         numFilterComps = 0;
  78                 }
  79                 for (i = 0; i < numFilterComps; i++) {
  80                         ls->filterComp[i] = sdup(myself, T, filterComp[i]);
  81                         if (ls->filterComp[i] == 0 && filterComp[i] != 0)
  82                                 err++;
  83                 }
  84                 ls->numFilterComps = numFilterComps;
  85                 if (filter == 0) {
  86                         ls->filter = concatenateFilterComps(ls->numFilterComps,
  87                                         ls->filterComp);
  88                         if (ls->filter == 0)
  89                                 err++;
  90                 }
  91         } else {
  92                 ls->filterComp = 0;
  93                 ls->numFilterComps = 0;
  94                 ls->filter = sdup(myself, T, filter);
  95                 if (ls->filter == 0 && filter != 0)
  96                         err++;
  97         }
  98 
  99         if (attrs != 0) {
 100                 for (na = 0, a = attrs; *a != 0; a++, na++);
 101                 ls->attrs = am(myself, (na + 1) * sizeof (ls->attrs[0]));
 102                 if (ls->attrs != 0) {
 103                         for (i = 0; i < na; i++) {
 104                                 ls->attrs[i] = sdup(myself, T, attrs[i]);
 105                                 if (ls->attrs[i] == 0 && attrs[i] != 0)
 106                                         err++;
 107                         }
 108                         ls->attrs[na] = 0;
 109                         ls->numAttrs = na;
 110                 } else {
 111                         err++;
 112                 }
 113         } else {
 114                 ls->attrs = 0;
 115                 ls->numAttrs = 0;
 116         }
 117 
 118         ls->attrsonly = attrsonly;
 119         ls->isDN = isDN;
 120 
 121         if (err > 0) {
 122                 freeLdapSearch(ls);
 123                 ls = 0;
 124         }
 125 
 126         return (ls);
 127 }
 128 
 129 void
 130 freeLdapSearch(__nis_ldap_search_t *ls) {
 131         int     i;
 132 
 133         if (ls == 0)
 134                 return;
 135 
 136         sfree(ls->base);
 137         if (ls->filterComp != 0) {
 138                 for (i = 0; i < ls->numFilterComps; i++) {
 139                         sfree(ls->filterComp[i]);
 140                 }
 141                 sfree(ls->filterComp);
 142         }
 143         sfree(ls->filter);
 144         if (ls->attrs != 0) {
 145                 for (i = 0; i < ls->numAttrs; i++) {
 146                         sfree(ls->attrs[i]);
 147                 }
 148                 sfree(ls->attrs);
 149         }
 150 
 151         free(ls);
 152 }
 153 
 154 /*
 155  * Given a table mapping, and a rule/value pointer,
 156  * return an LDAP search structure with values suitable for use
 157  * by ldap_search() or (if dn != 0) ldap_modify(). The rule/value
 158  * may be modified.
 159  *
 160  * If dn != 0 and *dn == 0, the function attemps to return a pointer
 161  * to the DN. This may necessitate an ldapSearch, if the rule set doesn't
 162  * produce a DN directly.
 163  *
 164  * if dn == 0, and the rule set produces a DN as well as other attribute/
 165  * value pairs, the function returns an LDAP search structure with the
 166  * DN only.
 167  *
 168  * If 'fromLDAP' is set, the caller wants base/scope/filter from
 169  * t->objectDN->read; otherwise, from t->objectDN->write.
 170  *
 171  * If 'rv' is NULL, the caller wants an enumeration of the container.
 172  *
 173  * Note that this function only creates a search structure for 't' itself;
 174  * if there are alternative mappings for the table, those must be handled
 175  * by our caller.
 176  */
 177 __nis_ldap_search_t *
 178 createLdapRequest(__nis_table_mapping_t *t,
 179                 __nis_rule_value_t *rv, char **dn, int fromLDAP,
 180                 int *res, __nis_object_dn_t *obj_dn) {
 181         int                     i, j;
 182         __nis_ldap_search_t     *ls = 0;
 183         char                    **locDN;
 184         int                     numLocDN, stat = 0, count = 0;
 185         char                    *myself = "createLdapRequest";
 186         __nis_object_dn_t       *objectDN = NULL;
 187 
 188         if (t == 0)
 189                 return (0);
 190 
 191         if (obj_dn == NULL)
 192                 objectDN = t->objectDN;
 193         else
 194                 objectDN = obj_dn;
 195 
 196         if (rv == 0) {
 197                 char    *base;
 198                 char    *filter;
 199 
 200                 if (fromLDAP) {
 201                         base = objectDN->read.base;
 202                         filter = makeFilter(objectDN->read.attrs);
 203                 } else {
 204                         base = objectDN->write.base;
 205                         filter = makeFilter(objectDN->write.attrs);
 206                 }
 207 
 208                 /* Create request to enumerate container */
 209                 ls = buildLdapSearch(base, objectDN->read.scope, 0, 0, filter,
 210                                         0, 0, 0);
 211                 sfree(filter);
 212                 return (ls);
 213         }
 214 
 215         for (i = 0; i < t->numRulesToLDAP; i++) {
 216                 rv = addLdapRuleValue(t, t->ruleToLDAP[i],
 217                                 mit_ldap, mit_nisplus, rv, !fromLDAP, &stat);
 218                 if (rv == 0)
 219                         return (0);
 220                 if (stat == NP_LDAP_RULES_NO_VALUE)
 221                         count++;
 222                 stat = 0;
 223         }
 224 
 225         /*
 226          * If none of the rules produced a value despite
 227          * having enough NIS+ columns, return error.
 228          */
 229         if (rv->numAttrs == 0 && count > 0) {
 230                 *res = NP_LDAP_RULES_NO_VALUE;
 231                 return (0);
 232         }
 233 
 234         /*
 235          * 'rv' now contains everything we know about the attributes and
 236          * values. Build an LDAP search structure from it.
 237          */
 238 
 239         /* Look for a single-valued DN */
 240         locDN = findDNs(myself, rv, 1,
 241                         fromLDAP ? objectDN->read.base :
 242                                         objectDN->write.base,
 243                         &numLocDN);
 244         if (locDN != 0 && numLocDN == 1) {
 245                 if (dn != 0 && *dn == 0) {
 246                         *dn = locDN[0];
 247                         sfree(locDN);
 248                 } else {
 249                         char    *filter;
 250 
 251                         if (fromLDAP)
 252                                 filter = makeFilter(objectDN->read.attrs);
 253                         else
 254                                 filter = makeFilter(objectDN->write.attrs);
 255                         ls = buildLdapSearch(locDN[0], LDAP_SCOPE_BASE, 0, 0,
 256                                                 filter, 0, 0, 1);
 257                         sfree(filter);
 258                         freeDNs(locDN, numLocDN);
 259                 }
 260         } else {
 261                 freeDNs(locDN, numLocDN);
 262         }
 263 
 264         if (ls != 0) {
 265                 ls->useCon = 1;
 266                 return (ls);
 267         }
 268 
 269         /*
 270          * No DN, or caller wanted a search structure with the non-DN
 271          * attributes.
 272          */
 273 
 274         /* Initialize search structure */
 275         {
 276                 char    *filter = (fromLDAP) ?
 277                                 makeFilter(objectDN->read.attrs) :
 278                                 makeFilter(objectDN->write.attrs);
 279                 char    **ofc;
 280                 int     nofc = 0;
 281 
 282                 ofc = makeFilterComp(filter, &nofc);
 283 
 284                 if (filter != 0 && ofc == 0) {
 285                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
 286                         "%s: Unable to break filter into components: \"%s\"",
 287                                 myself, NIL(filter));
 288                         sfree(filter);
 289                         return (0);
 290                 }
 291 
 292                 if (fromLDAP)
 293                         ls = buildLdapSearch(objectDN->read.base,
 294                                 objectDN->read.scope,
 295                                 nofc, ofc, 0, 0, 0, 0);
 296                 else
 297                         ls = buildLdapSearch(objectDN->write.base,
 298                                 objectDN->write.scope,
 299                                 nofc, ofc, 0, 0, 0, 0);
 300                 sfree(filter);
 301                 freeFilterComp(ofc, nofc);
 302                 if (ls == 0)
 303                         return (0);
 304         }
 305 
 306         /* Build and add the filter components */
 307         for (i = 0; i < rv->numAttrs; i++) {
 308                 /* Skip DN */
 309                 if (strcasecmp("dn", rv->attrName[i]) == 0)
 310                         continue;
 311 
 312                 /* Skip vt_ber values */
 313                 if (rv->attrVal[i].type == vt_ber)
 314                         continue;
 315 
 316                 for (j = 0; j < rv->attrVal[i].numVals; j++) {
 317                         __nis_buffer_t  b = {0, 0};
 318                         char            **tmpComp;
 319 
 320                         bp2buf(myself, &b, "%s=%s",
 321                                 rv->attrName[i], rv->attrVal[i].val[j].value);
 322                         tmpComp = addFilterComp(b.buf, ls->filterComp,
 323                                                 &ls->numFilterComps);
 324                         if (tmpComp == 0) {
 325                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 326                                 "%s: Unable to add filter component \"%s\"",
 327                                         myself, NIL(b.buf));
 328                                 sfree(b.buf);
 329                                 freeLdapSearch(ls);
 330                                 return (0);
 331                         }
 332                         ls->filterComp = tmpComp;
 333                         sfree(b.buf);
 334                 }
 335         }
 336 
 337         if (ls->numFilterComps > 0) {
 338                 sfree(ls->filter);
 339                 ls->filter = concatenateFilterComps(ls->numFilterComps,
 340                                                         ls->filterComp);
 341                 if (ls->filter == 0) {
 342                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
 343                         "%s: Unable to concatenate filter components",
 344                                 myself);
 345                         freeLdapSearch(ls);
 346                         return (0);
 347                 }
 348         }
 349 
 350         if (dn != 0 && *dn == 0) {
 351                 /*
 352                  * The caller wants a DN, but we didn't get one from the
 353                  * the rule set. We have an 'ls', so use it to ldapSearch()
 354                  * for an entry from which we can extract the DN.
 355                  */
 356                 __nis_rule_value_t      *rvtmp;
 357                 char                    **locDN;
 358                 int                     nv = 0, numLocDN;
 359 
 360                 rvtmp = ldapSearch(ls, &nv, 0, 0);
 361                 locDN = findDNs(myself, rvtmp, nv, 0, &numLocDN);
 362                 if (locDN != 0 && numLocDN == 1) {
 363                         *dn = locDN[0];
 364                         sfree(locDN);
 365                 } else {
 366                         freeDNs(locDN, numLocDN);
 367                 }
 368                 freeRuleValue(rvtmp, nv);
 369         }
 370 
 371         ls->useCon = 1;
 372         return (ls);
 373 }
 374 
 375 int     ldapConnAttemptRetryTimeout = 60;       /* seconds */
 376 
 377 typedef struct {
 378         LDAP            *ld;
 379         mutex_t         mutex;          /* Mutex for update of structure */
 380         pthread_t       owner;          /* Thread holding mutex */
 381         mutex_t         rcMutex;        /* Mutex for refCount */
 382         int             refCount;       /* Reference count */
 383         int             isBound;        /* Is connection open and usable ? */
 384         time_t          retryTime;      /* When should open be retried */
 385         int             status;         /* Status of last operation */
 386         int             doDis;          /* To be disconnected if refCount==0 */
 387         int             doDel;          /* To be deleted if refCount zero */
 388         int             onList;         /* True if on the 'ldapCon' list */
 389         char            *sp;            /* server string */
 390         char            *who;
 391         char            *cred;
 392         auth_method_t   method;
 393         int             port;
 394         struct timeval  bindTimeout;
 395         struct timeval  searchTimeout;
 396         struct timeval  modifyTimeout;
 397         struct timeval  addTimeout;
 398         struct timeval  deleteTimeout;
 399         int             simplePage;     /* Can do simple-page */
 400         int             vlv;            /* Can do VLV */
 401         uint_t          batchFrom;      /* # entries read in one operation */
 402         void            *next;
 403 } __nis_ldap_conn_t;
 404 
 405 /*
 406  * List of connections, 'ldapCon', protected by an RW lock.
 407  *
 408  * The following locking scheme is used:
 409  *
 410  * (1)  Find a connection structure to use to talk to LDAP
 411  *              Rlock list
 412  *                      Locate structure
 413  *                      Acquire 'mutex'
 414  *                              Acquire 'rcMutex'
 415  *                                      update refCount
 416  *                              Release 'rcMutex'
 417  *                      release 'mutex'
 418  *              Unlock list
 419  *              Use structure
 420  *              Release structure when done
 421  * (2)  Insert/delete structure(s) on/from list
 422  *              Wlock list
 423  *                      Insert/delete structure; if deleting, must
 424  *                      acquire 'mutex', and 'rcMutex' (in that order),
 425  *                      and 'refCount' must be zero.
 426  *              Unlock list
 427  * (3)  Modify structure
 428  *              Find structure
 429  *              Acquire 'mutex'
 430  *                      Modify (except refCount)
 431  *              Release 'mutex'
 432  *              Release structure
 433  */
 434 
 435 __nis_ldap_conn_t               *ldapCon = 0;
 436 __nis_ldap_conn_t               *ldapReferralCon = 0;
 437 static rwlock_t                 ldapConLock = DEFAULTRWLOCK;
 438 static rwlock_t                 referralConLock = DEFAULTRWLOCK;
 439 
 440 void
 441 exclusiveLC(__nis_ldap_conn_t *lc) {
 442         pthread_t       me = pthread_self();
 443         int             stat;
 444 
 445         if (lc == 0)
 446                 return;
 447 
 448         stat = mutex_trylock(&lc->mutex);
 449         if (stat == EBUSY && lc->owner != me)
 450                 mutex_lock(&lc->mutex);
 451 
 452         lc->owner = me;
 453 }
 454 
 455 /* Return 1 if mutex held by this thread, 0 otherwise */
 456 int
 457 assertExclusive(__nis_ldap_conn_t *lc) {
 458         pthread_t       me;
 459         int             stat;
 460 
 461         if (lc == 0)
 462                 return (0);
 463 
 464         stat = mutex_trylock(&lc->mutex);
 465 
 466         if (stat == 0) {
 467                 mutex_unlock(&lc->mutex);
 468                 return (0);
 469         }
 470 
 471         me = pthread_self();
 472         if (stat != EBUSY || lc->owner != me)
 473                 return (0);
 474 
 475         return (1);
 476 }
 477 
 478 void
 479 releaseLC(__nis_ldap_conn_t *lc) {
 480         pthread_t       me = pthread_self();
 481 
 482         if (lc == 0 || lc->owner != me)
 483                 return;
 484 
 485         lc->owner = 0;
 486         (void) mutex_unlock(&lc->mutex);
 487 }
 488 
 489 void
 490 incrementRC(__nis_ldap_conn_t *lc) {
 491         if (lc == 0)
 492                 return;
 493 
 494         (void) mutex_lock(&lc->rcMutex);
 495         lc->refCount++;
 496         (void) mutex_unlock(&lc->rcMutex);
 497 }
 498 
 499 void
 500 decrementRC(__nis_ldap_conn_t *lc) {
 501         if (lc == 0)
 502                 return;
 503 
 504         (void) mutex_lock(&lc->rcMutex);
 505         if (lc->refCount > 0)
 506                 lc->refCount--;
 507         (void) mutex_unlock(&lc->rcMutex);
 508 }
 509 
 510 /* Accept a server/port indication, and call ldap_init() */
 511 static LDAP *
 512 ldapInit(char *srv, int port, bool_t use_ssl) {
 513         LDAP                    *ld;
 514         int                     ldapVersion = LDAP_VERSION3;
 515         int                     derefOption = LDAP_DEREF_ALWAYS;
 516         int                     timelimit = proxyInfo.search_time_limit;
 517         int                     sizelimit = proxyInfo.search_size_limit;
 518         char                    *myself = "ldapInit";
 519 
 520         if (srv == 0)
 521                 return (0);
 522 
 523         if (use_ssl) {
 524                 ld = ldapssl_init(srv, port, 1);
 525         } else {
 526                 ld = ldap_init(srv, port);
 527         }
 528 
 529         if (ld != 0) {
 530                 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
 531                                         &ldapVersion);
 532                 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
 533                 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 534                 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &timelimit);
 535                 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
 536                 (void) ldap_set_option(ld, LDAP_OPT_REBIND_ARG, 0);
 537         }
 538 
 539         return (ld);
 540 }
 541 
 542 /*
 543  * Bind the specified LDAP structure per the supplied authentication.
 544  * Note: tested with none, simple, and digest_md5. May or may not
 545  * work with other authentication methods, mostly depending on whether
 546  * or not 'who' and 'cred' contain sufficient information.
 547  */
 548 static int
 549 ldapBind(LDAP **ldP, char *who, char *cred, auth_method_t method,
 550                 struct timeval timeout) {
 551         int             ret;
 552         LDAP            *ld;
 553         char            *myself = "ldapBind";
 554 
 555         if (ldP == 0 || (ld = *ldP) == 0)
 556                 return (LDAP_PARAM_ERROR);
 557 
 558         if (method == none) {
 559                 /* No ldap_bind() required (or even possible) */
 560                 ret = LDAP_SUCCESS;
 561         } else if (method == simple) {
 562                 struct timeval  tv;
 563                 LDAPMessage     *msg = 0;
 564 
 565                 tv = timeout;
 566                 ret = ldap_bind(ld, who, cred, LDAP_AUTH_SIMPLE);
 567                 if (ret != -1) {
 568                         ret = ldap_result(ld, ret, 0, &tv, &msg);
 569                         if (ret == 0) {
 570                                 ret = LDAP_TIMEOUT;
 571                         } else if (ret == -1) {
 572                                 (void) ldap_get_option(ld,
 573                                                         LDAP_OPT_ERROR_NUMBER,
 574                                                         &ret);
 575                         } else {
 576                                 ret = ldap_result2error(ld, msg, 0);
 577                         }
 578                         if (msg != 0)
 579                                 (void) ldap_msgfree(msg);
 580                 } else {
 581                         (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
 582                                                 &ret);
 583                 }
 584         } else if (method == cram_md5) {
 585                 /* Note: there is only a synchronous call for cram-md5 */
 586                 struct berval ber_cred;
 587 
 588                 ber_cred.bv_len = strlen(cred);
 589                 ber_cred.bv_val = cred;
 590                 ret = ldap_sasl_cram_md5_bind_s(ld, who, &ber_cred, NULL, NULL);
 591         } else if (method == digest_md5) {
 592                 /* Note: there is only a synchronous call for digest-md5 */
 593                 struct berval ber_cred;
 594 
 595                 ber_cred.bv_len = strlen(cred);
 596                 ber_cred.bv_val = cred;
 597                 ret = ldap_x_sasl_digest_md5_bind_s(ld, who, &ber_cred, NULL,
 598                         NULL);
 599         } else {
 600                 ret = LDAP_AUTH_METHOD_NOT_SUPPORTED;
 601         }
 602 
 603         if (ret != LDAP_SUCCESS) {
 604                 (void) ldap_unbind_s(ld);
 605                 *ldP = 0;
 606                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 607                         "%s: Unable to bind as: %s: %s",
 608                         myself, who, ldap_err2string(ret));
 609         }
 610 
 611         return (ret);
 612 }
 613 
 614 /*
 615  * Free 'lc' and all related memory. Caller must hold the exclusive lock.
 616  * Return LDAP_UNAVAILABLE upon success, in which case the caller mustn't
 617  * try to use the structure pointer in any way.
 618  */
 619 static int
 620 freeCon(__nis_ldap_conn_t *lc) {
 621         char                    *myself = "freeCon";
 622 
 623         if (!assertExclusive(lc))
 624                 return (LDAP_PARAM_ERROR);
 625 
 626         incrementRC(lc);
 627 
 628         /* Must be unused, unbound, and not on the 'ldapCon' list */
 629         if (lc->onList || lc->refCount != 1 || lc->isBound) {
 630                 lc->doDel++;
 631                 decrementRC(lc);
 632                 return (LDAP_BUSY);
 633         }
 634 
 635         sfree(lc->sp);
 636         sfree(lc->who);
 637         sfree(lc->cred);
 638 
 639         /* Delete structure with both mutex:es held */
 640 
 641         free(lc);
 642 
 643         return (LDAP_UNAVAILABLE);
 644 }
 645 
 646 /*
 647  * Disconnect the specified LDAP connection. Caller must have acquired 'mutex'.
 648  *
 649  * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
 650  * the structure in any way.
 651  */
 652 static int
 653 disconnectCon(__nis_ldap_conn_t *lc) {
 654         int     stat;
 655         char    *myself = "disconnectCon";
 656 
 657         if (lc == 0)
 658                 return (LDAP_SUCCESS);
 659 
 660         if (!assertExclusive(lc))
 661                 return (LDAP_UNAVAILABLE);
 662 
 663         if (lc->doDis) {
 664 
 665                 /* Increment refCount to protect against interference */
 666                 incrementRC(lc);
 667                 /* refCount must be one (i.e., just us) */
 668                 if (lc->refCount != 1) {
 669                         /*
 670                          * In use; already marked for disconnect,
 671                          * so do nothing.
 672                          */
 673                         decrementRC(lc);
 674                         return (LDAP_BUSY);
 675                 }
 676 
 677                 stat = ldap_unbind_s(lc->ld);
 678                 if (stat == LDAP_SUCCESS) {
 679                         lc->ld = 0;
 680                         lc->isBound = 0;
 681                         lc->doDis = 0;
 682                         /* Reset simple page and vlv indication */
 683                         lc->simplePage = 0;
 684                         lc->vlv = 0;
 685                 } else if (verbose) {
 686                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
 687                                 "%s: ldap_unbind_s() => %d (%s)",
 688                                 myself, stat, ldap_err2string(stat));
 689                 }
 690 
 691                 decrementRC(lc);
 692         }
 693 
 694         if (lc->doDel) {
 695                 if (LDAP_UNAVAILABLE == freeCon(lc))
 696                         stat = LDAP_UNAVAILABLE;
 697         }
 698 
 699         return (stat);
 700 }
 701 
 702 /*
 703  * controlSupported will determine for a given connection whether a set
 704  * of controls is supported or not. The input parameters:
 705  *      lc      The connection
 706  *      ctrl    A an array of OID strings, the terminal string should be NULL
 707  * The returned values if LDAP_SUCCESS is returned:
 708  *      supported       A caller supplied array which will be set to TRUE or
 709  *                      FALSE depending on whether the corresponding control
 710  *                      is reported as supported.
 711  * Returns LDAP_SUCCESS if the supportedControl attribute is read.
 712  */
 713 
 714 static int
 715 controlSupported(__nis_ldap_conn_t *lc, char **ctrl, bool_t *supported) {
 716         LDAPMessage     *res, *e;
 717         char            *attr[2], *a, **val;
 718         int             stat, i;
 719         BerElement      *ber = 0;
 720         char            *myself = "controlSupported";
 721 
 722         attr[0] = "supportedControl";
 723         attr[1] = 0;
 724 
 725         stat = ldap_search_st(lc->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
 726                                 attr, 0, &lc->searchTimeout, &res);
 727         if (stat != LDAP_SUCCESS) {
 728                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 729         "%s: Unable to retrieve supported control information for %s: %s",
 730                         myself, NIL(lc->sp), ldap_err2string(stat));
 731                 return (stat);
 732         }
 733 
 734         e = ldap_first_entry(lc->ld, res);
 735         if (e != 0) {
 736                 a = ldap_first_attribute(lc->ld, e, &ber);
 737                 if (a != 0) {
 738                         val = ldap_get_values(lc->ld, e, a);
 739                         if (val == 0) {
 740                                 ldap_memfree(a);
 741                                 if (ber != 0)
 742                                         ber_free(ber, 0);
 743                         }
 744                 }
 745         }
 746         if (e == 0 || a == 0 || val == 0) {
 747                 ldap_msgfree(res);
 748                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 749                         "%s: Unable to get root DSE for %s",
 750                         myself, NIL(lc->sp));
 751                 return (LDAP_OPERATIONS_ERROR);
 752         }
 753 
 754         while (*ctrl != NULL) {
 755                 *supported = FALSE;
 756                 for (i = 0; val[i] != 0; i++) {
 757                         if (strstr(val[i], *ctrl) != 0) {
 758                                 *supported = TRUE;
 759                                 break;
 760                         }
 761                 }
 762                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
 763                         "%s: %s: %s: %s",
 764                         myself, NIL(lc->sp), NIL(*ctrl),
 765                         *supported ? "enabled" : "disabled");
 766                 ctrl++;
 767                 supported++;
 768         }
 769 
 770         ldap_value_free(val);
 771         ldap_memfree(a);
 772         if (ber != 0)
 773                 ber_free(ber, 0);
 774         ldap_msgfree(res);
 775 
 776         return (stat);
 777 }
 778 
 779 /*
 780  * Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex',
 781  * and the refCount must be zero.
 782  *
 783  * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
 784  * the structure in any way.
 785  */
 786 static int
 787 connectCon(__nis_ldap_conn_t *lc, int check_ctrl) {
 788         struct timeval  tp;
 789         int             stat;
 790         bool_t          supported[2] = {FALSE, FALSE};
 791         char            *ctrl[3] = {LDAP_CONTROL_SIMPLE_PAGE,
 792                                         LDAP_CONTROL_VLVREQUEST,
 793                                         NULL};
 794 
 795         if (lc == 0)
 796                 return (LDAP_SUCCESS);
 797 
 798         if (!assertExclusive(lc))
 799                 return (LDAP_PARAM_ERROR);
 800 
 801         incrementRC(lc);
 802         if (lc->refCount != 1) {
 803                 /*
 804                  * Don't want to step on structure when it's used by someone
 805                  * else.
 806                  */
 807                 decrementRC(lc);
 808                 return (LDAP_BUSY);
 809         }
 810 
 811         (void) gettimeofday(&tp, 0);
 812 
 813         if (lc->ld != 0) {
 814                 /* Try to disconnect */
 815                 lc->doDis++;
 816                 decrementRC(lc);
 817                 /* disconnctCon() will do the delete if required */
 818                 stat = disconnectCon(lc);
 819                 if (stat != LDAP_SUCCESS)
 820                         return (stat);
 821                 incrementRC(lc);
 822                 if (lc->refCount != 1 || lc->ld != 0) {
 823                         decrementRC(lc);
 824                         return (lc->ld != 0) ? LDAP_SUCCESS :
 825                                                 LDAP_BUSY;
 826                 }
 827         } else if (tp.tv_sec < lc->retryTime) {
 828                 /* Too early to retry connect */
 829                 decrementRC(lc);
 830                 return (LDAP_SERVER_DOWN);
 831         }
 832 
 833         /* Set new retry time in case we fail below */
 834         lc->retryTime = tp.tv_sec + ldapConnAttemptRetryTimeout;
 835 
 836         lc->ld = ldapInit(lc->sp, lc->port, proxyInfo.tls_method != no_tls);
 837         if (lc->ld == 0) {
 838                 decrementRC(lc);
 839                 return (LDAP_LOCAL_ERROR);
 840         }
 841 
 842         stat = lc->status = ldapBind(&lc->ld, lc->who, lc->cred, lc->method,
 843                 lc->bindTimeout);
 844         if (lc->status == LDAP_SUCCESS) {
 845                 lc->isBound = 1;
 846                 lc->retryTime = 0;
 847                 if (check_ctrl) {
 848                         (void) controlSupported(lc, ctrl, supported);
 849                         lc->simplePage = supported[0];
 850                         lc->vlv = supported[1];
 851                         lc->batchFrom = 50000;
 852                 }
 853         }
 854 
 855         decrementRC(lc);
 856 
 857         return (stat);
 858 }
 859 
 860 /*
 861  * Find and return a connection believed to be OK.
 862  */
 863 static __nis_ldap_conn_t *
 864 findCon(int *stat) {
 865         __nis_ldap_conn_t       *lc;
 866         int                     ldapStat;
 867         char                    *myself = "findCon";
 868 
 869         if (stat == 0)
 870                 stat = &ldapStat;
 871 
 872         (void) rw_rdlock(&ldapConLock);
 873 
 874         if (ldapCon == 0) {
 875                 /* Probably first call; try to set up the connection list */
 876                 (void) rw_unlock(&ldapConLock);
 877                 if ((*stat = setupConList(proxyInfo.default_servers,
 878                                         proxyInfo.proxy_dn,
 879                                         proxyInfo.proxy_passwd,
 880                                         proxyInfo.auth_method)) !=
 881                                         LDAP_SUCCESS)
 882                         return (0);
 883                 (void) rw_rdlock(&ldapConLock);
 884         }
 885 
 886         for (lc = ldapCon; lc != 0; lc = lc->next) {
 887                 exclusiveLC(lc);
 888                 if (!lc->isBound) {
 889                         *stat = connectCon(lc, 1);
 890                         if (*stat != LDAP_SUCCESS) {
 891                                 if (*stat != LDAP_UNAVAILABLE) {
 892                                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 893                 "%s: Cannot open connection to LDAP server (%s): %s",
 894                                                 myself, NIL(lc->sp),
 895                                                 ldap_err2string(*stat));
 896                                         releaseLC(lc);
 897                                 }
 898                                 continue;
 899                         }
 900                 } else if (lc->doDis || lc->doDel) {
 901                         *stat = disconnectCon(lc);
 902                         if (*stat != LDAP_UNAVAILABLE)
 903                                 releaseLC(lc);
 904                         continue;
 905                 }
 906                 incrementRC(lc);
 907                 releaseLC(lc);
 908                 break;
 909         }
 910 
 911         (void) rw_unlock(&ldapConLock);
 912 
 913         return (lc);
 914 }
 915 
 916 /* Release connection; decrements ref count for the connection */
 917 static void
 918 releaseCon(__nis_ldap_conn_t *lc, int status) {
 919         int     stat;
 920 
 921         if (lc == 0)
 922                 return;
 923 
 924         exclusiveLC(lc);
 925 
 926         lc->status = status;
 927 
 928         decrementRC(lc);
 929 
 930         if (lc->doDis)
 931                 stat = disconnectCon(lc);
 932         else
 933                 stat = LDAP_SUCCESS;
 934 
 935         if (stat != LDAP_UNAVAILABLE)
 936                 releaseLC(lc);
 937 }
 938 
 939 static __nis_ldap_conn_t *
 940 createCon(char *sp, char *who, char *cred, auth_method_t method, int port) {
 941         __nis_ldap_conn_t       *lc;
 942         char                    *myself = "createCon";
 943         char                    *r;
 944 
 945         if (sp == 0)
 946                 return (0);
 947 
 948         lc = am(myself, sizeof (*lc));
 949         if (lc == 0)
 950                 return (0);
 951 
 952         (void) mutex_init(&lc->mutex, 0, 0);
 953         (void) mutex_init(&lc->rcMutex, 0, 0);
 954 
 955         /* If we need to delete 'lc', freeCon() wants the mutex held */
 956         exclusiveLC(lc);
 957 
 958         lc->sp = sdup(myself, T, sp);
 959         if (lc->sp == 0) {
 960                 (void) freeCon(lc);
 961                 return (0);
 962         }
 963 
 964         if ((r = strchr(lc->sp, ']')) != 0) {
 965                 /*
 966                  * IPv6 address. Does libldap want this with the
 967                  * '[' and ']' left in place ? Assume so for now.
 968                  */
 969                 r = strchr(r, ':');
 970         } else {
 971                 r = strchr(lc->sp, ':');
 972         }
 973 
 974         if (r != NULL) {
 975                 *r++ = '\0';
 976                 port = atoi(r);
 977         } else if (port == 0)
 978                 port = proxyInfo.tls_method == ssl_tls ? LDAPS_PORT : LDAP_PORT;
 979 
 980         if (who != 0) {
 981                 lc->who = sdup(myself, T, who);
 982                 if (lc->who == 0) {
 983                         (void) freeCon(lc);
 984                         return (0);
 985                 }
 986         }
 987 
 988         if (cred != 0) {
 989                 lc->cred = sdup(myself, T, cred);
 990                 if (lc->cred == 0) {
 991                         (void) freeCon(lc);
 992                         return (0);
 993                 }
 994         }
 995 
 996         lc->method = method;
 997         lc->port = port;
 998 
 999         lc->bindTimeout = proxyInfo.bind_timeout;
1000         lc->searchTimeout = proxyInfo.search_timeout;
1001         lc->modifyTimeout = proxyInfo.modify_timeout;
1002         lc->addTimeout = proxyInfo.add_timeout;
1003         lc->deleteTimeout = proxyInfo.delete_timeout;
1004 
1005         /* All other fields OK at zero */
1006 
1007         releaseLC(lc);
1008 
1009         return (lc);
1010 }
1011 
1012 static int
1013 setupConList(char *serverList, char *who, char *cred, auth_method_t method) {
1014         char                    *sls, *sl, *s, *e;
1015         __nis_ldap_conn_t       *lc, *tmp;
1016         char                    *myself = "setupConList";
1017 
1018         if (serverList == 0)
1019                 return (LDAP_PARAM_ERROR);
1020 
1021         (void) rw_wrlock(&ldapConLock);
1022 
1023         if (ldapCon != 0) {
1024                 /* Assume we've already been called and done the set-up */
1025                 (void) rw_unlock(&ldapConLock);
1026                 return (LDAP_SUCCESS);
1027         }
1028 
1029         /* Work on a copy of 'serverList' */
1030         sl = sls = sdup(myself, T, serverList);
1031         if (sl == 0) {
1032                 (void) rw_unlock(&ldapConLock);
1033                 return (LDAP_NO_MEMORY);
1034         }
1035 
1036         /* Remove leading white space */
1037         for (0; *sl == ' ' || *sl == '\t'; sl++);
1038 
1039         /* Create connection for each server on the list */
1040         for (s = sl; *s != '\0'; s = e+1) {
1041                 int     l;
1042 
1043                 /* Find end of server/port token */
1044                 for (e = s; *e != ' ' && *e != '\t' && *e != '\0'; e++);
1045                 if (*e != '\0')
1046                         *e = '\0';
1047                 else
1048                         e--;
1049                 l = slen(s);
1050 
1051                 if (l > 0) {
1052                         lc = createCon(s, who, cred, method, 0);
1053                         if (lc == 0) {
1054                                 free(sls);
1055                                 (void) rw_unlock(&ldapConLock);
1056                                 return (LDAP_NO_MEMORY);
1057                         }
1058                         lc->onList = 1;
1059                         if (ldapCon == 0) {
1060                                 ldapCon = lc;
1061                         } else {
1062                                 /* Insert at end of list */
1063                                 for (tmp = ldapCon; tmp->next != 0;
1064                                         tmp = tmp->next);
1065                                 tmp->next = lc;
1066                         }
1067                 }
1068         }
1069 
1070         free(sls);
1071 
1072         (void) rw_unlock(&ldapConLock);
1073 
1074         return (LDAP_SUCCESS);
1075 }
1076 
1077 static bool_t
1078 is_same_connection(__nis_ldap_conn_t *lc, LDAPURLDesc *ludpp)
1079 {
1080         return (strcasecmp(ludpp->lud_host, lc->sp) == 0 &&
1081             ludpp->lud_port == lc->port);
1082 }
1083 
1084 static __nis_ldap_conn_t *
1085 find_connection_from_list(__nis_ldap_conn_t *list,
1086                         LDAPURLDesc *ludpp, int *stat)
1087 {
1088         int                     ldapStat;
1089         __nis_ldap_conn_t       *lc     = NULL;
1090         if (stat == 0)
1091                 stat = &ldapStat;
1092 
1093         *stat = LDAP_SUCCESS;
1094 
1095         for (lc = list; lc != 0; lc = lc->next) {
1096                 exclusiveLC(lc);
1097                 if (is_same_connection(lc, ludpp)) {
1098                         if (!lc->isBound) {
1099                                 *stat = connectCon(lc, 1);
1100                                 if (*stat != LDAP_SUCCESS) {
1101                                         releaseLC(lc);
1102                                         continue;
1103                                 }
1104                         } else if (lc->doDis || lc->doDel) {
1105                                 (void) disconnectCon(lc);
1106                                 releaseLC(lc);
1107                                 continue;
1108                         }
1109                         incrementRC(lc);
1110                         releaseLC(lc);
1111                         break;
1112                 }
1113                 releaseLC(lc);
1114         }
1115         return (lc);
1116 }
1117 
1118 static __nis_ldap_conn_t *
1119 findReferralCon(char **referralsp, int *stat)
1120 {
1121         __nis_ldap_conn_t       *lc     = NULL;
1122         __nis_ldap_conn_t       *tmp;
1123         int                     ldapStat;
1124         int                     i;
1125         LDAPURLDesc             *ludpp  = NULL;
1126         char                    *myself = "findReferralCon";
1127 
1128         if (stat == 0)
1129                 stat = &ldapStat;
1130 
1131         *stat = LDAP_SUCCESS;
1132 
1133         /*
1134          * We have the referral lock - to prevent multiple
1135          * threads from creating a referred connection simultaneously
1136          *
1137          * Note that this code assumes that the ldapCon list is a
1138          * static list - that it has previously been created
1139          * (otherwise we wouldn't have gotten a referral) and that
1140          * it will neither grow or shrink - elements may have new
1141          * connections or unbound. If this assumption is no longer valid,
1142          * the locking needs to be reworked.
1143          */
1144         (void) rw_rdlock(&referralConLock);
1145 
1146         for (i = 0; referralsp[i] != NULL; i++) {
1147                 if (ldap_url_parse(referralsp[i], &ludpp) != LDAP_SUCCESS)
1148                         continue;
1149                 /* Ignore referrals if not at the appropriate tls level */
1150 #ifdef LDAP_URL_OPT_SECURE
1151                 if (ludpp->lud_options & LDAP_URL_OPT_SECURE) {
1152                         if (proxyInfo.tls_method != ssl_tls) {
1153                                 ldap_free_urldesc(ludpp);
1154                                 continue;
1155                         }
1156                 } else {
1157                         if (proxyInfo.tls_method != no_tls) {
1158                                 ldap_free_urldesc(ludpp);
1159                                 continue;
1160                         }
1161                 }
1162 #endif
1163 
1164                 /* Determine if we already have a connection to the server */
1165                 lc = find_connection_from_list(ldapReferralCon, ludpp, stat);
1166                 if (lc == NULL)
1167                         lc = find_connection_from_list(ldapCon, ludpp, stat);
1168                 ldap_free_urldesc(ludpp);
1169                 if (lc != NULL) {
1170                         (void) rw_unlock(&referralConLock);
1171                         return (lc);
1172                 }
1173         }
1174 
1175         for (i = 0; referralsp[i] != NULL; i++) {
1176                 if (ldap_url_parse(referralsp[i], &ludpp) != LDAP_SUCCESS)
1177                         continue;
1178                 /* Ignore referrals if not at the appropriate tls level */
1179 #ifdef LDAP_URL_OPT_SECURE
1180                 if (ludpp->lud_options & LDAP_URL_OPT_SECURE) {
1181                         if (proxyInfo.tls_method != ssl_tls) {
1182                                 ldap_free_urldesc(ludpp);
1183                                 continue;
1184                         }
1185                 } else {
1186                         if (proxyInfo.tls_method != no_tls) {
1187                                 ldap_free_urldesc(ludpp);
1188                                 continue;
1189                         }
1190                 }
1191 #endif
1192                 lc = createCon(ludpp->lud_host, proxyInfo.proxy_dn,
1193                     proxyInfo.proxy_passwd,
1194                     proxyInfo.auth_method,
1195                     ludpp->lud_port);
1196                 if (lc == 0) {
1197                         ldap_free_urldesc(ludpp);
1198                         (void) rw_unlock(&referralConLock);
1199                         *stat = LDAP_NO_MEMORY;
1200                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1201                             "%s: Could not connect to host: %s",
1202                             myself, NIL(ludpp->lud_host));
1203                         return (NULL);
1204                 }
1205 
1206                 lc->onList = 1;
1207                 if (ldapReferralCon == 0) {
1208                         ldapReferralCon = lc;
1209                 } else {
1210                         /* Insert at end of list */
1211                         for (tmp = ldapReferralCon; tmp->next != 0;
1212                             tmp = tmp->next) {}
1213                         tmp->next = lc;
1214                 }
1215                 lc = find_connection_from_list(ldapReferralCon, ludpp, stat);
1216                 ldap_free_urldesc(ludpp);
1217                 if (lc != NULL)
1218                         break;
1219         }
1220         (void) rw_unlock(&referralConLock);
1221         if (lc == NULL) {
1222                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1223                     "%s: Could not find a connection to %s, ...",
1224                     myself, NIL(referralsp[0]));
1225         }
1226 
1227         return (lc);
1228 }
1229 
1230 /*
1231  * Find and return a connection believed to be OK and ensure children
1232  * will never use parent's connection.
1233  */
1234 static __nis_ldap_conn_t *
1235 findYPCon(__nis_ldap_search_t *ls, int *stat) {
1236         __nis_ldap_conn_t       *lc, *newlc;
1237         int                     ldapStat, newstat;
1238         char                    *myself = "findYPCon";
1239 
1240         if (stat == 0)
1241                 stat = &ldapStat;
1242 
1243         (void) rw_rdlock(&ldapConLock);
1244 
1245         if (ldapCon == 0) {
1246                 /* Probably first call; try to set up the connection list */
1247                 (void) rw_unlock(&ldapConLock);
1248                 if ((*stat = setupConList(proxyInfo.default_servers,
1249                                         proxyInfo.proxy_dn,
1250                                         proxyInfo.proxy_passwd,
1251                                         proxyInfo.auth_method)) !=
1252                                         LDAP_SUCCESS)
1253                         return (0);
1254                 (void) rw_rdlock(&ldapConLock);
1255         }
1256 
1257         for (lc = ldapCon; lc != 0; lc = lc->next) {
1258                 exclusiveLC(lc);
1259 
1260                 if (lc->isBound && (lc->doDis || lc->doDel)) {
1261                         *stat = disconnectCon(lc);
1262                         if (*stat != LDAP_UNAVAILABLE)
1263                                 releaseLC(lc);
1264                         continue;
1265                 }
1266 
1267                 /*
1268                  * Use a new connection for all cases except when
1269                  * requested by the main thread in the parent ypserv
1270                  * process.
1271                  */
1272                 if (ls->useCon == 0) {
1273                         newlc = createCon(lc->sp, lc->who, lc->cred,
1274                                                 lc->method, lc->port);
1275                         if (!newlc) {
1276                                 releaseLC(lc);
1277                                 continue;
1278                         }
1279                         if (lc->ld != 0) {
1280                                 newlc->simplePage = lc->simplePage;
1281                                 newlc->vlv = lc->vlv;
1282                                 newlc->batchFrom = lc->batchFrom;
1283                         }
1284                         releaseLC(lc);
1285                         exclusiveLC(newlc);
1286                         newstat = connectCon(newlc, 0);
1287                         if (newstat != LDAP_SUCCESS) {
1288                                 if (newstat != LDAP_UNAVAILABLE) {
1289                                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1290                         "%s: Cannot open connection to LDAP server (%s): %s",
1291                                                 myself, NIL(newlc->sp),
1292                                                 ldap_err2string(*stat));
1293                                 }
1294                                 (void) freeCon(newlc);
1295                                 newlc = 0;
1296                                 continue;
1297                         }
1298 
1299                         /*
1300                          * No need to put newlc on the ldapCon list as this
1301                          * connection will be freed after use.
1302                          */
1303                         newlc->onList = 0;
1304 
1305                         lc = newlc;
1306                 } else  if (!lc->isBound) {
1307                         *stat = connectCon(lc, 1);
1308                         if (*stat != LDAP_SUCCESS) {
1309                                 if (*stat != LDAP_UNAVAILABLE) {
1310                                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1311                 "%s: Cannot open connection to LDAP server (%s): %s",
1312                                                 myself, NIL(lc->sp),
1313                                                 ldap_err2string(*stat));
1314                                         releaseLC(lc);
1315                                 }
1316                                 continue;
1317                         }
1318                 }
1319 
1320                 incrementRC(lc);
1321                 releaseLC(lc);
1322                 break;
1323         }
1324 
1325         (void) rw_unlock(&ldapConLock);
1326 
1327         return (lc);
1328 }
1329 
1330 #define SORTKEYLIST     "cn uid"
1331 
1332 /*
1333  * Perform an LDAP search operation per 'ls', adding the result(s) to
1334  * a copy of the 'rvIn' structure; the copy becomes the return value.
1335  * The caller must deallocate both 'rvIn' and the result, if any.
1336  *
1337  * On entry, '*numValues' contains a hint regarding the expected
1338  * number of entries. Zero is the same as one, and negative values
1339  * imply no information. This is used to decide whether or not to
1340  * try an indexed search.
1341  *
1342  * On successful (non-NULL) return, '*numValues' contains the number
1343  * of __nis_rule_value_t elements in the returned array, and '*stat'
1344  * the LDAP operations status.
1345  */
1346 __nis_rule_value_t *
1347 ldapSearch(__nis_ldap_search_t *ls, int *numValues, __nis_rule_value_t *rvIn,
1348                 int *ldapStat) {
1349         __nis_rule_value_t      *rv = 0;
1350         int                     stat, numEntries, numVals, tnv, done, lprEc;
1351         LDAPMessage             *msg = 0, *m;
1352         __nis_ldap_conn_t       *lc;
1353         struct timeval          tv, start, now;
1354         LDAPsortkey             **sortKeyList = 0;
1355         LDAPControl             *ctrls[3], *sortCtrl = 0, *vlvCtrl = 0;
1356         LDAPControl             **retCtrls = 0;
1357         LDAPVirtualList         vList;
1358         struct berval           *spCookie = 0;
1359         int                     doVLV = 0;
1360         int                     doSP = 0;
1361         long                    index;
1362         char                    *myself = "ldapSearch";
1363         bool_t                  follow_referral =
1364                                         proxyInfo.follow_referral == follow;
1365         int                     doIndex = 1;
1366         char                    **referralsp = NULL;
1367 
1368         ctrls[0] = ctrls[1] = ctrls[2] = 0;
1369 
1370         if (ldapStat == 0)
1371                 ldapStat = &stat;
1372 
1373         if (ls == 0) {
1374                 *ldapStat = LDAP_PARAM_ERROR;
1375                 return (0);
1376         }
1377 
1378         if (yp2ldap) {
1379                 /* make sure the parent's connection is not used by child */
1380                 if ((lc = findYPCon(ls, ldapStat)) == 0) {
1381                         *ldapStat = LDAP_SERVER_DOWN;
1382                         return (0);
1383                 }
1384         } else {
1385                 if ((lc = findCon(ldapStat)) == 0) {
1386                         *ldapStat = LDAP_SERVER_DOWN;
1387                         return (0);
1388                 }
1389         }
1390 
1391         if (numValues != 0 && (*numValues == 0 || *numValues == 1))
1392                 doIndex = 0;
1393 
1394 retry_new_conn:
1395         /* Prefer VLV over simple page, and SP over nothing */
1396         if (doIndex && lc->vlv) {
1397                 stat = ldap_create_sort_keylist(&sortKeyList, SORTKEYLIST);
1398                 if (stat != LDAP_SUCCESS) {
1399                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1400                                 "%s: Error creating sort keylist: %s",
1401                                 myself, ldap_err2string(stat));
1402                         freeRuleValue(rv, numVals);
1403                         *ldapStat = stat;
1404                         rv = 0;
1405                         goto retry_noVLV;
1406                 }
1407                 stat = ldap_create_sort_control(lc->ld, sortKeyList, 1,
1408                                                 &sortCtrl);
1409                 if (stat == LDAP_SUCCESS) {
1410                         vList.ldvlist_before_count = 0;
1411                         vList.ldvlist_after_count = lc->batchFrom - 1;
1412                         vList.ldvlist_attrvalue = 0;
1413                         vList.ldvlist_extradata = 0;
1414                         index = 1;
1415                         doVLV = 1;
1416                 } else {
1417                         ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
1418                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1419                                 "%s: Error creating VLV sort control: %s",
1420                                 myself, ldap_err2string(stat));
1421                         freeRuleValue(rv, numVals);
1422                         *ldapStat = stat;
1423                         rv = 0;
1424                 }
1425         }
1426 
1427 retry_noVLV:
1428 
1429         if (doIndex && !doVLV && lc->simplePage) {
1430                 spCookie = am(myself, sizeof (*spCookie));
1431                 if (spCookie != 0 &&
1432                                 (spCookie->bv_val = sdup(myself, T, "")) != 0) {
1433                         spCookie->bv_len = 0;
1434                         doSP = 1;
1435                 } else {
1436                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1437         "%s: No memory for simple page cookie; using un-paged LDAP search",
1438                                 myself);
1439                         freeRuleValue(rv, numVals);
1440                         *ldapStat = stat;
1441                         rv = 0;
1442                         goto cleanup;
1443                 }
1444         }
1445 
1446         if (!doVLV && !doSP)
1447                 ctrls[0] = ctrls[1] = 0;
1448 
1449         numVals = 0;
1450         done = 0;
1451 
1452         if (ls->timeout.tv_sec || ls->timeout.tv_usec) {
1453                 tv = ls->timeout;
1454         } else {
1455                 tv = lc->searchTimeout;
1456         }
1457         (void) gettimeofday(&start, 0);
1458 
1459         do {
1460                 /* don't do vlv or simple page for base level searches */
1461                 if (doVLV && ls->base != LDAP_SCOPE_BASE) {
1462                         vList.ldvlist_index = index;
1463                         vList.ldvlist_size = 0;
1464                         if (vlvCtrl != 0)
1465                                 ldap_control_free(vlvCtrl);
1466                         stat = ldap_create_virtuallist_control(lc->ld,
1467                                         &vList, &vlvCtrl);
1468                         if (stat != LDAP_SUCCESS) {
1469                                 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
1470                                                 &stat);
1471                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1472                                 "%s: Error creating VLV at index %ld: %s",
1473                                         myself, index, ldap_err2string(stat));
1474                                 *ldapStat = stat;
1475                                 freeRuleValue(rv, numVals);
1476                                 rv = 0;
1477                                 goto cleanup;
1478                         }
1479                         ctrls[0] = sortCtrl;
1480                         ctrls[1] = vlvCtrl;
1481                         ctrls[2] = 0;
1482                         stat = ldap_search_ext_s(lc->ld, ls->base,
1483                                         ls->scope, ls->filter, ls->attrs,
1484                                         ls->attrsonly, ctrls, 0, &tv,
1485                                         proxyInfo.search_size_limit, &msg);
1486                 /* don't do vlv or simple page for base level searches */
1487                 } else if (doSP && ls->base != LDAP_SCOPE_BASE) {
1488                         if (ctrls[0] != 0)
1489                                 ldap_control_free(ctrls[0]);
1490                         stat = ldap_create_page_control(lc->ld,
1491                                         lc->batchFrom, spCookie, 0, &ctrls[0]);
1492                         if (stat != LDAP_SUCCESS) {
1493                                 ber_bvfree(spCookie);
1494                                 spCookie = 0;
1495                                 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
1496                                                 &stat);
1497                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1498                                         "%s: Simple page error: %s",
1499                                         myself, ldap_err2string(stat));
1500                                 freeRuleValue(rv, numVals);
1501                                 *ldapStat = stat;
1502                                 rv = 0;
1503                                 goto cleanup;
1504                         }
1505                         ctrls[1] = 0;
1506                         stat = ldap_search_ext_s(lc->ld, ls->base,
1507                                         ls->scope, ls->filter, ls->attrs,
1508                                         ls->attrsonly, ctrls, 0, &tv,
1509                                         proxyInfo.search_size_limit, &msg);
1510                 } else {
1511                         stat = ldap_search_st(lc->ld, ls->base, ls->scope,
1512                                         ls->filter, ls->attrs, ls->attrsonly,
1513                                         &tv, &msg);
1514                 }
1515                 if (stat == LDAP_SUCCESS)
1516                         ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
1517 
1518                 if (stat == LDAP_SERVER_DOWN) {
1519                         lc->doDis++;
1520                         releaseCon(lc, stat);
1521                         lc = (yp2ldap)?findYPCon(ls, ldapStat):
1522                                 findCon(ldapStat);
1523                         if (lc == 0) {
1524                                 *ldapStat = LDAP_SERVER_DOWN;
1525                                 rv =  0;
1526                                 goto cleanup;
1527                         }
1528                         goto retry_new_conn;
1529                 }
1530 
1531                 if (stat == LDAP_REFERRAL && follow_referral) {
1532                         (void) ldap_parse_result(lc->ld, msg, NULL, NULL, NULL,
1533                                 &referralsp, NULL, 0);
1534                         if (referralsp != NULL) {
1535                                 /* We support at most one level of referrals */
1536                                 follow_referral = FALSE;
1537                                 releaseCon(lc, stat);
1538                                 lc = findReferralCon(referralsp, &stat);
1539                                 ldap_value_free(referralsp);
1540                                 if (lc == NULL) {
1541                                         freeRuleValue(rv, numVals);
1542                                         rv = 0;
1543                                         *ldapStat = stat;
1544                                         goto cleanup;
1545                                 }
1546                                 stat = LDAP_SUCCESS;
1547                                 goto retry_new_conn;
1548                         }
1549                 }
1550                 *ldapStat = stat;
1551 
1552                 if (*ldapStat == LDAP_NO_SUCH_OBJECT) {
1553                         freeRuleValue(rv, numVals);
1554                         rv = 0;
1555                         goto cleanup;
1556                 } else if (doVLV && *ldapStat == LDAP_INSUFFICIENT_ACCESS) {
1557                         /*
1558                          * The LDAP server (at least Netscape 4.x) can return
1559                          * LDAP_INSUFFICIENT_ACCESS when VLV is supported,
1560                          * but not for the bind DN specified. So, just in
1561                          * case, we clean up, and try again without VLV.
1562                          */
1563                         doVLV = 0;
1564                         if (msg != 0) {
1565                                 (void) ldap_msgfree(msg);
1566                                 msg = 0;
1567                         }
1568                         if (ctrls[0] != 0) {
1569                                 ldap_control_free(ctrls[0]);
1570                                 ctrls[0] = 0;
1571                         }
1572                         if (ctrls[1] != 0) {
1573                                 ldap_control_free(ctrls[1]);
1574                                 ctrls[1] = 0;
1575                         }
1576                         logmsg(MSG_VLV_INSUFF_ACC, LOG_WARNING,
1577         "%s: VLV insufficient access from server %s; retrying without VLV",
1578                                 myself, NIL(lc->sp));
1579                         goto retry_noVLV;
1580                 } else if (*ldapStat != LDAP_SUCCESS) {
1581                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1582                                 "ldap_search(0x%x,\n\t\"%s\",\n\t %d,",
1583                                 lc->ld, NIL(ls->base), ls->scope);
1584                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1585                                 "\t\"%s\",\n\t0x%x,\n\t%d) => %d (%s)",
1586                                 NIL(ls->filter), ls->attrs, ls->attrsonly,
1587                                 *ldapStat, ldap_err2string(stat));
1588                         freeRuleValue(rv, numVals);
1589                         rv = 0;
1590                         goto cleanup;
1591                 }
1592 
1593                 numEntries = ldap_count_entries(lc->ld, msg);
1594                 if (numEntries == 0 && *ldapStat == LDAP_SUCCESS) {
1595                         /*
1596                          * This is a bit weird, but the server (or, at least,
1597                          * ldap_search_ext()) can sometimes return
1598                          * LDAP_SUCCESS and no entries when it didn't
1599                          * find what we were looking for. Seems it ought to
1600                          * return LDAP_NO_SUCH_OBJECT or some such.
1601                          */
1602                         freeRuleValue(rv, numVals);
1603                         rv = 0;
1604                         *ldapStat = LDAP_NO_SUCH_OBJECT;
1605                         goto cleanup;
1606                 }
1607 
1608                 tnv = numVals + numEntries;
1609                 if ((rv = growRuleValue(numVals, tnv, rv, rvIn)) == 0) {
1610                         *ldapStat = LDAP_NO_MEMORY;
1611                         goto cleanup;
1612                 }
1613 
1614                 for (m = ldap_first_entry(lc->ld, msg); m != 0;
1615                                 m = ldap_next_entry(lc->ld, m), numVals++) {
1616                         char            *nm;
1617                         BerElement      *ber = 0;
1618 
1619                         if (numVals > tnv) {
1620                                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1621                                 "%s: Inconsistent LDAP entry count > %d",
1622                                         myself, numEntries);
1623                                 break;
1624                         }
1625 
1626                         nm = ldap_get_dn(lc->ld, m);
1627                         if (nm == 0 || addSAttr2RuleValue("dn", nm,
1628                                         &rv[numVals])) {
1629                                 sfree(nm);
1630                                 *ldapStat = LDAP_NO_MEMORY;
1631                                 freeRuleValue(rv, tnv);
1632                                 rv = 0;
1633                                 goto cleanup;
1634                         }
1635                         sfree(nm);
1636 
1637                         for (nm = ldap_first_attribute(lc->ld, m, &ber);
1638                                         nm != 0;
1639                                 nm = ldap_next_attribute(lc->ld, m, ber)) {
1640                                 struct berval   **val;
1641                                 int             i, nv;
1642 
1643                                 val = ldap_get_values_len(lc->ld, m, nm);
1644                                 nv = (val == 0) ? 0 :
1645                                                 ldap_count_values_len(val);
1646                                 for (i = 0; i < nv; i++) {
1647                                         /*
1648                                          * Since we don't know if the value is
1649                                          * BER-encoded or not, we mark it as a
1650                                          * string. All is well as long as we
1651                                          * don't insist on 'vt_ber' when
1652                                          * interpreting.
1653                                          */
1654                                         if (addAttr2RuleValue(vt_string, nm,
1655                                                         val[i]->bv_val,
1656                                                         val[i]->bv_len,
1657                                                         &rv[numVals])) {
1658                                                 if (ber != 0)
1659                                                         ber_free(ber, 0);
1660                                                 ldap_value_free_len(val);
1661                                                 *ldapStat = LDAP_NO_MEMORY;
1662                                                 freeRuleValue(rv, tnv);
1663                                                 rv = 0;
1664                                                 goto cleanup;
1665                                         }
1666                                 }
1667                                 ldap_memfree(nm);
1668                                 if (val != 0)
1669                                         ldap_value_free_len(val);
1670                         }
1671                         if (ber != 0)
1672                                 ber_free(ber, 0);
1673                 }
1674 
1675                 if (numVals != tnv) {
1676                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1677                 "%s: Inconsistent LDAP entry count, found = %d, expected %d",
1678                                 myself, numVals, tnv);
1679                 }
1680 
1681                 if (doVLV) {
1682                         stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0,
1683                                                 &retCtrls, 0);
1684                         if (stat != LDAP_SUCCESS) {
1685                                 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
1686                                                 &stat);
1687                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1688                                         "%s: VLV parse result error: %s",
1689                                         myself, ldap_err2string(stat));
1690                                 *ldapStat = stat;
1691                                 freeRuleValue(rv, tnv);
1692                                 rv = 0;
1693                                 goto cleanup;
1694                         }
1695                         if (retCtrls != 0) {
1696                                 unsigned long   targetPosP = 0;
1697                                 unsigned long   listSize = 0;
1698 
1699                                 stat = ldap_parse_virtuallist_control(lc->ld,
1700                                         retCtrls, &targetPosP, &listSize,
1701                                         &lprEc);
1702                                 if (stat == LDAP_SUCCESS) {
1703                                         index = targetPosP + lc->batchFrom;
1704                                         if (index >= listSize)
1705                                                 done = 1;
1706                                 }
1707                                 ldap_controls_free(retCtrls);
1708                                 retCtrls = 0;
1709                         } else {
1710                                 done = 1;
1711                         }
1712                 } else if (doSP) {
1713                         stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0,
1714                                                 &retCtrls, 0);
1715                         if (stat != LDAP_SUCCESS) {
1716                                 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
1717                                                 &stat);
1718                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1719                                 "%s: Simple page parse result error: %s",
1720                                         myself, ldap_err2string(stat));
1721                                 *ldapStat = stat;
1722                                 freeRuleValue(rv, tnv);
1723                                 rv = 0;
1724                                 goto cleanup;
1725                         }
1726                         if (retCtrls != 0) {
1727                                 unsigned int    count;
1728 
1729                                 if (spCookie != 0) {
1730                                         ber_bvfree(spCookie);
1731                                         spCookie = 0;
1732                                 }
1733                                 stat = ldap_parse_page_control(lc->ld,
1734                                                 retCtrls, &count, &spCookie);
1735                                 if (stat == LDAP_SUCCESS) {
1736                                         if (spCookie == 0 ||
1737                                                 spCookie->bv_val == 0 ||
1738                                                 spCookie->bv_len == 0)
1739                                                 done = 1;
1740                                 }
1741                                 ldap_controls_free(retCtrls);
1742                                 retCtrls = 0;
1743                         } else {
1744                                 done = 1;
1745                         }
1746                 } else {
1747                         done = 1;
1748                 }
1749 
1750                 (void) ldap_msgfree(msg);
1751                 msg = 0;
1752 
1753                 /*
1754                  * If we're using VLV or SP, the timeout should apply
1755                  * to all calls as an aggregate, so we need to reduce
1756                  * 'tv' with the time spent on this chunk of data.
1757                  */
1758                 if (!done) {
1759                         struct timeval  tmp;
1760 
1761                         (void) gettimeofday(&now, 0);
1762                         tmp = now;
1763                         now.tv_sec -= start.tv_sec;
1764                         now.tv_usec -= start.tv_usec;
1765                         if (now.tv_usec < 0) {
1766                                 now.tv_usec += 1000000;
1767                                 now.tv_sec -= 1;
1768                         }
1769                         tv.tv_sec -= now.tv_sec;
1770                         tv.tv_usec -= now.tv_usec;
1771                         if (tv.tv_usec < 0) {
1772                                 tv.tv_usec += 1000000;
1773                                 tv.tv_sec -= 1;
1774                         }
1775                         if (tv.tv_sec < 0) {
1776                                 *ldapStat = LDAP_TIMEOUT;
1777                                 freeRuleValue(rv, tnv);
1778                                 rv = 0;
1779                                 goto cleanup;
1780                         }
1781                         start = tmp;
1782                 }
1783 
1784         } while (!done);
1785 
1786         if (numValues != 0)
1787                 *numValues = numVals;
1788 
1789 cleanup:
1790         if (NULL != lc) {
1791                 if (yp2ldap && ls->useCon == 0) {
1792                         /* Disconnect and free the connection */
1793                         lc->doDis++;
1794                         lc->doDel++;
1795                         releaseCon(lc, stat);
1796                         releaseLC(lc);
1797 
1798                 } else {
1799                         releaseCon(lc, stat);
1800                 }
1801         }
1802         if (msg != 0)
1803                 (void) ldap_msgfree(msg);
1804         if (ctrls[0] != 0)
1805                 ldap_control_free(ctrls[0]);
1806         if (ctrls[1] != 0)
1807                 ldap_control_free(ctrls[1]);
1808         if (spCookie != 0)
1809                 ber_bvfree(spCookie);
1810         if (sortKeyList != 0)
1811                 ldap_free_sort_keylist(sortKeyList);
1812 
1813         return (rv);
1814 }
1815 
1816 static void
1817 freeLdapModEntry(LDAPMod *m) {
1818 
1819         if (m == 0)
1820                 return;
1821 
1822         sfree(m->mod_type);
1823         if ((m->mod_op & LDAP_MOD_BVALUES) == 0) {
1824                 char    **v = m->mod_values;
1825 
1826                 if (v != 0) {
1827                         while (*v != 0) {
1828                                 sfree(*v);
1829                                 v++;
1830                         }
1831                         free(m->mod_values);
1832                 }
1833         } else {
1834                 struct berval   **b = m->mod_bvalues;
1835 
1836                 if (b != 0) {
1837                         while (*b != 0) {
1838                                 sfree((*b)->bv_val);
1839                                 free(*b);
1840                                 b++;
1841                         }
1842                         free(m->mod_bvalues);
1843                 }
1844         }
1845 
1846         free(m);
1847 }
1848 
1849 static void
1850 freeLdapMod(LDAPMod **mods) {
1851         LDAPMod         *m, **org = mods;
1852 
1853         if (mods == 0)
1854                 return;
1855 
1856         while ((m = *mods) != 0) {
1857                 freeLdapModEntry(m);
1858                 mods++;
1859         }
1860 
1861         free(org);
1862 }
1863 
1864 /*
1865  * Convert a rule-value structure to the corresponding LDAPMod.
1866  * If 'add' is set, attributes/values are added; object classes
1867  * are also added. If 'add' is cleared, attributes/values are modified,
1868  * and 'oc' controls whether or not object classes are added.
1869  */
1870 LDAPMod **
1871 search2LdapMod(__nis_rule_value_t *rv, int add, int oc) {
1872         LDAPMod         **mods;
1873         int             i, j, nm;
1874         char            *myself = "search2LdapMod";
1875 
1876         if (rv == 0 || rv->numAttrs <= 0)
1877                 return (0);
1878 
1879         mods = am(myself, (rv->numAttrs + 1) * sizeof (mods[0]));
1880         if (mods == 0)
1881                 return (0);
1882 
1883         for (i = 0, nm = 0; i < rv->numAttrs; i++) {
1884                 int     isOc;
1885                 /*
1886                  * If we're creating an LDAPMod array for an add operation,
1887                  * just skip attributes that should be deleted.
1888                  */
1889                 if (add && rv->attrVal[i].numVals < 0)
1890                         continue;
1891 
1892                 /*
1893                  * Skip DN; it's specified separately to ldap_modify()
1894                  * and ldap_add(), and mustn't appear among the
1895                  * attributes to be modified/added.
1896                  */
1897                 if (strcasecmp("dn", rv->attrName[i]) == 0)
1898                         continue;
1899 
1900                 /*
1901                  * If modifying, and 'oc' is off, skip object class
1902                  * attributes.
1903                  */
1904                 isOc = (strcasecmp("objectclass", rv->attrName[i]) == 0);
1905                 if (!add && !oc && isOc)
1906                         continue;
1907 
1908                 mods[nm] = am(myself, sizeof (*mods[nm]));
1909                 if (mods[nm] == 0) {
1910                         freeLdapMod(mods);
1911                         return (0);
1912                 }
1913 
1914                 /* 'mod_type' is the attribute name */
1915                 mods[nm]->mod_type = sdup(myself, T, rv->attrName[i]);
1916                 if (mods[nm]->mod_type == 0) {
1917                         freeLdapMod(mods);
1918                         return (0);
1919                 }
1920 
1921                 /*
1922                  * numVals < 0 means attribute and all values should
1923                  * be deleted.
1924                  */
1925                 if (rv->attrVal[i].numVals < 0) {
1926                         mods[nm]->mod_op = LDAP_MOD_DELETE;
1927                         mods[nm]->mod_values = 0;
1928                         nm++;
1929                         continue;
1930                 }
1931 
1932                 /* objectClass attributes always added */
1933                 mods[nm]->mod_op = (add) ? 0 : ((isOc) ? 0 : LDAP_MOD_REPLACE);
1934 
1935                 if (rv->attrVal[i].type == vt_string) {
1936                         /*
1937                          * mods[]->mod_values is a NULL-terminated array
1938                          * of (char *)'s.
1939                          */
1940                         mods[nm]->mod_values = am(myself,
1941                                         (rv->attrVal[i].numVals + 1) *
1942                                         sizeof (mods[nm]->mod_values[0]));
1943                         if (mods[nm]->mod_values == 0) {
1944                                 freeLdapMod(mods);
1945                                 return (0);
1946                         }
1947                         for (j = 0; j < rv->attrVal[i].numVals; j++) {
1948                                 /*
1949                                  * Just in case the string isn't NUL
1950                                  * terminated, add one byte to the
1951                                  * allocated length; am() will initialize
1952                                  * the buffer to zero.
1953                                  */
1954                                 mods[nm]->mod_values[j] = am(myself,
1955                                         rv->attrVal[i].val[j].length + 1);
1956                                 if (mods[nm]->mod_values[j] == 0) {
1957                                         freeLdapMod(mods);
1958                                         return (0);
1959                                 }
1960                                 memcpy(mods[nm]->mod_values[j],
1961                                         rv->attrVal[i].val[j].value,
1962                                         rv->attrVal[i].val[j].length);
1963                         }
1964                 } else {
1965                         mods[nm]->mod_op |= LDAP_MOD_BVALUES;
1966                         mods[nm]->mod_bvalues = am(myself,
1967                                         (rv->attrVal[i].numVals+1) *
1968                                         sizeof (mods[nm]->mod_bvalues[0]));
1969                         if (mods[nm]->mod_bvalues == 0) {
1970                                 freeLdapMod(mods);
1971                                 return (0);
1972                         }
1973                         for (j = 0; j < rv->attrVal[i].numVals; j++) {
1974                                 mods[nm]->mod_bvalues[j] = am(myself,
1975                                         sizeof (*mods[nm]->mod_bvalues[j]));
1976                                 if (mods[nm]->mod_bvalues[j] == 0) {
1977                                         freeLdapMod(mods);
1978                                         return (0);
1979                                 }
1980                                 mods[nm]->mod_bvalues[j]->bv_val = am(myself,
1981                                         rv->attrVal[i].val[j].length);
1982                                 if (mods[nm]->mod_bvalues[j]->bv_val == 0) {
1983                                         freeLdapMod(mods);
1984                                         return (0);
1985                                 }
1986                                 mods[nm]->mod_bvalues[j]->bv_len =
1987                                         rv->attrVal[i].val[j].length;
1988                                 memcpy(mods[nm]->mod_bvalues[j]->bv_val,
1989                                         rv->attrVal[i].val[j].value,
1990                                         mods[nm]->mod_bvalues[j]->bv_len);
1991                         }
1992                 }
1993                 nm++;
1994         }
1995 
1996         return (mods);
1997 }
1998 
1999 /*
2000  * Remove 'value' from 'val'. If value==0, remove the entire
2001  * __nis_single_value_t array from 'val'.
2002  */
2003 static void
2004 removeSingleValue(__nis_value_t *val, void *value, int length) {
2005         int     i;
2006 
2007         if (val == 0)
2008                 return;
2009 
2010         if (value == 0) {
2011                 for (i = 0; i < val->numVals; i++) {
2012                         sfree(val->val[i].value);
2013                 }
2014                 sfree(val->val);
2015                 val->val = 0;
2016                 val->numVals = 0;
2017                 return;
2018         }
2019 
2020         for (i = 0; i < val->numVals; i++) {
2021                 if (val->val[i].value == 0 || (val->val[i].length != length))
2022                         continue;
2023                 if (memcmp(val->val[i].value, value, length) != 0)
2024                         continue;
2025                 sfree(val->val[i].value);
2026                 if (i != (val->numVals - 1)) {
2027                         (void) memmove(&val->val[i], &val->val[i+1],
2028                                 (val->numVals - 1 - i) * sizeof (val->val[0]));
2029                 }
2030                 val->numVals -= 1;
2031                 break;
2032         }
2033 }
2034 
2035 /*
2036  * Helper function for LdapModify
2037  * When a modify operation fails with an object class violation,
2038  * the most probable reason is that the attributes we're modifying are new,
2039  * and the needed object class are not present. So, try the modify again,
2040  * but add the object classes this time.
2041  */
2042 
2043 static int
2044 ldapModifyObjectClass(__nis_ldap_conn_t **lc, char *dn,
2045                 __nis_rule_value_t *rvIn, char *objClassAttrs)
2046 {
2047         LDAPMod                 **mods = 0;
2048         int                     msgid;
2049         int                     lderr;
2050         struct timeval          tv;
2051         int                     stat;
2052         LDAPMessage             *msg = 0;
2053         char                    **referralsp = NULL;
2054         __nis_rule_value_t      *rv, *rvldap;
2055         __nis_ldap_search_t     *ls;
2056         int                     i, ocrv, ocrvldap, nv;
2057         char                    *oc[2] = { "objectClass", 0};
2058         char                    *myself = "ldapModifyObjectClass";
2059 
2060         rv = initRuleValue(1, rvIn);
2061         if (rv == 0)
2062                 return (LDAP_NO_MEMORY);
2063 
2064         delAttrFromRuleValue(rv, "objectClass");
2065         rv = addObjectClasses(rv, objClassAttrs);
2066         if (rv == 0) {
2067                 stat = LDAP_OPERATIONS_ERROR;
2068                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2069                     "%s: addObjectClasses failed for %s",
2070                     myself, NIL(dn));
2071                 goto cleanup;
2072         }
2073 
2074         /*
2075          * Before adding the object classes whole-sale, try retrieving
2076          * the entry specified by the 'dn'. If it exists, we filter out
2077          * those object classes that already are present in LDAP from our
2078          * update.
2079          */
2080         ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0, "objectClass=*",
2081             oc, 0, 1);
2082         if (ls == 0) {
2083                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
2084                     "%s: Unable to build DN search for \"%s\"",
2085                     myself, NIL(dn));
2086                 /* Fall through to try just adding the object classes */
2087                 goto addObjectClasses;
2088         }
2089 
2090         nv = 0;
2091         rvldap = ldapSearch(ls, &nv, 0, &lderr);
2092         freeLdapSearch(ls);
2093         if (rvldap == 0) {
2094                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
2095                     "%s: No data for DN search (\"%s\"); LDAP status %d",
2096                     myself, NIL(dn), lderr);
2097                 /* Fall through to try just adding the object classes */
2098                 goto addObjectClasses;
2099         }
2100 
2101         /*
2102          * Find the indices of the 'objectClass' attribute
2103          * in 'rvldap' and 'rv'.
2104          */
2105         for (i = 0, ocrvldap = -1; i < rvldap->numAttrs; i++) {
2106                 if (rvldap->attrName[i] != 0 &&
2107                     strcasecmp("objectClass", rvldap->attrName[i]) == 0) {
2108                         ocrvldap = i;
2109                         break;
2110                 }
2111         }
2112         for (i = 0, ocrv = -1; i < rv->numAttrs; i++) {
2113                 if (rv->attrName[i] != 0 &&
2114                     strcasecmp("objectClass", rv->attrName[i]) == 0) {
2115                         ocrv = i;
2116                         break;
2117                 }
2118         }
2119 
2120         /*
2121          * Remove those object classes that already exist
2122          * in LDAP (i.e., in 'rvldap') from 'rv'.
2123          */
2124         if (ocrv >= 0 && ocrvldap >= 0) {
2125                 for (i = 0; i < rvldap->attrVal[ocrvldap].numVals; i++) {
2126                         removeSingleValue(&rv->attrVal[ocrv],
2127                             rvldap->attrVal[ocrvldap].val[i].value,
2128                             rvldap->attrVal[ocrvldap].val[i].length);
2129                 }
2130                 /*
2131                  * If no 'objectClass' values left in 'rv', delete
2132                  * 'objectClass' from 'rv'.
2133                  */
2134                 if (rv->attrVal[ocrv].numVals == 0)
2135                         delAttrFromRuleValue(rv, "objectClass");
2136         }
2137 
2138         /*
2139          * 'rv' now contains the update we want to make, with just the
2140          * object class(es) that need to be added. Fall through to the
2141          * actual LDAP modify operation.
2142          */
2143         freeRuleValue(rvldap, 1);
2144 
2145 addObjectClasses:
2146 
2147         mods = search2LdapMod(rv, 0, 1);
2148         if (mods == 0) {
2149                 stat = LDAP_OPERATIONS_ERROR;
2150                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2151         "%s: Unable to create LDAP modify changes with object classes for %s",
2152                     myself, NIL(dn));
2153                 goto cleanup;
2154         }
2155         msgid = ldap_modify((*lc)->ld, dn, mods);
2156         if (msgid != -1) {
2157                 tv = (*lc)->modifyTimeout;
2158                 stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg);
2159                 if (stat == 0) {
2160                         stat = LDAP_TIMEOUT;
2161                 } else if (stat == -1) {
2162                         (void) ldap_get_option((*lc)->ld,
2163                             LDAP_OPT_ERROR_NUMBER, &stat);
2164                 } else {
2165                         stat = ldap_parse_result((*lc)->ld, msg, &lderr, NULL,
2166                             NULL, &referralsp, NULL, 0);
2167                         if (stat == LDAP_SUCCESS)
2168                                 stat = lderr;
2169                         stat = ldap_result2error((*lc)->ld, msg, 0);
2170                 }
2171         } else {
2172                 (void) ldap_get_option((*lc)->ld, LDAP_OPT_ERROR_NUMBER,
2173                     &stat);
2174         }
2175         if (proxyInfo.follow_referral == follow &&
2176             stat == LDAP_REFERRAL && referralsp != NULL) {
2177                 releaseCon(*lc, stat);
2178                 if (msg != NULL)
2179                         (void) ldap_msgfree(msg);
2180                 msg = NULL;
2181                 *lc = findReferralCon(referralsp, &stat);
2182                 ldap_value_free(referralsp);
2183                 referralsp = NULL;
2184                 if (*lc == NULL)
2185                         goto cleanup;
2186                 msgid = ldap_modify((*lc)->ld, dn, mods);
2187                 if (msgid == -1) {
2188                         (void) ldap_get_option((*lc)->ld,
2189                             LDAP_OPT_ERROR_NUMBER, &stat);
2190                         goto cleanup;
2191                 }
2192                 stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg);
2193                 if (stat == 0) {
2194                         stat = LDAP_TIMEOUT;
2195                 } else if (stat == -1) {
2196                         (void) ldap_get_option((*lc)->ld,
2197                             LDAP_OPT_ERROR_NUMBER, &stat);
2198                 } else {
2199                         stat = ldap_parse_result((*lc)->ld, msg, &lderr,
2200                             NULL, NULL, NULL, NULL, 0);
2201                         if (stat == LDAP_SUCCESS)
2202                                 stat = lderr;
2203                 }
2204         }
2205 cleanup:
2206         if (mods != 0)
2207                 freeLdapMod(mods);
2208         freeRuleValue(rv, 1);
2209         return (stat);
2210 }
2211 
2212 /*
2213  * Modify the specified 'dn' per the attribute names/values in 'rv'.
2214  * If 'rv' is NULL, we attempt to delete the entire entry.
2215  *
2216  * The 'objClassAttrs' parameter is needed if the entry must be added
2217  * (i.e., created), or a modify fails with an object class violation.
2218  *
2219  * If 'addFirst' is set, we try an add before a modify; modify before
2220  * add otherwise (ignored if we're deleting).
2221  */
2222 int
2223 ldapModify(char *dn, __nis_rule_value_t *rv, char *objClassAttrs,
2224                 int addFirst) {
2225         int                     stat, add = 0;
2226         LDAPMod                 **mods = 0;
2227         __nis_ldap_conn_t       *lc;
2228         struct timeval          tv;
2229         LDAPMessage             *msg = 0;
2230         char                    *myself = "ldapModify";
2231         int                     msgid;
2232         int                     lderr;
2233         char                    **referralsp = NULL;
2234         bool_t                  delete = FALSE;
2235 
2236         if (dn == 0)
2237                 return (LDAP_PARAM_ERROR);
2238 
2239         if ((lc = findCon(&stat)) == 0)
2240                 return (stat);
2241 
2242         if (rv == 0) {
2243                 delete = TRUE;
2244                 /* Simple case: if rv == 0, try to delete the entire entry */
2245                 msgid = ldap_delete(lc->ld, dn);
2246                 if (msgid == -1) {
2247                         (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2248                                                 &stat);
2249                         goto cleanup;
2250                 }
2251                 tv = lc->deleteTimeout;
2252                 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2253 
2254                 if (stat == 0) {
2255                         stat = LDAP_TIMEOUT;
2256                 } else if (stat == -1) {
2257                         (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2258                                                 &stat);
2259                 } else {
2260                         stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
2261                                 NULL, &referralsp, NULL, 0);
2262                         if (stat == LDAP_SUCCESS)
2263                                 stat = lderr;
2264                 }
2265                 if (proxyInfo.follow_referral == follow &&
2266                                 stat == LDAP_REFERRAL && referralsp != NULL) {
2267                         releaseCon(lc, stat);
2268                         if (msg != NULL)
2269                                 (void) ldap_msgfree(msg);
2270                         msg = NULL;
2271                         lc = findReferralCon(referralsp, &stat);
2272                         ldap_value_free(referralsp);
2273                         if (lc == NULL)
2274                                 goto cleanup;
2275                         msgid = ldap_delete(lc->ld, dn);
2276                         if (msgid == -1) {
2277                                 (void) ldap_get_option(lc->ld,
2278                                         LDAP_OPT_ERROR_NUMBER, &stat);
2279                                 goto cleanup;
2280                         }
2281                         stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2282                         if (stat == 0) {
2283                                 stat = LDAP_TIMEOUT;
2284                         } else if (stat == -1) {
2285                                 (void) ldap_get_option(lc->ld,
2286                                         LDAP_OPT_ERROR_NUMBER, &stat);
2287                         } else {
2288                                 stat = ldap_parse_result(lc->ld, msg, &lderr,
2289                                         NULL, NULL, NULL, NULL, 0);
2290                                 if (stat == LDAP_SUCCESS)
2291                                         stat = lderr;
2292                         }
2293                 }
2294                 /* No such object means someone else has done our job */
2295                 if (stat == LDAP_NO_SUCH_OBJECT)
2296                         stat = LDAP_SUCCESS;
2297         } else {
2298                 if (addFirst) {
2299                         stat = ldapAdd(dn, rv, objClassAttrs, lc);
2300                         lc = NULL;
2301                         if (stat != LDAP_ALREADY_EXISTS)
2302                                 goto cleanup;
2303                         if ((lc = findCon(&stat)) == 0)
2304                                 return (stat);
2305                 }
2306 
2307                 /*
2308                  * First try the modify without specifying object classes
2309                  * (i.e., assume they're already present).
2310                  */
2311                 mods = search2LdapMod(rv, 0, 0);
2312                 if (mods == 0) {
2313                         stat = LDAP_PARAM_ERROR;
2314                         goto cleanup;
2315                 }
2316 
2317                 msgid = ldap_modify(lc->ld, dn, mods);
2318                 if (msgid == -1) {
2319                         (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2320                                                 &stat);
2321                         goto cleanup;
2322                 }
2323                 tv = lc->modifyTimeout;
2324                 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2325                 if (stat == 0) {
2326                         stat = LDAP_TIMEOUT;
2327                 } else if (stat == -1) {
2328                         (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2329                                                 &stat);
2330                 } else {
2331                         stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
2332                                 NULL, &referralsp, NULL, 0);
2333                         if (stat == LDAP_SUCCESS)
2334                                 stat = lderr;
2335                 }
2336                 if (proxyInfo.follow_referral == follow &&
2337                                 stat == LDAP_REFERRAL && referralsp != NULL) {
2338                         releaseCon(lc, stat);
2339                         if (msg != NULL)
2340                                 (void) ldap_msgfree(msg);
2341                         msg = NULL;
2342                         lc = findReferralCon(referralsp, &stat);
2343                         ldap_value_free(referralsp);
2344                         referralsp = NULL;
2345                         if (lc == NULL)
2346                                 goto cleanup;
2347                         msgid = ldap_modify(lc->ld, dn, mods);
2348                         if (msgid == -1) {
2349                                 (void) ldap_get_option(lc->ld,
2350                                         LDAP_OPT_ERROR_NUMBER, &stat);
2351                                 goto cleanup;
2352                         }
2353                         stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2354                         if (stat == 0) {
2355                                 stat = LDAP_TIMEOUT;
2356                         } else if (stat == -1) {
2357                                 (void) ldap_get_option(lc->ld,
2358                                         LDAP_OPT_ERROR_NUMBER, &stat);
2359                         } else {
2360                                 stat = ldap_parse_result(lc->ld, msg, &lderr,
2361                                         NULL, NULL, NULL, NULL, 0);
2362                                 if (stat == LDAP_SUCCESS)
2363                                         stat = lderr;
2364                         }
2365                 }
2366 
2367                 /*
2368                  * If the modify failed with an object class violation,
2369                  * the most probable reason is that at least on of the
2370                  * attributes we're modifying didn't exist before, and
2371                  * neither did its object class. So, try the modify again,
2372                  * but add the object classes this time.
2373                  */
2374                 if (stat == LDAP_OBJECT_CLASS_VIOLATION &&
2375                                 objClassAttrs != 0) {
2376                         freeLdapMod(mods);
2377                         mods = 0;
2378                         stat = ldapModifyObjectClass(&lc, dn, rv,
2379                                 objClassAttrs);
2380                 }
2381 
2382                 if (stat == LDAP_NO_SUCH_ATTRIBUTE) {
2383                         /*
2384                          * If there was at least one attribute delete, then
2385                          * the cause of this error could be that said attribute
2386                          * didn't exist in LDAP. So, do things the slow way,
2387                          * and try to delete one attribute at a time.
2388                          */
2389                         int                     d, numDelete, st;
2390                         __nis_rule_value_t      *rvt;
2391 
2392                         for (d = 0, numDelete = 0; d < rv->numAttrs; d++) {
2393                                 if (rv->attrVal[d].numVals < 0)
2394                                         numDelete++;
2395                         }
2396 
2397                         /* If there's just one, we've already tried */
2398                         if (numDelete <= 1)
2399                                 goto cleanup;
2400 
2401                         /* Make a copy of the rule value */
2402                         rvt = initRuleValue(1, rv);
2403                         if (rvt == 0)
2404                                 goto cleanup;
2405 
2406                         /*
2407                          * Remove all delete attributes from the tmp
2408                          * rule value.
2409                          */
2410                         for (d = 0; d < rv->numAttrs; d++) {
2411                                 if (rv->attrVal[d].numVals < 0) {
2412                                         delAttrFromRuleValue(rvt,
2413                                                 rv->attrName[d]);
2414                                 }
2415                         }
2416 
2417                         /*
2418                          * Now put the attributes back in one by one, and
2419                          * invoke ourselves.
2420                          */
2421                         for (d = 0; d < rv->numAttrs; d++) {
2422                                 if (rv->attrVal[d].numVals >= 0)
2423                                         continue;
2424                                 st = addAttr2RuleValue(rv->attrVal[d].type,
2425                                         rv->attrName[d], 0, 0, rvt);
2426                                 if (st != 0) {
2427                                         logmsg(MSG_NOMEM, LOG_ERR,
2428                                         "%s: Error deleting \"%s\" for \"%s\"",
2429                                                 NIL(rv->attrName[d]), NIL(dn));
2430                                         stat = LDAP_NO_MEMORY;
2431                                         freeRuleValue(rvt, 1);
2432                                         goto cleanup;
2433                                 }
2434                                 stat = ldapModify(dn, rvt, objClassAttrs, 0);
2435                                 if (stat != LDAP_SUCCESS &&
2436                                         stat != LDAP_NO_SUCH_ATTRIBUTE) {
2437                                         freeRuleValue(rvt, 1);
2438                                         goto cleanup;
2439                                 }
2440                                 delAttrFromRuleValue(rvt, rv->attrName[d]);
2441                         }
2442 
2443                         /*
2444                          * If we got here, then all attributes that should
2445                          * be deleted either have been, or didn't exist. For
2446                          * our purposes, the latter is as good as the former.
2447                          */
2448                         stat = LDAP_SUCCESS;
2449                         freeRuleValue(rvt, 1);
2450                 }
2451 
2452                 if (stat == LDAP_NO_SUCH_OBJECT && !addFirst) {
2453                         /*
2454                          * Entry doesn't exist, so try an ldap_add(). If the
2455                          * ldap_add() also fails, that could be because someone
2456                          * else added it between our modify and add operations.
2457                          * If so, we consider that foreign add to be
2458                          * authoritative (meaning we don't retry our modify).
2459                          *
2460                          * Also, if all modify operations specified by 'mods'
2461                          * are deletes, LDAP_NO_SUCH_OBJECT is a kind of
2462                          * success; we certainly don't want to create the
2463                          * entry.
2464                          */
2465                         int     allDelete;
2466                         LDAPMod **m;
2467 
2468                         for (m = mods, allDelete = 1; *m != 0 && allDelete;
2469                                         m++) {
2470                                 if (((*m)->mod_op & LDAP_MOD_DELETE) == 0)
2471                                         allDelete = 0;
2472                         }
2473 
2474                         add = 1;
2475 
2476                         if (allDelete) {
2477                                 stat = LDAP_SUCCESS;
2478                         } else if (objClassAttrs == 0) {
2479                                 /* Now we need it, so this is fatal */
2480                                 stat = LDAP_PARAM_ERROR;
2481                         } else {
2482                                 stat = ldapAdd(dn, rv, objClassAttrs, lc);
2483                                 lc = NULL;
2484                         }
2485                 }
2486         }
2487 
2488 cleanup:
2489         if (stat != LDAP_SUCCESS) {
2490                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
2491                         "%s(0x%x (%s), \"%s\") => %d (%s)\n",
2492                         !delete ? (add ? "ldap_add" : "ldap_modify") :
2493                                 "ldap_delete",
2494                         lc != NULL ? lc->ld : 0,
2495                         lc != NULL ? NIL(lc->sp) : "nil",
2496                         dn, stat, ldap_err2string(stat));
2497         }
2498 
2499         releaseCon(lc, stat);
2500         freeLdapMod(mods);
2501         if (msg != 0)
2502                 (void) ldap_msgfree(msg);
2503 
2504         return (stat);
2505 }
2506 
2507 /*
2508  * Create the entry specified by 'dn' to have the values per 'rv'.
2509  * The 'objClassAttrs' are the extra object classes we need when
2510  * creating an entry.
2511  *
2512  * If 'lc' is non-NULL, we use that connection; otherwise, we find
2513  * our own. CAUTION: This connection will be released on return. Regardless
2514  * of return value, this connection should not subsequently used by the
2515  * caller.
2516  *
2517  * Returns an LDAP status.
2518  */
2519 int
2520 ldapAdd(char *dn, __nis_rule_value_t *rv, char *objClassAttrs, void *lcv) {
2521         int                     stat;
2522         LDAPMod                 **mods = 0;
2523         struct timeval          tv;
2524         LDAPMessage             *msg = 0;
2525         __nis_ldap_conn_t       *lc = lcv;
2526         int                     msgid;
2527         int                     lderr;
2528         char                    **referralsp = NULL;
2529 
2530         if (dn == 0 || rv == 0 || objClassAttrs == 0) {
2531                 releaseCon(lc, LDAP_SUCCESS);
2532                 return (LDAP_PARAM_ERROR);
2533         }
2534 
2535         if (lc == 0) {
2536                 if ((lc = findCon(&stat)) == 0)
2537                         return (stat);
2538         }
2539 
2540         rv = addObjectClasses(rv, objClassAttrs);
2541         if (rv == 0) {
2542                 stat = LDAP_OPERATIONS_ERROR;
2543                 goto cleanup;
2544         }
2545 
2546         mods = search2LdapMod(rv, 1, 0);
2547         if (mods == 0) {
2548                 stat = LDAP_OPERATIONS_ERROR;
2549                 goto cleanup;
2550         }
2551 
2552         msgid = ldap_add(lc->ld, dn, mods);
2553         if (msgid == -1) {
2554                 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
2555                 goto cleanup;
2556         }
2557         tv = lc->addTimeout;
2558         stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2559         if (stat == 0) {
2560                 stat = LDAP_TIMEOUT;
2561         } else if (stat == -1) {
2562                 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
2563         } else {
2564                 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, NULL,
2565                         &referralsp, NULL, 0);
2566                 if (stat == LDAP_SUCCESS)
2567                         stat = lderr;
2568         }
2569         if (proxyInfo.follow_referral == follow && stat == LDAP_REFERRAL &&
2570                         referralsp != NULL) {
2571                 releaseCon(lc, stat);
2572                 if (msg != NULL)
2573                         (void) ldap_msgfree(msg);
2574                 msg = NULL;
2575                 lc = findReferralCon(referralsp, &stat);
2576                 ldap_value_free(referralsp);
2577                 if (lc == NULL)
2578                         goto cleanup;
2579                 msgid = ldap_add(lc->ld, dn, mods);
2580                 if (msgid == -1) {
2581                         (void) ldap_get_option(lc->ld,
2582                                 LDAP_OPT_ERROR_NUMBER, &stat);
2583                         goto cleanup;
2584                 }
2585                 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2586                 if (stat == 0) {
2587                         stat = LDAP_TIMEOUT;
2588                 } else if (stat == -1) {
2589                         (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2590                                                 &stat);
2591                 } else {
2592                         stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
2593                                 NULL, NULL, NULL, 0);
2594                         if (stat == LDAP_SUCCESS)
2595                                 stat = lderr;
2596                 }
2597         }
2598 
2599 cleanup:
2600         if (stat != LDAP_SUCCESS) {
2601                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
2602                         "ldap_add(0x%x (%s), \"%s\") => %d (%s)\n",
2603                         lc != NULL ? lc->ld : 0,
2604                         lc != NULL ? NIL(lc->sp) : "nil",
2605                         dn, stat, ldap_err2string(stat));
2606         }
2607 
2608         releaseCon(lc, stat);
2609         freeLdapMod(mods);
2610         if (msg != 0)
2611                 (void) ldap_msgfree(msg);
2612 
2613         return (stat);
2614 }
2615 
2616 /*
2617  * Change the entry at 'oldDn' to have the new DN (not RDN) 'dn'.
2618  * Returns an LDAP error status.
2619  */
2620 int
2621 ldapChangeDN(char *oldDn, char *dn) {
2622         int                     stat;
2623         __nis_ldap_conn_t       *lc;
2624         int                     i, j, lo, ln;
2625         char                    *rdn;
2626         int                     msgid;
2627         int                     lderr;
2628         struct timeval          tv;
2629         LDAPMessage             *msg = 0;
2630         char                    **referralsp = NULL;
2631         char                    *myself = "ldapChangeDN";
2632 
2633         if ((lo = slen(oldDn)) <= 0 || (ln = slen(dn)) <= 0)
2634                 return (LDAP_PARAM_ERROR);
2635 
2636         if (strcasecmp(oldDn, dn) == 0)
2637                 return (LDAP_SUCCESS);
2638 
2639         if ((lc = findCon(&stat)) == 0)
2640                 return (stat);
2641 
2642         rdn = sdup(myself, T, dn);
2643         if (rdn == 0) {
2644                 releaseCon(lc, LDAP_SUCCESS);
2645                 return (LDAP_NO_MEMORY);
2646         }
2647 
2648         /* Compare old and new DN from the end */
2649         for (i = lo-1, j = ln-1; i >= 0 && j >= 0; i--, j--) {
2650                 if (tolower(oldDn[i]) != tolower(rdn[j])) {
2651                         /*
2652                          * Terminate 'rdn' after this character in order
2653                          * to snip off the portion of the new DN that is
2654                          * the same as the old DN. What remains in 'rdn'
2655                          * is the relative DN.
2656                          */
2657                         rdn[j+1] = '\0';
2658                         break;
2659                 }
2660         }
2661 
2662         stat = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, &msgid);
2663 
2664         if (msgid != -1) {
2665                 tv = lc->modifyTimeout;
2666                 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2667                 if (stat == 0) {
2668                         stat = LDAP_TIMEOUT;
2669                 } else if (stat == -1) {
2670                         (void) ldap_get_option(lc->ld,
2671                                 LDAP_OPT_ERROR_NUMBER, &stat);
2672                 } else {
2673                         stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
2674                                 NULL, &referralsp, NULL, 0);
2675                         if (stat == LDAP_SUCCESS)
2676                                 stat = lderr;
2677                         stat = ldap_result2error(lc->ld, msg, 0);
2678                 }
2679         } else {
2680                 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2681                         &stat);
2682         }
2683         if (proxyInfo.follow_referral == follow &&
2684                         stat == LDAP_REFERRAL && referralsp != NULL) {
2685                 releaseCon(lc, stat);
2686                 if (msg != NULL)
2687                         (void) ldap_msgfree(msg);
2688                 msg = NULL;
2689                 lc = findReferralCon(referralsp, &stat);
2690                 ldap_value_free(referralsp);
2691                 referralsp = NULL;
2692                 if (lc == NULL)
2693                         goto cleanup;
2694                 msgid = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL,
2695                         &msgid);
2696                 if (msgid == -1) {
2697                         (void) ldap_get_option(lc->ld,
2698                                 LDAP_OPT_ERROR_NUMBER, &stat);
2699                         goto cleanup;
2700                 }
2701                 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
2702                 if (stat == 0) {
2703                         stat = LDAP_TIMEOUT;
2704                 } else if (stat == -1) {
2705                         (void) ldap_get_option(lc->ld,
2706                                 LDAP_OPT_ERROR_NUMBER, &stat);
2707                 } else {
2708                         stat = ldap_parse_result(lc->ld, msg, &lderr,
2709                                 NULL, NULL, NULL, NULL, 0);
2710                         if (stat == LDAP_SUCCESS)
2711                                 stat = lderr;
2712                 }
2713         }
2714 
2715 cleanup:
2716         if (msg != NULL)
2717                 (void) ldap_msgfree(msg);
2718 
2719 #if     1
2720         fprintf(stderr, "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s\n",
2721                 myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn),
2722                 ldap_err2string(stat));
2723         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2724                 "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s",
2725                 myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn),
2726                 ldap_err2string(stat));
2727 #endif
2728 
2729         if (stat == LDAP_NO_SUCH_OBJECT) {
2730                 /*
2731                  * Fine from our point of view, since all we want to do
2732                  * is to make sure that an update to the new DN doesn't
2733                  * leave the old entry around.
2734                  */
2735                 stat = LDAP_SUCCESS;
2736         }
2737 
2738         releaseCon(lc, stat);
2739         sfree(rdn);
2740 
2741         return (stat);
2742 }