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