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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <strings.h>
  27 #include <sys/types.h>
  28 #include <sys/stat.h>
  29 #include <errno.h>
  30 #include <stdio.h>
  31 #include <rpcsvc/nis.h>
  32 #include <rpc/xdr.h>
  33 
  34 #include "ldap_util.h"
  35 #include "ldap_attr.h"
  36 #include "ldap_ruleval.h"
  37 #include "ldap_op.h"
  38 #include "ldap_map.h"
  39 #include "ldap_glob.h"
  40 #include "ldap_xdr.h"
  41 #include "ldap_val.h"
  42 
  43 /* From yptol/dit_access_utils.h */
  44 #define N2LKEY          "rf_key"
  45 #define N2LIPKEY        "rf_ipkey"
  46 
  47 __nis_hash_table_mt     ldapMappingList = NIS_HASH_TABLE_MT_INIT;
  48 extern  int yp2ldap;
  49 
  50 
  51 int
  52 setColumnNames(__nis_table_mapping_t *t) {
  53         int     i, j, nic, noc;
  54         char    **col;
  55         zotypes type;
  56         char    *myself = "setColumnNames";
  57 
  58         if (t == 0)
  59                 return (0);
  60 
  61         type = t->objType;
  62         col = t->column;
  63         nic = (col != 0) ? t->numColumns : -1;
  64 
  65         t->objType = NIS_BOGUS_OBJ;
  66         t->obj = 0;
  67 
  68         /*
  69          * If it's a table object, but there are no translation rules,
  70          * this mapping is for the table object itself. In that case,
  71          * we throw away the column names (if any).
  72          */
  73         if (t->objType == NIS_TABLE_OBJ && t->numRulesFromLDAP == 0 &&
  74                         t->numRulesToLDAP == 0) {
  75                 for (i = 0; i < t->numColumns; i++)
  76                         sfree(t->column[i]);
  77                 sfree(t->column);
  78                 t->column = 0;
  79                 t->numColumns = 0;
  80                 noc = 0;
  81         }
  82 
  83         /*
  84          * Verify that all column names found by the parser
  85          * are present in the actual column list.
  86          */
  87         if (verbose) {
  88                 for (i = 0, noc = 0; i < nic; i++) {
  89                         int     found = 0;
  90 
  91                         if (col[i] == 0)
  92                                 continue;
  93                         /* Skip the 'zo_*' special column names */
  94                         if (isObjAttrString(col[i]))
  95                                 continue;
  96                         for (j = 0; j < t->numColumns; j++) {
  97                                 if (strcmp(col[i], t->column[j]) == 0) {
  98                                         noc++;
  99                                         found = 1;
 100                                         break;
 101                                 }
 102                         }
 103                         if (!found) {
 104                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 105                                         "%s: No column \"%s\" in \"%s\"",
 106                                         myself, NIL(col[i]), NIL(t->objName));
 107                         }
 108                 }
 109         }
 110 
 111         /* Remove any setup by the parser */
 112         for (i = 0; i < nic; i++) {
 113                 sfree(col[i]);
 114         }
 115         sfree(col);
 116 
 117         return (0);
 118 }
 119 
 120 void
 121 freeSingleObjAttr(__nis_obj_attr_t *attr) {
 122         if (attr == 0)
 123                 return;
 124 
 125         sfree(attr->zo_owner);
 126         sfree(attr->zo_group);
 127         sfree(attr->zo_domain);
 128         sfree(attr);
 129 }
 130 
 131 void
 132 freeObjAttr(__nis_obj_attr_t **attr, int numAttr) {
 133         int     i;
 134 
 135         if (attr == 0)
 136                 return;
 137 
 138         for (i = 0; i < numAttr; i++) {
 139                 freeSingleObjAttr(attr[i]);
 140         }
 141 
 142         sfree(attr);
 143 }
 144 
 145 __nis_obj_attr_t *
 146 cloneObjAttr(__nis_obj_attr_t *old) {
 147         __nis_obj_attr_t        *new;
 148         char                    *myself = "cloneObjAttr";
 149 
 150         if (old == 0)
 151                 return (0);
 152 
 153         new = am(myself, sizeof (*new));
 154         if (new == 0)
 155                 return (0);
 156 
 157         new->zo_owner = sdup(myself, T, old->zo_owner);
 158         if (new->zo_owner == 0 && old->zo_owner != 0)
 159                 goto cleanup;
 160 
 161         new->zo_group = sdup(myself, T, old->zo_group);
 162         if (new->zo_group == 0 && old->zo_group != 0)
 163                 goto cleanup;
 164 
 165         new->zo_domain = sdup(myself, T, old->zo_domain);
 166         if (new->zo_domain == 0 && old->zo_domain != 0)
 167                 goto cleanup;
 168 
 169         new->zo_access = old->zo_access;
 170         new->zo_ttl = old->zo_ttl;
 171 
 172         return (new);
 173 
 174 cleanup:
 175         freeSingleObjAttr(new);
 176 
 177         return (0);
 178 }
 179 
 180 
 181 /*
 182  * Obtain NIS+ entries (in the form of db_query's) from the supplied table
 183  * mapping and db_query.
 184  *
 185  * If 'qin' is NULL, enumeration is desired.
 186  *
 187  * On exit, '*numQueries' contains the number of (db_query *)'s in the
 188  * return array, '*ldapStat' the LDAP operation status, and '*objAttr'
 189  * a pointer to an array (of '*numQueries elements) of object attributes
 190  * (zo_owner, etc.). If no object attributes were retrieved, '*objAttr'
 191  * is NULL; any and all of the (*objAttr)[i]'s may be NULL.
 192  */
 193 db_query **
 194 mapFromLDAP(__nis_table_mapping_t *t, db_query *qin, int *numQueries,
 195                 char *dbId, int *ldapStat, __nis_obj_attr_t ***objAttr) {
 196         __nis_table_mapping_t   **tp;
 197         db_query                **q;
 198         __nis_rule_value_t      *rv;
 199         __nis_ldap_search_t     *ls;
 200         int                     n, numVals, numMatches = 0;
 201         int                     stat;
 202         __nis_obj_attr_t        **attr;
 203         char                    *myself = "mapFromLDAP";
 204 
 205         if (ldapStat == 0)
 206                 ldapStat = &stat;
 207 
 208         if (t == 0 || numQueries == 0) {
 209                 *ldapStat = LDAP_PARAM_ERROR;
 210                 return (0);
 211         }
 212 
 213         /* Select the correct table mapping(s) */
 214         tp = selectTableMapping(t, qin, 0, 0, dbId, &numMatches);
 215         if (tp == 0 || numMatches <= 0) {
 216                 /*
 217                  * Not really an error; just no matching mapping
 218                  * for the query.
 219                  */
 220                 *ldapStat = LDAP_SUCCESS;
 221                 return (0);
 222         }
 223 
 224         q = 0;
 225         attr = 0;
 226 
 227         /* For each mapping */
 228         for (numVals = 0, n = 0; n < numMatches; n++) {
 229                 db_query                **qt;
 230                 int                     i, nqt = 0, filterOnQin, res = 0;
 231 
 232                 t = tp[n];
 233 
 234                 if (qin != 0) {
 235                         rv = buildNisPlusRuleValue(t, qin, 0);
 236                         if (rv != 0) {
 237                                 /*
 238                                  * Depending on the value of res, we shall
 239                                  * proceed to next table mapping.
 240                                  */
 241                                 ls = createLdapRequest(t, rv, 0, 1, &res, NULL);
 242                         }
 243                         else
 244                                 ls = 0;
 245                 } else {
 246                         /* Build enumeration request */
 247                         rv = 0;
 248                         ls = createLdapRequest(t, 0, 0, 1, NULL, NULL);
 249                 }
 250 
 251                 freeRuleValue(rv, 1);
 252 
 253                 if (ls == 0) {
 254                         /*
 255                          * if the res is NP_LDAP_RULES_NO_VALUE, that means we
 256                          * have enough NIS+ columns for the rules to produce
 257                          * values, but none of them did, so continue to the
 258                          * next table mapping. Otherwise do cleanup and return
 259                          * error.
 260                          */
 261                         if (res == NP_LDAP_RULES_NO_VALUE)
 262                                 continue;
 263                         for (i = 0; i < numVals; i++)
 264                                 freeQuery(q[i]);
 265                         sfree(q);
 266                         free(tp);
 267                         *ldapStat = LDAP_OPERATIONS_ERROR;
 268                         return (0);
 269                 }
 270 
 271                 /* Query LDAP */
 272                 nqt = (ls->isDN || qin != 0) ? 0 : -1;
 273                 rv = ldapSearch(ls, &nqt, 0, ldapStat);
 274 
 275                 /*
 276                  * If qin != 0, then we need to make sure that the
 277                  * LDAP search is filtered so that only entries that
 278                  * are compatible with 'qin' are retained. This will
 279                  * happen automatically if we do a DN search (in which
 280                  * case, no need to filter on 'qin').
 281                  */
 282                 if (ls->isDN || qin == 0)
 283                         filterOnQin = 0;
 284                 else
 285                         filterOnQin = 1;
 286 
 287                 freeLdapSearch(ls);
 288 
 289                 /* Convert rule-values to db_query's */
 290                 if (rv != 0 && nqt > 0) {
 291                         int                     nrv = nqt;
 292                         __nis_obj_attr_t        **at = 0;
 293 
 294                         qt = ruleValue2Query(t, rv,
 295                                 (filterOnQin) ? qin : 0, &at, &nqt);
 296                         freeRuleValue(rv, nrv);
 297 
 298                         if (qt != 0 && q == 0) {
 299                                 q = qt;
 300                                 attr = at;
 301                                 numVals = nqt;
 302                         } else if (qt != 0) {
 303                                 db_query                **tmp;
 304                                 __nis_obj_attr_t        **atmp;
 305 
 306                                 /* Extend the 'q' array */
 307                                 tmp = realloc(q,
 308                                         (numVals+nqt) * sizeof (q[0]));
 309                                 /* ... and the 'attr' array */
 310                                 atmp = realloc(attr,
 311                                         (numVals+nqt) * sizeof (attr[0]));
 312                                 if (tmp == 0 || atmp == 0) {
 313                                         logmsg(MSG_NOMEM, LOG_ERR,
 314                                                 "%s: realloc(%d) => NULL",
 315                                                 myself,
 316                                                 (numVals+nqt) * sizeof (q[0]));
 317                                         for (i = 0; i < numVals; i++)
 318                                                 freeQuery(q[i]);
 319                                         for (i = 0; i < nqt; i++)
 320                                                 freeQuery(qt[i]);
 321                                         sfree(tmp);
 322                                         sfree(atmp);
 323                                         sfree(q);
 324                                         sfree(qt);
 325                                         sfree(tp);
 326                                         freeObjAttr(at, nqt);
 327                                         freeObjAttr(attr, numVals);
 328                                         *ldapStat = LDAP_NO_MEMORY;
 329                                         return (0);
 330                                 }
 331                                 q = tmp;
 332                                 attr = atmp;
 333                                 /* Add the results for this 't' */
 334                                 (void) memcpy(&q[numVals], qt,
 335                                                 nqt * sizeof (qt[0]));
 336                                 (void) memcpy(&attr[numVals], at,
 337                                                 nqt * sizeof (at[0]));
 338                                 numVals += nqt;
 339 
 340                                 sfree(qt);
 341                                 sfree(at);
 342                         }
 343                 }
 344         }
 345 
 346         *numQueries = numVals;
 347         if (objAttr != 0)
 348                 *objAttr = attr;
 349         else
 350                 freeObjAttr(attr, numVals);
 351         sfree(tp);
 352 
 353         return (q);
 354 }
 355 
 356 /*
 357  * Add the object attributes (zo_owner, etc.) to the rule-value 'rv'.
 358  * Returns a pointer to the (possibly newly allocated) rule-value,
 359  * or NULL in case of failure. If not returning 'rvIn', the latter
 360  * will have been freed.
 361  */
 362 __nis_rule_value_t *
 363 addObjAttr2RuleValue(nis_object *obj, __nis_rule_value_t *rvIn) {
 364         __nis_rule_value_t      *rv;
 365         char                    abuf[2 * sizeof (obj->zo_access) + 1];
 366         char                    tbuf[2 * sizeof (obj->zo_ttl) + 1];
 367 
 368         if (obj == 0)
 369                 return (0);
 370 
 371         if (rvIn != 0) {
 372                 rv = rvIn;
 373         } else {
 374                 rv = initRuleValue(1, 0);
 375                 if (rv == 0)
 376                         return (0);
 377         }
 378 
 379         if (obj->zo_owner != 0) {
 380                 if (addSCol2RuleValue("zo_owner", obj->zo_owner, rv) != 0) {
 381                         freeRuleValue(rv, 1);
 382                         return (0);
 383                 }
 384         }
 385 
 386         if (obj->zo_group != 0) {
 387                 if (addSCol2RuleValue("zo_group", obj->zo_group, rv) != 0) {
 388                         freeRuleValue(rv, 1);
 389                         return (0);
 390                 }
 391         }
 392 
 393         if (obj->zo_domain != 0) {
 394                 if (addSCol2RuleValue("zo_domain", obj->zo_domain, rv) != 0) {
 395                         freeRuleValue(rv, 1);
 396                         return (0);
 397                 }
 398         }
 399 
 400         (void) memset(abuf, 0, sizeof (abuf));
 401         (void) memset(tbuf, 0, sizeof (tbuf));
 402 
 403         sprintf(abuf, "%x", obj->zo_access);
 404         sprintf(tbuf, "%x", obj->zo_ttl);
 405 
 406         if (addSCol2RuleValue("zo_access", abuf, rv) != 0) {
 407                 freeRuleValue(rv, 1);
 408                 return (0);
 409         }
 410         if (addSCol2RuleValue("zo_ttl", tbuf, rv) != 0) {
 411                 freeRuleValue(rv, 1);
 412                 return (0);
 413         }
 414 
 415         return (rv);
 416 }
 417 
 418 /*
 419  * Returns a pointer to (NOT a copy of) the value for the specified
 420  * column 'col' in the rule-value 'rv'.
 421  */
 422 __nis_value_t *
 423 findColValue(char *col, __nis_rule_value_t *rv) {
 424         int             i;
 425 
 426         if (col == 0 || rv == 0 || rv->numColumns <= 0)
 427                 return (0);
 428 
 429         for (i = 0; i < rv->numColumns; i++) {
 430                 if (strcmp(col, rv->colName[i]) == 0)
 431                         return (&rv->colVal[i]);
 432         }
 433 
 434         return (0);
 435 }
 436 
 437 /*
 438  * Return the NIS+ object attributes (if any) in the rule-value 'rv'.
 439  */
 440 __nis_obj_attr_t *
 441 ruleValue2ObjAttr(__nis_rule_value_t *rv) {
 442         __nis_obj_attr_t        *attr;
 443         __nis_value_t           *val;
 444         char                    *myself = "ruleValue2ObjAttr";
 445 
 446         if (rv == 0 || rv->numColumns <= 0)
 447                 return (0);
 448 
 449         attr = am(myself, sizeof (*attr));
 450 
 451         if ((val = findColValue("zo_owner", rv)) != 0 &&
 452                         val->type == vt_string && val->numVals == 1 &&
 453                         val->val[0].value != 0) {
 454                 attr->zo_owner = sdup(myself, T, val->val[0].value);
 455                 if (attr->zo_owner == 0) {
 456                         freeSingleObjAttr(attr);
 457                         return (0);
 458                 }
 459         }
 460 
 461         if ((val = findColValue("zo_group", rv)) != 0 &&
 462                         val->type == vt_string && val->numVals == 1 &&
 463                         val->val[0].value != 0) {
 464                 attr->zo_group = sdup(myself, T, val->val[0].value);
 465                 if (attr->zo_group == 0) {
 466                         freeSingleObjAttr(attr);
 467                         return (0);
 468                 }
 469         }
 470 
 471         if ((val = findColValue("zo_domain", rv)) != 0 &&
 472                         val->type == vt_string && val->numVals == 1 &&
 473                         val->val[0].value != 0) {
 474                 attr->zo_domain = sdup(myself, T, val->val[0].value);
 475                 if (attr->zo_domain == 0) {
 476                         freeSingleObjAttr(attr);
 477                         return (0);
 478                 }
 479         }
 480 
 481         if ((val = findColValue("zo_access", rv)) != 0 &&
 482                         val->type == vt_string && val->numVals == 1 &&
 483                         val->val[0].value != 0) {
 484                 if (sscanf(val->val[0].value, "%x", &attr->zo_access) != 1) {
 485                         freeSingleObjAttr(attr);
 486                         return (0);
 487                 }
 488         }
 489 
 490         if ((val = findColValue("zo_ttl", rv)) != 0 &&
 491                         val->type == vt_string && val->numVals == 1 &&
 492                         val->val[0].value != 0) {
 493                 if (sscanf(val->val[0].value, "%x", &attr->zo_ttl) != 1) {
 494                         freeSingleObjAttr(attr);
 495                         return (0);
 496                 }
 497         }
 498 
 499         return (attr);
 500 }
 501 
 502 /*
 503  * If the supplied string is one of the object attributes, return one.
 504  * Otherwise, return zero.
 505  */
 506 int
 507 isObjAttrString(char *str) {
 508         if (str == 0)
 509                 return (0);
 510 
 511         if (strcmp("zo_owner", str) == 0 ||
 512                 strcmp("zo_group", str) == 0 ||
 513                 strcmp("zo_domain", str) == 0 ||
 514                 strcmp("zo_access", str) == 0 ||
 515                 strcmp("zo_ttl", str) == 0)
 516                 return (1);
 517         else
 518                 return (0);
 519 }
 520 
 521 
 522 /*
 523  * If the supplied value is one of the object attribute strings, return
 524  * a pointer to the string. Otherwise, return NULL.
 525  */
 526 char *
 527 isObjAttr(__nis_single_value_t *val) {
 528         if (val == 0 || val->length <= 0 || val->value == 0)
 529                 return (0);
 530 
 531         if (isObjAttrString(val->value))
 532                 return (val->value);
 533         else
 534                 return (0);
 535 }
 536 
 537 int
 538 setObjAttrField(char *attrName, __nis_single_value_t *val,
 539                 __nis_obj_attr_t **objAttr) {
 540         __nis_obj_attr_t        *attr;
 541         char                    *myself = "setObjAttrField";
 542 
 543         if (attrName == 0 || val == 0 || objAttr == 0 ||
 544                         val->value == 0 || val->length <= 0)
 545                 return (-1);
 546 
 547         if (*objAttr != 0) {
 548                 attr = *objAttr;
 549         } else {
 550                 attr = am(myself, sizeof (*attr));
 551                 if (attr == 0)
 552                         return (-2);
 553                 *objAttr = attr;
 554         }
 555 
 556         if (strcmp("zo_owner", attrName) == 0) {
 557                 if (attr->zo_owner == 0) {
 558                         attr->zo_owner = sdup(myself, T, val->value);
 559                         if (attr->zo_owner == 0)
 560                                 return (-11);
 561                 }
 562         } else if (strcmp("zo_group", attrName) == 0) {
 563                 if (attr->zo_group == 0) {
 564                         attr->zo_group = sdup(myself, T, val->value);
 565                         if (attr->zo_group == 0)
 566                                 return (-12);
 567                 }
 568         } else if (strcmp("zo_domain", attrName) == 0) {
 569                 if (attr->zo_domain == 0) {
 570                         attr->zo_domain = sdup(myself, T, val->value);
 571                         if (attr->zo_domain == 0)
 572                                 return (-13);
 573                 }
 574         } else if (strcmp("zo_access", attrName) == 0) {
 575                 if (attr->zo_access == 0) {
 576                         if (sscanf(val->value, "%x", &attr->zo_access) != 1)
 577                                 return (-14);
 578                 }
 579         } else if (strcmp("zo_ttl", attrName) == 0) {
 580                 if (attr->zo_ttl == 0) {
 581                         if (sscanf(val->value, "%x", &attr->zo_ttl) != 1)
 582                                 return (-15);
 583                 }
 584         }
 585 
 586         return (0);
 587 }
 588 
 589 /*
 590  * Return a DN and rule-value for the supplied mapping, db_query's, and
 591  * input rule-value. This function only works on a single mapping. See
 592  * mapToLDAP() below for a description of the action depending on the
 593  * values of 'old' and 'new'.
 594  *
 595  * If both 'old' and 'new' are supplied, and the modify would result
 596  * in a change to the DN, '*oldDN' will contain the old DN. Otherwise
 597  * (and normally), '*oldDN' will be NULL.
 598  */
 599 char *
 600 map1qToLDAP(__nis_table_mapping_t *t, db_query *old, db_query *new,
 601                 __nis_rule_value_t *rvIn, __nis_rule_value_t **rvOutP,
 602                 char **oldDnP) {
 603 
 604         __nis_rule_value_t      *rv, *rvt;
 605         __nis_ldap_search_t     *ls;
 606         char                    *dn = 0, *oldDn = 0;
 607         __nis_table_mapping_t   del;
 608         char                    *myself = "map1qToLDAP";
 609 
 610         if (t == 0 || (old == 0 && new == 0) || rvOutP == 0)
 611                 return (0);
 612 
 613         /*
 614          * If entry should be deleted, we look at the delete
 615          * policy in the table mapping. Should it specify a
 616          * rule set, we use that rule set to build a rule-
 617          * value, and the delete actually becomes a modify
 618          * operation.
 619          */
 620         if (old != 0 && new == 0) {
 621                 if (t->objectDN->delDisp == dd_perDbId) {
 622                         /*
 623                          * The functions that build a rule-value from a
 624                          * rule set expect a __nis_table_mapping_t, but the
 625                          * rule set in the __nis_object_dn_t isn't of that
 626                          * form. So, build a pseudo-__nis_table_mapping_t that
 627                          * borrows heavily from 't'.
 628                          */
 629                         del = *t;
 630 
 631                         del.numRulesToLDAP = del.objectDN->numDbIds;
 632                         del.ruleToLDAP = del.objectDN->dbId;
 633 
 634                         /*
 635                          * Do a modify with the pseudo-table
 636                          * mapping, and the 'old' db_query
 637                          * supplying input to the delete rule
 638                          * set.
 639                          */
 640                         t = &del;
 641                         new = old;
 642                 } else if (t->objectDN->delDisp == dd_always) {
 643 
 644                         /* Nothing to do here; all handled below */
 645 
 646                 } else if (t->objectDN->delDisp == dd_never) {
 647 
 648                         return (0);
 649 
 650                 } else {
 651 
 652                         logmsg(MSG_INVALIDDELDISP, LOG_WARNING,
 653                                 "%s: Invalid delete disposition %d for \"%s\"",
 654                                 myself, t->objectDN->delDisp,
 655                                 NIL(t->dbId));
 656                         return (0);
 657 
 658                 }
 659         }
 660 
 661         /* Make a copy of the input rule-value */
 662         if (rvIn != 0) {
 663                 rv = initRuleValue(1, rvIn);
 664                 if (rv == 0)
 665                         return (0);
 666         } else {
 667                 rv = 0;
 668         }
 669 
 670         /* First get a rule-value from the supplied NIS+ entry. */
 671         rvt = rv;
 672         rv = buildNisPlusRuleValue(t, ((old != 0) ? old : new), rvt);
 673         freeRuleValue(rvt, 1);
 674         if (rv == 0) {
 675                 logmsg(MSG_NORULEVALUE, LOG_WARNING,
 676                         "%s: No in-query rule-value derived for \"%s\"",
 677                         myself, NIL(t->dbId));
 678                 return (0);
 679         }
 680 
 681         /*
 682          * Create a request (really only care about the DN) from the
 683          * supplied NIS+ entry data.
 684          */
 685         ls = createLdapRequest(t, rv, &dn, 0, NULL, NULL);
 686         if (ls == 0 || dn == 0) {
 687                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 688                         "%s: Unable to create LDAP request for %s: %s",
 689                         myself, NIL(t->dbId),
 690                         (dn != 0) ? dn : rvId(rv, mit_nisplus));
 691                 sfree(dn);
 692                 freeLdapSearch(ls);
 693                 freeRuleValue(rv, 1);
 694                 return (0);
 695         }
 696 
 697         freeLdapSearch(ls);
 698 
 699         if (new != 0) {
 700                 /*
 701                  * Create a rule-value from the new NIS+ entry.
 702                  * Don't want to mix in the rule-value derived
 703                  * from 'old', so delete it. However, we still
 704                  * want the owner, group, etc., from 'rvIn'.
 705                  */
 706                 if (old != 0) {
 707                         freeRuleValue(rv, 1);
 708                         if (rvIn != 0) {
 709                                 rv = initRuleValue(1, rvIn);
 710                                 if (rv == 0) {
 711                                         sfree(dn);
 712                                         return (0);
 713                                 }
 714                         } else {
 715                                 rv = 0;
 716                         }
 717                 }
 718                 rvt = rv;
 719                 rv = buildNisPlusRuleValue(t, new, rvt);
 720                 freeRuleValue(rvt, 1);
 721                 if (rv == 0) {
 722                         logmsg(MSG_NORULEVALUE, LOG_WARNING,
 723                                 "%s: No new rule-value derived for \"%s: %s\"",
 724                                 myself, NIL(t->dbId), dn);
 725                         sfree(dn);
 726                         return (0);
 727                 }
 728                 /*
 729                  * Check if the proposed modification would result in a
 730                  * a change to the DN.
 731                  */
 732                 if (old != 0) {
 733                         oldDn = dn;
 734                         dn = 0;
 735                         ls = createLdapRequest(t, rv, &dn, 0, NULL, NULL);
 736                         if (ls == 0 || dn == 0) {
 737                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 738                                 "%s: Unable to create new DN for \"%s: %s\"",
 739                                         myself, NIL(t->dbId), oldDn);
 740                                 sfree(oldDn);
 741                                 freeLdapSearch(ls);
 742                                 freeRuleValue(rv, 1);
 743                                 return (0);
 744                         }
 745                         freeLdapSearch(ls);
 746                         if (strcasecmp(oldDn, dn) == 0) {
 747                                 sfree(oldDn);
 748                                 oldDn = 0;
 749                         }
 750                 }
 751         }
 752 
 753 
 754         *rvOutP = rv;
 755         if (oldDnP != 0)
 756                 *oldDnP = oldDn;
 757 
 758         return (dn);
 759 }
 760 
 761 /*
 762  * Since the DN hash list is an automatic variable, there's no need for
 763  * locking, and we remove the locking overhead by using the libnsl
 764  * hash functions.
 765  */
 766 #undef  NIS_HASH_ITEM
 767 #undef  NIS_HASH_TABLE
 768 
 769 typedef struct {
 770         NIS_HASH_ITEM   item;
 771         int             index;
 772         char            *oldDn;
 773 } __dn_item_t;
 774 
 775 /*
 776  * Update LDAP per the supplied table mapping and db_query's.
 777  *
 778  * 'nq' is the number of elements in the 'old', 'new', and 'rvIn'
 779  * arrays. mapToLDAP() generally performs one update for each
 780  * element; however, if one or more of the individual queries
 781  * produce the same DN, they're merged into a single update.
 782  *
 783  * There are four cases, depending on the values of 'old[iq]' and
 784  * 'new[iq]':
 785  *
 786  * (1)  old[iq] == 0 && new[iq] == 0
 787  *      No action; skip to next query
 788  *
 789  * (2)  old[iq] == 0 && new[iq] != 0
 790  *      Attempt to use the 'new' db_query to get a DN, and try to create
 791  *      the corresponding LDAP entry.
 792  *
 793  * (3)  old[iq] != 0 && new[iq] == 0
 794  *      Use the 'old' db_query to get a DN, and try to delete the LDAP
 795  *      entry per the table mapping.
 796  *
 797  * (4)  old[iq] != 0 && new[iq] != 0
 798  *      Use the 'old' db_query to get a DN, and update (possibly create)
 799  *      the corresponding LDAP entry per the 'new' db_query.
 800  *
 801  * If 'rvIn' is non-NULL, it is expected to contain the object attributes
 802  * (zo_owner, etc.) to be written to LDAP. 'rvIn' is an array with 'nq'
 803  * elements.
 804  *
 805  * If 'firstOnly' is set, only the first old[iq]/new[iq] pair is used
 806  * to perform the actual update. Any additional queries specified will
 807  * have their values folded in, but are not used to derive update targets.
 808  * This mode is inteded to support the case where multiple NIS+ entries
 809  * map to one and the same LDAP entry. Note that 'rvIn' must still be
 810  * an array of 'nq' elements, though if 'firstOnly' is set, it should be
 811  * OK to leave all but 'rvIn[0]' empty.
 812  *
 813  * 'dbId' is used to further narow down the selection of mapping candidates
 814  * to those matching the 'dbId' value.
 815  */
 816 int
 817 mapToLDAP(__nis_table_mapping_t *tm, int nq, db_query **old, db_query **new,
 818                 __nis_rule_value_t *rvIn, int firstOnly, char *dbId) {
 819         __nis_table_mapping_t   **tp, **tpa;
 820         int                     i, n, rnq, iq, r, ret = LDAP_SUCCESS;
 821         int                     maxMatches, numMatches = 0;
 822         __nis_ldap_search_t     *ls;
 823         char                    **dn = 0, **odn = 0;
 824         __nis_rule_value_t      **rv;
 825         __dn_item_t             *dni;
 826         char                    *myself = "mapToLDAP";
 827 
 828 
 829         if (tm == 0 || (old == 0 && new == 0) || nq <= 0)
 830                 return (LDAP_PARAM_ERROR);
 831 
 832         /* Determine maximum number of table mapping matches */
 833         if (nq == 1) {
 834                 tp = selectTableMapping(tm,
 835                         (old != 0 && old[0] != 0) ? old[0] : new[0], 1, 0,
 836                                 dbId, &maxMatches);
 837                 numMatches = maxMatches;
 838         } else {
 839                 tp = selectTableMapping(tm, 0, 1, 0, dbId, &maxMatches);
 840         }
 841 
 842         /*
 843          * If no matching mapping, we're not mapping to LDAP in this
 844          * particular case.
 845          */
 846         if (tp == 0 || maxMatches == 0) {
 847                 sfree(tp);
 848                 return (LDAP_SUCCESS);
 849         }
 850 
 851         /*
 852          * Allocate the 'rv', 'dn', and 'tpa' arrays. Worst case is that
 853          * we need nq * maxMatches elements in each array. However, if
 854          * 'firstOnly' is set, we only need one element per matching
 855          * mapping in each.
 856          */
 857         dn = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (dn[0]));
 858         odn = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (odn[0]));
 859         rv = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (rv[0]));
 860         tpa = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (tpa[0]));
 861         if (dn == 0 || odn == 0 || rv == 0 || tpa == 0) {
 862                 sfree(tp);
 863                 sfree(dn);
 864                 sfree(odn);
 865                 sfree(rv);
 866                 sfree(tpa);
 867                 return (LDAP_NO_MEMORY);
 868         }
 869 
 870         /* Unless nq == 1, we don't need the 'tp' value */
 871         if (nq != 1)
 872                 sfree(tp);
 873 
 874         logmsg(MSG_NOTIMECHECK,
 875 #ifdef  NISDB_LDAP_DEBUG
 876                 LOG_WARNING,
 877 #else
 878                 LOG_INFO,
 879 #endif  /* NISDB_LDAP_DEBUG */
 880                 "%s: %s: %d * %d potential updates",
 881                 myself, NIL(tm->objName), nq, maxMatches);
 882 
 883         /*
 884          * Create DNs, column and attribute values, and merge duplicate DNs.
 885          */
 886         for (iq = 0, rnq = 0; iq < nq; iq++) {
 887                 int     idx;
 888 
 889                 if ((old == 0 || old[iq] == 0) &&
 890                                 (new == 0 || new[iq] == 0))
 891                         continue;
 892 
 893                 /*
 894                  * Select matching table mappings; if nq == 1, we've already
 895                  * got the 'tp' array from above. We expect this to be the
 896                  * most common case, so it's worth special treatment.
 897                  */
 898                 if (nq != 1)
 899                         tp = selectTableMapping(tm,
 900                         (old != 0 && old[iq] != 0) ? old[iq] : new[iq], 1, 0,
 901                                         dbId, &numMatches);
 902                 if (tp == 0)
 903                         continue;
 904                 else if (numMatches <= 0) {
 905                         sfree(tp);
 906                         continue;
 907                 }
 908 
 909                 idx = iq * maxMatches;
 910 
 911                 if (idx == 0 || !firstOnly)
 912                         (void) memcpy(&tpa[idx], tp,
 913                                         numMatches * sizeof (tpa[idx]));
 914 
 915                 for (n = 0; n < numMatches; n++) {
 916                         char                    *dnt, *odnt;
 917                         __nis_rule_value_t      *rvt = 0;
 918 
 919                         if (tp[n] == 0)
 920                                 continue;
 921 
 922                         dnt = map1qToLDAP(tp[n],
 923                                         (old != 0) ? old[iq] : 0,
 924                                         (new != 0) ? new[iq] : 0,
 925                                         (rvIn != 0) ? &rvIn[iq] : 0,
 926                                         &rvt, &odnt);
 927 
 928                         if (dnt == 0)
 929                                 continue;
 930                         if (rvt == 0) {
 931 #ifdef  NISDB_LDAP_DEBUG
 932                                 abort();
 933 #else
 934                                 sfree(dnt);
 935                                 sfree(odnt);
 936                                 continue;
 937 #endif  /* NISDB_LDAP_DEBUG */
 938                         }
 939 
 940                         /*
 941                          * Create a request to get a rule-value with
 942                          * NIS+ data translated to LDAP equivalents.
 943                          */
 944                         ls = createLdapRequest(tp[n], rvt, 0, 0, NULL, NULL);
 945                         if (ls == 0) {
 946                                 if (ret == LDAP_SUCCESS)
 947                                         ret = LDAP_OPERATIONS_ERROR;
 948                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 949                                 "%s: Unable to map to LDAP attrs for %s:dn=%s",
 950                                 myself, NIL(tp[n]->dbId), dnt);
 951                                 sfree(dnt);
 952                                 freeRuleValue(rvt, 1);
 953                                 continue;
 954                         }
 955                         freeLdapSearch(ls);
 956 
 957                         /*
 958                          * If the DN is the same as one we already know
 959                          * about, merge the rule-values.
 960                          */
 961 
 962                         if ((iq == 0 || !firstOnly) && dnt != 0) {
 963                                 dni = am(myself, sizeof (*dni));
 964                                 if (dni != 0) {
 965                                         dni->item.name = dnt;
 966                                         dni->index = idx + n;
 967                                         dni->oldDn = odnt;
 968                                 } else {
 969                                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
 970                                         "%s: Skipping update for dn=\"%s\"",
 971                                                 myself, dnt);
 972                                         sfree(dnt);
 973                                         dnt = 0;
 974                                 }
 975                                 if (dnt != 0) {
 976                                         dn[idx+n] = dnt;
 977                                         odn[idx+n] = odnt;
 978                                         rv[idx+n] = rvt;
 979                                         rnq++;
 980                                 } else {
 981                                         freeRuleValue(rvt, 1);
 982                                         rvt = 0;
 983                                 }
 984                         } else if (dnt != 0) {
 985                                 sfree(dnt);
 986                                 sfree(odnt);
 987                                 freeRuleValue(rvt, 1);
 988                         }
 989                 }
 990                 sfree(tp);
 991         }
 992 
 993         logmsg(MSG_NOTIMECHECK,
 994 #ifdef  NISDB_LDAP_DEBUG
 995                 LOG_WARNING,
 996 #else
 997                 LOG_INFO,
 998 #endif  /* NISDB_LDAP_DEBUG */
 999                 "%s: %s: %d update%s requested",
1000                 myself, NIL(tm->objName), rnq, rnq != 1 ? "s" : "");
1001 
1002         /* Perform the updates */
1003         for (i = rnq = 0; i < (firstOnly ? maxMatches : nq*maxMatches); i++) {
1004                 int     delPerDbId;
1005 
1006                 if (dn[i] == 0)
1007                         continue;
1008 
1009 #ifdef  NISDB_LDAP_DEBUG
1010                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1011                         "%s: %s %s:dn=%s",
1012                         myself,
1013                         (new != 0 && new[i/maxMatches] != 0) ?
1014                                 "modify" : "delete",
1015                         NIL(tpa[i]->dbId), dn[i]);
1016 #endif  /* NISDB_LDAP_DEBUG */
1017 
1018                 delPerDbId = (tpa[i]->objectDN->delDisp == dd_perDbId);
1019                 if ((new != 0 && new[i/maxMatches] != 0) || delPerDbId) {
1020                         /*
1021                          * Try to modify/create the specified DN. First,
1022                          * however, if the update changes the DN, make
1023                          * that change.
1024                          */
1025                         if (odn[i] == 0 || (r = ldapChangeDN(odn[i], dn[i])) ==
1026                                         LDAP_SUCCESS) {
1027                                 int     addFirst;
1028 
1029                                 addFirst = (new != 0 &&
1030                                                 new[i/maxMatches] != 0 &&
1031                                                 !delPerDbId);
1032                                 r = ldapModify(dn[i], rv[i],
1033                                         tpa[i]->objectDN->write.attrs,
1034                                                 addFirst);
1035                         }
1036                 } else {
1037                         /* Try to delete the specified DN */
1038                         r = ldapModify(dn[i], 0,
1039                                         tpa[i]->objectDN->write.attrs, 0);
1040                 }
1041 
1042                 if (r == LDAP_SUCCESS) {
1043                         rnq++;
1044                 } else {
1045                         if (ret == LDAP_SUCCESS)
1046                                 ret = r;
1047                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
1048                                 "%s: LDAP %s request error %d for %s:dn=%s",
1049                                 myself,
1050                                 (new != 0 && new[i/maxMatches] != 0) ?
1051                                         "modify" : "delete",
1052                                 r, NIL(tpa[i]->dbId), dn[i]);
1053                 }
1054 
1055                 sfree(dn[i]);
1056                 dn[i] = 0;
1057                 freeRuleValue(rv[i], 1);
1058                 rv[i] = 0;
1059         }
1060 
1061         sfree(dn);
1062         sfree(odn);
1063         sfree(rv);
1064         sfree(tpa);
1065 
1066         logmsg(MSG_NOTIMECHECK,
1067 #ifdef  NISDB_LDAP_DEBUG
1068                 LOG_WARNING,
1069 #else
1070                 LOG_INFO,
1071 #endif  /* NISDB_LDAP_DEBUG */
1072                 "%s: %s: %d update%s performed",
1073                 myself, NIL(tm->objName), rnq, rnq != 1 ? "s" : "");
1074 
1075         return (ret);
1076 }
1077 
1078 /*
1079  * In nis+2ldap, check if the query 'q' matches the selector index 'x->index'.
1080  *
1081  * In nis2ldap, if 'name' is provided then check if its value in 'val'
1082  * matches the selector index. If 'name' is NULL, then check if rule-value 'rv'
1083  * matches the index.
1084  * To match the selector index, all fieldspecs in the indexlist should match
1085  * (AND). In nis2ldap, an exception is, if there are multiple fieldspecs with
1086  * the same fieldname then only one of them needs to match (OR).
1087  * Example:
1088  *      Indexlist = [host="H*", host="I*", user="U*", domain="D*"]
1089  * Then,
1090  *      host = "H1", user="U1", domain="D1" ==> pass
1091  *      host = "I1", user="U1", domain="D1" ==> pass
1092  *      host = "X1", user="U1", domain="D1" ==> fail
1093  *      host = "H1", user="X1", domain="D1" ==> fail
1094  *      host = "H1", user="U1" ==> fail
1095  *
1096  * Return 1 in case of a match, 0 otherwise.
1097  */
1098 int
1099 verifyIndexMatch(__nis_table_mapping_t *x, db_query *q,
1100                 __nis_rule_value_t *rv, char *name, char *val) {
1101         int     i, j, k, match = 1;
1102         char    *myself = "verifyIndexMatch";
1103 
1104         /*
1105          * The pass and fail arrays are used by N2L to keep track of
1106          * index matches. This saves us from having matches in a
1107          * nested loop to decide OR or AND.
1108          */
1109         int     ppos, fpos;
1110         char    **pass, **fail;
1111 
1112         if (x == 0)
1113                 return (0);
1114 
1115         /* Trivial match */
1116         if (x->index.numIndexes <= 0 || (!yp2ldap && q == 0))
1117                 return (1);
1118 
1119         if (yp2ldap) {
1120                 if (!(pass = am(myself, x->index.numIndexes * sizeof (char *))))
1121                         return (0);
1122                 if (!(fail = am(myself,
1123                                 x->index.numIndexes * sizeof (char *)))) {
1124                         sfree(pass);
1125                         return (0);
1126                 }
1127                 ppos = fpos = 0;
1128         }
1129 
1130         /* Check each index */
1131         for (i = 0; i < x->index.numIndexes; i++) {
1132                 int     len = 0;
1133                 char    *value = 0;
1134 
1135                 /* Skip NULL index names */
1136                 if (x->index.name[i] == 0)
1137                         continue;
1138 
1139                 /* Check N2L values */
1140                 if (yp2ldap) {
1141                         if (name) {
1142                                 if (strcasecmp(x->index.name[i], name) == 0)
1143                                         value = val;
1144                                 else
1145                                         continue;
1146                         } else if (rv) {
1147                                 if (strcasecmp(x->index.name[i], N2LKEY) == 0 ||
1148                                         strcasecmp(x->index.name[i], N2LIPKEY)
1149                                                         == 0)
1150                                         continue;
1151                                 value = findVal(x->index.name[i], rv,
1152                                                         mit_nisplus);
1153                         }
1154 
1155                         if (value && verifyMappingMatch(x->index.value[i],
1156                                                                         value))
1157                                 pass[ppos++] = x->index.name[i];
1158                         else
1159                                 fail[fpos++] = x->index.name[i];
1160                         continue;
1161                 }
1162 
1163                 /* If here, means nis+2ldap */
1164 
1165                 /* Is the index name a known column ? */
1166                 for (j = 0; j < x->numColumns; j++) {
1167                         if (strcmp(x->index.name[i], x->column[j]) == 0) {
1168                                 /*
1169                                  * Do we have a value for the column ?
1170                                  */
1171                                 for (k = 0; k < q->components.components_len;
1172                                                 k++) {
1173                                         if (q->components.components_val[k].
1174                                                         which_index == j) {
1175                                                 value = q->components.
1176                                                         components_val[k].
1177                                                         index_value->
1178                                                         itemvalue.
1179                                                         itemvalue_val;
1180                                                 len = q->components.
1181                                                         components_val[k].
1182                                                         index_value->
1183                                                         itemvalue.
1184                                                         itemvalue_len;
1185                                                 break;
1186                                         }
1187                                 }
1188                                 if (value != 0)
1189                                         break;
1190                         }
1191                 }
1192 
1193                 /*
1194                  * If we found a value, check if it matches the
1195                  * format. If no value found or no match, this
1196                  * mapping is _not_ an alternative. Otherwise,
1197                  * we continue checking any other indexes.
1198                  */
1199                 if (value == 0 ||
1200                         !verifyMappingMatch(x->index.value[i],
1201                                 value)) {
1202                         match = 0;
1203                         break;
1204                 }
1205         }
1206 
1207         if (yp2ldap) {
1208                 for (--fpos; fpos >= 0; fpos--) {
1209                         for (i = 0; i < ppos; i++) {
1210                                 if (strcmp(pass[i], fail[fpos]) == 0)
1211                                         break;
1212                         }
1213                         if (i == ppos) {
1214                                 match = 0;
1215                                 break;
1216                         }
1217                 }
1218                 sfree(pass);
1219                 sfree(fail);
1220         }
1221 
1222         return (match);
1223 }
1224 
1225 /*
1226  * Return all table mappings that match the column values in 'q'.
1227  * If there's no match, return those alternative mappings that don't
1228  * have an index; if no such mapping exists, return NULL.
1229  *
1230  * If 'wantWrite' is set, we want mappings for writing (i.e., data
1231  * to LDAP); otherwise, we want mappings for reading.
1232  *
1233  * If 'wantObj' is set, we want object mappings only (i.e., _not_
1234  * those used to map entries in tables).
1235  *
1236  * If 'dbId' is non-NULL, we select mappings with a matching dbId field.
1237  */
1238 __nis_table_mapping_t **
1239 selectTableMapping(__nis_table_mapping_t *t, db_query *q,
1240                         int wantWrite, int wantObj, char *dbId,
1241                         int *numMatches) {
1242         __nis_table_mapping_t   *r, *x, **tp;
1243         int                     i, j, k, nm, numap;
1244         char                    *myself = "selectTableMapping";
1245 
1246         if (numMatches == 0)
1247                 numMatches = &nm;
1248 
1249         /*
1250          * Count the number of possible mappings, so that we can
1251          * allocate the 'tp' array up front.
1252          */
1253         for (numap = 0, x = t; x != 0; numap++, x = x->next);
1254 
1255         if (numap == 0) {
1256                 *numMatches = 0;
1257                 return (0);
1258         }
1259 
1260         tp = am(myself, numap * sizeof (tp[0]));
1261         if (tp == 0) {
1262                 *numMatches = -1;
1263                 return (0);
1264         }
1265 
1266         /*
1267          * Special cases:
1268          *
1269          *      q == 0 trivially matches any 't' of the correct object type
1270          *
1271          *      wantObj != 0 means we ignore 'q'
1272          */
1273         if (q == 0 || wantObj) {
1274                 for (i = 0, x = t, nm = 0; i < numap; i++, x = x->next) {
1275                         if (x->objectDN == 0)
1276                                 continue;
1277                         if (wantWrite) {
1278                                 if (x->objectDN->write.scope ==
1279                                                 LDAP_SCOPE_UNKNOWN)
1280                                         continue;
1281                         } else {
1282                                 if (x->objectDN->read.scope ==
1283                                                 LDAP_SCOPE_UNKNOWN)
1284                                         continue;
1285                         }
1286                         if (wantObj) {
1287                                 if (x->numColumns > 0)
1288                                         continue;
1289                         } else {
1290                                 if (x->numColumns <= 0)
1291                                         continue;
1292                         }
1293                         if (dbId != 0 && x->dbId != 0 &&
1294                                         strcmp(dbId, x->dbId) != 0)
1295                                 continue;
1296                         tp[nm] = x;
1297                         nm++;
1298                 }
1299                 *numMatches = nm;
1300                 if (nm == 0) {
1301                         sfree(tp);
1302                         tp = 0;
1303                 }
1304                 return (tp);
1305         }
1306 
1307         /* Scan all mappings, and collect candidates */
1308         for (nm = 0, r = 0, x = t; x != 0; x = x->next) {
1309                 if (x->objectDN == 0)
1310                         continue;
1311                 if (wantWrite) {
1312                         if (x->objectDN->write.scope == LDAP_SCOPE_UNKNOWN)
1313                                 continue;
1314                 } else {
1315                         if (x->objectDN->read.scope == LDAP_SCOPE_UNKNOWN)
1316                                 continue;
1317                 }
1318                 /* Only want table/entry mappings */
1319                 if (x->numColumns <= 0)
1320                         continue;
1321                 if (dbId != 0 && x->dbId != 0 &&
1322                                 strcmp(dbId, x->dbId) != 0)
1323                         continue;
1324                 /*
1325                  * It's a match if: there are no indexes, or we actually
1326                  * match the query with the indexes.
1327                  */
1328                 if (x->index.numIndexes <= 0 ||
1329                                         verifyIndexMatch(x, q, 0, 0, 0)) {
1330                         tp[nm] = x;
1331                         nm++;
1332                 }
1333         }
1334 
1335         if (nm == 0) {
1336                 free(tp);
1337                 tp = 0;
1338         }
1339 
1340         *numMatches = nm;
1341 
1342         return (tp);
1343 }
1344 
1345 /*
1346  * Return 1 if there's an indexed mapping, 0 otherwise.
1347  */
1348 int
1349 haveIndexedMapping(__nis_table_mapping_t *t) {
1350         __nis_table_mapping_t   *x;
1351 
1352         for (x = t; x != 0; x = x->next) {
1353                 if (x->index.numIndexes > 0)
1354                         return (1);
1355         }
1356 
1357         return (0);
1358 }
1359 
1360 /*
1361  * Given an input string 'attrs' of the form "attr1=val1,attr2=val2,...",
1362  * or a filter, return the value associated with the attribute 'attrName'.
1363  * If no instance of 'attrName' is found, return 'default'. In all cases,
1364  * the return value is a copy, and must be freed by the caller.
1365  *
1366  * Of course, return NULL in case of failure.
1367  */
1368 static char *
1369 attrVal(char *msg, char *attrName, char *def, char *attrs) {
1370         char    *val, *filter, **fc = 0;
1371         int     i, nfc;
1372         char    *myself = "attrVal";
1373 
1374         if (attrName == 0 || attrs == 0)
1375                 return (0);
1376 
1377         if (msg == 0)
1378                 msg = myself;
1379 
1380         val = def;
1381 
1382         filter = makeFilter(attrs);
1383         if (filter != 0 && (fc = makeFilterComp(filter, &nfc)) != 0 &&
1384                         nfc > 0) {
1385                 for (i = 0; i < nfc; i++) {
1386                         char    *name, *value;
1387 
1388                         name = fc[i];
1389                         /* Skip if not of attr=value form */
1390                         if ((value = strchr(name, '=')) == 0)
1391                                 continue;
1392 
1393                         *value = '\0';
1394                         value++;
1395 
1396                         if (strcasecmp(attrName, name) == 0) {
1397                                 val = value;
1398                                 break;
1399                         }
1400                 }
1401         }
1402 
1403         if (val != 0)
1404                 val = sdup(msg, T, val);
1405 
1406         sfree(filter);
1407         freeFilterComp(fc, nfc);
1408 
1409         return (val);
1410 }
1411 
1412 extern bool_t   xdr_nis_object(register XDR *xdrs, nis_object *objp);
1413 
1414 /*
1415  * Copy an XDR:ed version of the NIS+ object 'o' (or the one indicated
1416  * by 't->objName' if 'o' is NULL) to the place indicated by
1417  * 't->objectDN->write'. Return an appropriate LDAP status code.
1418  */
1419 int
1420 objToLDAP(__nis_table_mapping_t *t, nis_object *o, entry_obj **ea, int numEa) {
1421         __nis_table_mapping_t   **tp;
1422         XDR                     xdr;
1423         char                    *objName;
1424         int                     stat, osize, n, numMatches = 0;
1425         void                    *buf;
1426         __nis_rule_value_t      *rv;
1427         __nis_value_t           *val;
1428         __nis_single_value_t    *sv;
1429         char                    **attrName, *dn;
1430         char                    *myself = "objToLDAP";
1431 
1432         if (t == 0)
1433                 return (LDAP_PARAM_ERROR);
1434 
1435         logmsg(MSG_NOTIMECHECK,
1436 #ifdef  NISDB_LDAP_DEBUG
1437                 LOG_WARNING,
1438 #else
1439                 LOG_INFO,
1440 #endif  /* NISDB_LDAP_DEBUG */
1441                 "%s: %s", myself, NIL(t->objName));
1442 
1443         tp = selectTableMapping(t, 0, 1, 1, 0, &numMatches);
1444         if (tp == 0 || numMatches <= 0) {
1445                 sfree(tp);
1446                 logmsg(MSG_NOTIMECHECK,
1447 #ifdef  NISDB_LDAP_DEBUG
1448                         LOG_WARNING,
1449 #else
1450                         LOG_INFO,
1451 #endif  /* NISDB_LDAP_DEBUG */
1452                         "%s: %s (no mapping)", myself, NIL(t->objName));
1453                 return (LDAP_SUCCESS);
1454         }
1455 
1456         for (n = 0; n < numMatches; n++) {
1457 
1458                 t = tp[n];
1459 
1460                 if (o == 0) {
1461                         sfree(tp);
1462                         return (LDAP_OPERATIONS_ERROR);
1463                 }
1464 
1465                 buf = (char *)xdrNisObject(o, ea, numEa, &osize);
1466                 if (buf == 0) {
1467                         sfree(tp);
1468                         return (LDAP_OPERATIONS_ERROR);
1469                 }
1470 
1471                 /*
1472                  * Prepare to build a rule-value containing the XDR:ed
1473                  * object
1474                  */
1475                 rv = am(myself, sizeof (*rv));
1476                 sv = am(myself, sizeof (*sv));
1477                 val = am(myself, sizeof (*val));
1478                 attrName = am(myself, sizeof (attrName[0]));
1479                 if (attrName != 0)
1480                         attrName[0] = attrVal(myself, "nisplusObject",
1481                                                 "nisplusObject",
1482                                                 t->objectDN->write.attrs);
1483                 if (rv == 0 || sv == 0 || val == 0 || attrName == 0 ||
1484                                 attrName[0] == 0) {
1485                         sfree(tp);
1486                         sfree(buf);
1487                         sfree(rv);
1488                         sfree(sv);
1489                         sfree(val);
1490                         sfree(attrName);
1491                         return (LDAP_NO_MEMORY);
1492                 }
1493 
1494                 sv->length = osize;
1495                 sv->value = buf;
1496 
1497                 /* 'vt_ber' just means "not a NUL-terminated string" */
1498                 val->type = vt_ber;
1499                 val->repeat = 0;
1500                 val->numVals = 1;
1501                 val->val = sv;
1502 
1503                 rv->numAttrs = 1;
1504                 rv->attrName = attrName;
1505                 rv->attrVal = val;
1506 
1507                 /*
1508                  * The 'write.base' is the actual DN of the entry (and the
1509                  * scope had better be 'base', but we don't check that).
1510                  */
1511                 dn = t->objectDN->write.base;
1512 
1513                 stat = ldapModify(dn, rv, t->objectDN->write.attrs, 1);
1514 
1515                 freeRuleValue(rv, 1);
1516 
1517         logmsg(MSG_NOTIMECHECK,
1518 #ifdef  NISDB_LDAP_DEBUG
1519                 LOG_WARNING,
1520 #else
1521                 LOG_INFO,
1522 #endif  /* NISDB_LDAP_DEBUG */
1523                 "%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat));
1524 
1525                 if (stat != LDAP_SUCCESS)
1526                         break;
1527 
1528         }
1529 
1530         sfree(tp);
1531 
1532         return (stat);
1533 }
1534 
1535 /*
1536  * Retrieve a copy of the 't->objName' object from LDAP, where it's
1537  * stored in XDR:ed form in the place indicated by 't->objectDN->read'.
1538  * Un-XDR the object, and return a pointer to it in '*obj'; it's the
1539  * responsibility of the caller to free the object when it's no
1540  * longer needed.
1541  *
1542  * Returns an appropriate LDAP status.
1543  */
1544 int
1545 objFromLDAP(__nis_table_mapping_t *t, nis_object **obj,
1546                 entry_obj ***eaP, int *numEaP) {
1547         __nis_table_mapping_t   **tp;
1548         XDR                     xdr;
1549         nis_object              *o;
1550         __nis_rule_value_t      *rv;
1551         __nis_ldap_search_t     *ls;
1552         char                    *attrs[2], *filter, **fc = 0;
1553         void                    *buf;
1554         int                     i, j, nfc, nrv, blen, stat = LDAP_SUCCESS;
1555         int                     n, numMatches;
1556         char                    *myself = "objFromLDAP";
1557 
1558         if (t == 0)
1559                 return (LDAP_PARAM_ERROR);
1560 
1561         /*
1562          * If there's nowhere to store the result, we might as
1563          * well pretend all went well, and return right away.
1564          */
1565         if (obj == 0)
1566                 return (LDAP_SUCCESS);
1567 
1568         /* Prepare for the worst */
1569         *obj = 0;
1570 
1571         logmsg(MSG_NOTIMECHECK,
1572 #ifdef  NISDB_LDAP_DEBUG
1573                 LOG_WARNING,
1574 #else
1575                 LOG_INFO,
1576 #endif  /* NISDB_LDAP_DEBUG */
1577                 "%s: %s", myself, NIL(t->objName));
1578 
1579         tp = selectTableMapping(t, 0, 0, 1, 0, &numMatches);
1580         if (tp == 0 || numMatches <= 0) {
1581                 sfree(tp);
1582                 logmsg(MSG_NOTIMECHECK,
1583 #ifdef  NISDB_LDAP_DEBUG
1584                         LOG_WARNING,
1585 #else
1586                         LOG_INFO,
1587 #endif  /* NISDB_LDAP_DEBUG */
1588                         "%s: %s (no mapping)", myself, NIL(t->objName));
1589                 return (LDAP_SUCCESS);
1590         }
1591 
1592         for (n = 0; n < numMatches; n++) {
1593 
1594                 t = tp[n];
1595 
1596                 filter = makeFilter(t->objectDN->read.attrs);
1597                 if (filter == 0 || (fc = makeFilterComp(filter, &nfc)) == 0 ||
1598                                 nfc <= 0) {
1599                         sfree(tp);
1600                         sfree(filter);
1601                         freeFilterComp(fc, nfc);
1602                         return ((t->objectDN->read.attrs != 0) ?
1603                                 LDAP_NO_MEMORY : LDAP_PARAM_ERROR);
1604                 }
1605                 /* Don't need the filter, just the components */
1606                 sfree(filter);
1607 
1608                 /*
1609                  * Look for a "nisplusObject" attribute, and (if found) copy
1610                  * the value to attrs[0]. Also remove the "nisplusObject"
1611                  * attribute and value from the filter components.
1612                  */
1613                 attrs[0] = sdup(myself, T, "nisplusObject");
1614                 if (attrs[0] == 0) {
1615                         sfree(tp);
1616                         freeFilterComp(fc, nfc);
1617                         return (LDAP_NO_MEMORY);
1618                 }
1619                 attrs[1] = 0;
1620                 for (i = 0; i < nfc; i++) {
1621                         char    *name, *value;
1622                         int     compare;
1623 
1624                         name = fc[i];
1625                         /* Skip if not of attr=value form */
1626                         if ((value = strchr(name, '=')) == 0)
1627                                 continue;
1628 
1629                         /* Temporarily overWrite the '=' with a '\0' */
1630                         *value = '\0';
1631 
1632                         /* Compare with our target attribute name */
1633                         compare = strcasecmp("nisplusObject", name);
1634 
1635                         /* Put back the '=' */
1636                         *value = '=';
1637 
1638                         /* Is it the name we're looking for ? */
1639                         if (compare == 0) {
1640                                 sfree(attrs[0]);
1641                                 attrs[0] = sdup(myself, T, value+1);
1642                                 if (attrs[0] == 0) {
1643                                         sfree(tp);
1644                                         freeFilterComp(fc, nfc);
1645                                         return (LDAP_NO_MEMORY);
1646                                 }
1647                                 sfree(fc[i]);
1648                                 if (i < nfc-1)
1649                                         (void) memmove(&fc[i], &fc[i+1],
1650                                                 (nfc-1-i) * sizeof (fc[i]));
1651                                 nfc--;
1652                                 break;
1653                         }
1654                 }
1655 
1656                 ls = buildLdapSearch(t->objectDN->read.base,
1657                                         t->objectDN->read.scope,
1658                                         nfc, fc, 0, attrs, 0, 1);
1659                 sfree(attrs[0]);
1660                 freeFilterComp(fc, nfc);
1661                 if (ls == 0) {
1662                         sfree(tp);
1663                         return (LDAP_OPERATIONS_ERROR);
1664                 }
1665 
1666                 nrv = 0;
1667                 rv = ldapSearch(ls, &nrv, 0, &stat);
1668                 if (rv == 0) {
1669                         sfree(tp);
1670                         freeLdapSearch(ls);
1671                         return (stat);
1672                 }
1673 
1674                 for (i = 0, buf = 0; i < nrv && buf == 0; i++) {
1675                         for (j = 0; j < rv[i].numAttrs; j++) {
1676                                 if (strcasecmp(ls->attrs[0],
1677                                         rv[i].attrName[j]) == 0) {
1678                                         if (rv[i].attrVal[j].numVals <= 0)
1679                                                 continue;
1680                                         buf = rv[i].attrVal[j].val[0].value;
1681                                         blen = rv[i].attrVal[j].val[0].length;
1682                                         break;
1683                                 }
1684                         }
1685                 }
1686 
1687                 if (buf != 0) {
1688                         o = unXdrNisObject(buf, blen, eaP, numEaP);
1689                         if (o == 0) {
1690                                 sfree(tp);
1691                                 freeLdapSearch(ls);
1692                                 freeRuleValue(rv, nrv);
1693                                 return (LDAP_OPERATIONS_ERROR);
1694                         }
1695                         stat = LDAP_SUCCESS;
1696                         *obj = o;
1697                 } else {
1698                         stat = LDAP_NO_SUCH_OBJECT;
1699                 }
1700 
1701                 freeLdapSearch(ls);
1702                 freeRuleValue(rv, nrv);
1703 
1704         logmsg(MSG_NOTIMECHECK,
1705 #ifdef  NISDB_LDAP_DEBUG
1706                 LOG_WARNING,
1707 #else
1708                 LOG_INFO,
1709 #endif  /* NISDB_LDAP_DEBUG */
1710                 "%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat));
1711 
1712                 if (stat != LDAP_SUCCESS)
1713                         break;
1714 
1715         }
1716 
1717         sfree(tp);
1718 
1719         return (stat);
1720 }
1721 
1722 int
1723 deleteLDAPobj(__nis_table_mapping_t *t) {
1724         __nis_table_mapping_t   **tp;
1725         int             n, stat, numMatches = 0;
1726         char            *myself = "deleteLDAPobj";
1727 
1728         if (t == 0)
1729                 return (LDAP_PARAM_ERROR);
1730 
1731         logmsg(MSG_NOTIMECHECK,
1732 #ifdef  NISDB_LDAP_DEBUG
1733                 LOG_WARNING,
1734 #else
1735                 LOG_INFO,
1736 #endif  /* NISDB_LDAP_DEBUG */
1737                 "%s: %s", myself, NIL(t->objName));
1738 
1739         tp = selectTableMapping(t, 0, 1, 1, 0, &numMatches);
1740         if (tp == 0 || numMatches <= 0) {
1741                 sfree(tp);
1742                 logmsg(MSG_NOTIMECHECK,
1743 #ifdef  NISDB_LDAP_DEBUG
1744                         LOG_WARNING,
1745 #else
1746                         LOG_INFO,
1747 #endif  /* NISDB_LDAP_DEBUG */
1748                         "%s: %s (no mapping)", myself, NIL(t->objName));
1749                 return (LDAP_SUCCESS);
1750         }
1751 
1752         for (n = 0; n < numMatches; n++) {
1753 
1754                 t = tp[n];
1755 
1756                 if (t->objectDN->delDisp == dd_always) {
1757                         /* Delete entire entry */
1758                         stat = ldapModify(t->objectDN->write.base, 0,
1759                                         t->objectDN->write.attrs, 1);
1760                 } else if (t->objectDN->delDisp == dd_perDbId) {
1761                         /*
1762                          * Delete the attribute holding the object.
1763                          * First, determine what that attribute is called.
1764                          */
1765                         char                    *attrName =
1766                                                 attrVal(myself,
1767                                                         "nisplusObject",
1768                                                         "nisplusObject",
1769                                                 t->objectDN->write.attrs);
1770                         __nis_rule_value_t      rv;
1771                         __nis_value_t           val;
1772 
1773                         if (attrName == 0) {
1774                                 sfree(tp);
1775                                 return (LDAP_NO_MEMORY);
1776                         }
1777 
1778                         /*
1779                          * Build a __nis_value_t with 'numVals' < 0 to
1780                          * indicate deletion.
1781                          */
1782                         val.type = vt_ber;
1783                         val.numVals = -1;
1784                         val.val = 0;
1785 
1786                         /*
1787                          * Build a rule-value with the name we determined
1788                          * above, and the deletion value.
1789                          */
1790                         (void) memset(&rv, 0, sizeof (rv));
1791                         rv.numAttrs = 1;
1792                         rv.attrName = &attrName;
1793                         rv.attrVal = &val;
1794 
1795                         stat = ldapModify(t->objectDN->write.base, &rv,
1796                                                 t->objectDN->write.attrs, 0);
1797 
1798                         sfree(attrName);
1799                 } else if (t->objectDN->delDisp == dd_never) {
1800                         /* Nothing to do, so we're trivially successful */
1801                         stat = LDAP_SUCCESS;
1802                 } else {
1803                         stat = LDAP_PARAM_ERROR;
1804                 }
1805 
1806         logmsg(MSG_NOTIMECHECK,
1807 #ifdef  NISDB_LDAP_DEBUG
1808                 LOG_WARNING,
1809 #else
1810                 LOG_INFO,
1811 #endif  /* NISDB_LDAP_DEBUG */
1812                 "%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat));
1813 
1814                 /* If there were no such object, we've trivially succeeded */
1815                 if (stat == LDAP_NO_SUCH_OBJECT)
1816                         stat = LDAP_SUCCESS;
1817 
1818                 if (stat != LDAP_SUCCESS)
1819                         break;
1820 
1821         }
1822 
1823         sfree(tp);
1824 
1825         return (stat);
1826 }