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 
  28 #include <lber.h>
  29 #include <ldap.h>
  30 #include <strings.h>
  31 
  32 #include "nisdb_mt.h"
  33 
  34 #include "ldap_util.h"
  35 #include "ldap_val.h"
  36 #include "ldap_attr.h"
  37 #include "ldap_ldap.h"
  38 #include "ldap_ruleval.h"
  39 
  40 
  41 /*
  42  * Free an array of 'count' rule-value elements.
  43  */
  44 void
  45 freeRuleValue(__nis_rule_value_t *rv, int count) {
  46         int     n, i, j;
  47 
  48         if (rv == 0)
  49                 return;
  50 
  51         for (n = 0; n < count; n++) {
  52 
  53                 if (rv[n].colName != 0) {
  54                         for (i = 0; i < rv[n].numColumns; i++) {
  55                                 sfree(rv[n].colName[i]);
  56                         }
  57                         free(rv[n].colName);
  58                 }
  59                 if (rv[n].colVal != 0) {
  60                         for (i = 0; i < rv[n].numColumns; i++) {
  61                                 for (j = 0; j < rv[n].colVal[i].numVals; j++) {
  62                                         sfree(rv[n].colVal[i].val[j].value);
  63                                 }
  64                                 if (rv[n].colVal[i].numVals > 0)
  65                                         sfree(rv[n].colVal[i].val);
  66                         }
  67                         free(rv[n].colVal);
  68                 }
  69 
  70                 if (rv[n].attrName != 0) {
  71                         for (i = 0; i < rv[n].numAttrs; i++) {
  72                                 sfree(rv[n].attrName[i]);
  73                         }
  74                         free(rv[n].attrName);
  75                 }
  76                 if (rv[n].attrVal != 0) {
  77                         for (i = 0; i < rv[n].numAttrs; i++) {
  78                                 for (j = 0; j < rv[n].attrVal[i].numVals;
  79                                                 j++) {
  80                                         sfree(rv[n].attrVal[i].val[j].value);
  81                                 }
  82                                 if (rv[n].attrVal[i].numVals > 0)
  83                                         sfree(rv[n].attrVal[i].val);
  84                         }
  85                         free(rv[n].attrVal);
  86                 }
  87 
  88         }
  89         sfree(rv);
  90 }
  91 
  92 /*
  93  * Return an array of 'count' __nis_rule_value_t elements, initialized
  94  * to be copies of 'rvIn' if supplied; empty otherwise.
  95  */
  96 __nis_rule_value_t *
  97 initRuleValue(int count, __nis_rule_value_t *rvIn) {
  98         return (growRuleValue(0, count, 0, rvIn));
  99 }
 100 
 101 static const __nis_rule_value_t rvZero = {0};
 102 
 103 /*
 104  * Grow 'old' from 'oldCount' to 'newCount' elements, initialize the
 105  * new portion to 'rvIn' (empty if not supplied), and return a pointer
 106  * to the result. Following a call to this function, the caller must
 107  * refer only to the returned array, not to 'old'.
 108  */
 109 __nis_rule_value_t *
 110 growRuleValue(int oldCount, int newCount, __nis_rule_value_t *old,
 111                 __nis_rule_value_t *rvIn) {
 112         __nis_rule_value_t      *rv;
 113         int                     i;
 114         char                    *myself = "growRuleValue";
 115 
 116         if (newCount <= 0 || newCount <= oldCount)
 117                 return (old);
 118 
 119         if (oldCount <= 0) {
 120                 oldCount = 0;
 121                 old = 0;
 122         }
 123 
 124         if (rvIn == 0)
 125                 rvIn = (__nis_rule_value_t *)&rvZero;
 126 
 127         rv = realloc(old, newCount * sizeof (rv[0]));
 128         if (rv == 0) {
 129                 logmsg(MSG_NOMEM, LOG_ERR,
 130                         "%s: realloc(%d ((%d+%d)*%d)) => 0",
 131                         myself, (oldCount+newCount) * sizeof (rv[0]),
 132                         oldCount, newCount, sizeof (rv[0]));
 133                 freeRuleValue(old, oldCount);
 134                 return (0);
 135         }
 136 
 137         (void) memset(&rv[oldCount], 0, (newCount-oldCount)*sizeof (rv[0]));
 138 
 139         for (i = oldCount; i < newCount; i++) {
 140                 rv[i].numColumns = rvIn->numColumns;
 141                 if (rv[i].numColumns > 0) {
 142                         rv[i].colName = cloneName(rvIn->colName,
 143                                         rv[i].numColumns);
 144                         rv[i].colVal = cloneValue(rvIn->colVal,
 145                                         rv[i].numColumns);
 146                 }
 147                 if (rv[i].numColumns > 0 &&
 148                                 (rv[i].colName == 0 || rv[i].colVal == 0)) {
 149                         freeRuleValue(rv, i);
 150                         return (0);
 151                 }
 152                 rv[i].numAttrs = rvIn->numAttrs;
 153                 rv[i].attrName = cloneName(rvIn->attrName, rv[i].numAttrs);
 154                 rv[i].attrVal = cloneValue(rvIn->attrVal, rv[i].numAttrs);
 155                 if (rv[i].numAttrs > 0 &&
 156                         (rv[i].attrName == 0 || rv[i].attrVal == 0)) {
 157                         freeRuleValue(rv, i);
 158                         return (0);
 159                 }
 160         }
 161 
 162         return (rv);
 163 }
 164 
 165 /*
 166  * Merge the source rule-value 's' into the target rule-value 't'.
 167  * If successful, unless 's' is a sub-set of 't', 't' will be changed
 168  * on exit, and will contain the values from 's' as well.
 169  */
 170 int
 171 mergeRuleValue(__nis_rule_value_t *t, __nis_rule_value_t *s) {
 172         int     i, j;
 173 
 174         if (s == 0)
 175                 return (0);
 176         else if (t == 0)
 177                 return (-1);
 178 
 179         for (i = 0; i < s->numColumns; i++) {
 180                 for (j = 0; j < s->colVal[i].numVals; j++) {
 181                         if (addCol2RuleValue(s->colVal[i].type, s->colName[i],
 182                                         s->colVal[i].val[j].value,
 183                                         s->colVal[i].val[j].length,
 184                                         t))
 185                                 return (-1);
 186                 }
 187         }
 188 
 189         for (i = 0; i < s->numAttrs; i++) {
 190                 for (j = 0; j < s->attrVal[i].numVals; j++) {
 191                         if (addAttr2RuleValue(s->attrVal[i].type,
 192                                         s->attrName[i],
 193                                         s->attrVal[i].val[j].value,
 194                                         s->attrVal[i].val[j].length,
 195                                         t))
 196                                 return (-1);
 197                 }
 198         }
 199 
 200         return (0);
 201 }
 202 
 203 static int
 204 addVal2RuleValue(char *msg, int caseSens, int snipNul, __nis_value_type_t type,
 205                 char *name, void *value, int valueLen,
 206                 int *numP, char ***inNameP, __nis_value_t **inValP) {
 207         int                     i, j, copyLen = valueLen;
 208         __nis_single_value_t    *v;
 209         char                    **inName = *inNameP;
 210         __nis_value_t           *inVal = *inValP;
 211         int                     num = *numP;
 212         int                     (*comp)(const char *s1, const char *s2);
 213         char                    *myself = "addVal2RuleValue";
 214 
 215         /* Internal function, so assume arguments OK */
 216 
 217         if (msg == 0)
 218                 msg = myself;
 219 
 220         /* Should we match the 'inName' value case sensitive or not ? */
 221         if (caseSens)
 222                 comp = strcmp;
 223         else
 224                 comp = strcasecmp;
 225 
 226         /*
 227          * String-valued NIS+ entries count the concluding NUL in the
 228          * length, while LDAP entries don't. In order to support this,
 229          * we implement the following for vt_string value types:
 230          *
 231          * If the last byte of the value isn't a NUL, add one to the
 232          * allocated length, so that there always is a NUL after the
 233          * value, making it safe to pass to strcmp() etc.
 234          *
 235          * If 'snipNul' is set (presumably meaning we're inserting a
 236          * value derived from a NIS+ entry), and the last byte of the
 237          * value already is a NUL, decrement the length to be copied by
 238          * one. This (a) doesn't count the NUL in the value length, but
 239          * (b) still leaves a NUL following the value.
 240          *
 241          * In N2L, for all cases we set 'copyLen' to the number of non-0
 242          * characters in 'value'.
 243          */
 244         if (type == vt_string && valueLen > 0) {
 245                 char    *charval = value;
 246 
 247                 if (charval[valueLen-1] != '\0')
 248                         valueLen += 1;
 249                 else if (yp2ldap || snipNul)
 250                         copyLen -= 1;
 251         } else if (valueLen == 0) {
 252                 /*
 253                  * If the 'value' pointer is non-NULL, we create a zero-
 254                  * length value with one byte allocated. This takes care
 255                  * of empty strings.
 256                  */
 257                 valueLen += 1;
 258         }
 259 
 260         /* If we already have values for this attribute, add another one */
 261         for (i = 0; i < num; i++) {
 262                 if ((*comp)(inName[i], name) == 0) {
 263 
 264                         /*
 265                          * Our caller often doesn't know the type of the
 266                          * value; this happens because the type (vt_string
 267                          * or vt_ber) is determined by the format in the
 268                          * rule sets, and we may be invoked as a preparation
 269                          * for evaluating the rules. Hence, we only use the
 270                          * supplied 'type' if we need to create a value.
 271                          * Otherwise, we accept mixed types.
 272                          *
 273                          * Strings are OK in any case, since we always make
 274                          * sure to have a zero byte at the end of any value,
 275                          * whatever the type.
 276                          */
 277 
 278                         if (inVal[i].numVals < 0) {
 279                                 /*
 280                                  * Used to indicate deletion of attribute,
 281                                  * so we honor that and don't add a value.
 282                                  */
 283                                 return (0);
 284                         }
 285 
 286                         /*
 287                          * If 'value' is NULL, we should delete, so
 288                          * remove any existing values, and set the
 289                          * 'numVals' field to -1.
 290                          */
 291                         if (value == 0) {
 292                                 for (j = 0; j < inVal[i].numVals; j++) {
 293                                         sfree(inVal[i].val[j].value);
 294                                 }
 295                                 sfree(inVal[i].val);
 296                                 inVal[i].val = 0;
 297                                 inVal[i].numVals = -1;
 298                                 return (0);
 299                         }
 300 
 301                         /* Is the value a duplicate ? */
 302                         for (j = 0; j < inVal[i].numVals; j++) {
 303                                 if (copyLen == inVal[i].val[j].length &&
 304                                         memcmp(value, inVal[i].val[j].value,
 305                                                 copyLen) == 0) {
 306                                         break;
 307                                 }
 308                         }
 309                         if (j < inVal[i].numVals)
 310                                 return (0);
 311 
 312                         /* Not a duplicate, so add the name/value pair */
 313                         v = realloc(inVal[i].val,
 314                                         (inVal[i].numVals+1) *
 315                                         sizeof (inVal[i].val[0]));
 316                         if (v == 0)
 317                                 return (-1);
 318                         inVal[i].val = v;
 319                         v[inVal[i].numVals].length = copyLen;
 320                         v[inVal[i].numVals].value = am(msg, valueLen);
 321                         if (v[inVal[i].numVals].value == 0 &&
 322                                         value != 0) {
 323                                 sfree(v);
 324                                 return (-1);
 325                         }
 326                         memcpy(v[inVal[i].numVals].value, value, copyLen);
 327                         inVal[i].numVals++;
 328 
 329                         return (0);
 330                 }
 331         }
 332 
 333         /* No previous value for this attribute */
 334 
 335         /*
 336          * value == 0 means deletion, in which case we create a
 337          * __nis_value_t with the numVals field set to -1.
 338          */
 339         if (value != 0) {
 340                 if ((v = am(msg, sizeof (*v))) == 0)
 341                         return (-1);
 342                 v->length = copyLen;
 343                 v->value = am(msg, valueLen);
 344                 if (v->value == 0 && value != 0) {
 345                         sfree(v);
 346                         return (-1);
 347                 }
 348                 memcpy(v->value, value, copyLen);
 349         }
 350 
 351         inVal = realloc(inVal, (num+1)*sizeof (inVal[0]));
 352         if (inVal == 0) {
 353                 if (value != 0) {
 354                         sfree(v->value);
 355                         sfree(v);
 356                 }
 357                 return (-1);
 358         }
 359         *inValP = inVal;
 360 
 361         inName = realloc(inName,
 362                 (num+1)*sizeof (inName[0]));
 363         if (inName == 0 || (inName[num] =
 364                         sdup(msg, T, name)) == 0) {
 365                 sfree(v->value);
 366                 sfree(v);
 367                 return (-1);
 368         }
 369         *inNameP = inName;
 370 
 371         inVal[num].type = type;
 372         inVal[num].repeat = 0;
 373         if (value != 0) {
 374                 inVal[num].numVals = 1;
 375                 inVal[num].val = v;
 376         } else {
 377                 inVal[num].numVals = -1;
 378                 inVal[num].val = 0;
 379         }
 380 
 381         *numP += 1;
 382 
 383         return (0);
 384 }
 385 
 386 int
 387 addAttr2RuleValue(__nis_value_type_t type, char *name, void *value,
 388                 int valueLen, __nis_rule_value_t *rv) {
 389         char                    *myself = "addAttr2RuleValue";
 390 
 391         if (name == 0 || rv == 0)
 392                 return (-1);
 393 
 394         return (addVal2RuleValue(myself, 0, 0, type, name, value, valueLen,
 395                                 &rv->numAttrs, &rv->attrName, &rv->attrVal));
 396 }
 397 
 398 int
 399 addSAttr2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
 400         return (addAttr2RuleValue(vt_string, name, value, slen(value), rv));
 401 }
 402 
 403 int
 404 addCol2RuleValue(__nis_value_type_t type, char *name, void *value,
 405                 int valueLen, __nis_rule_value_t *rv) {
 406         char *myself = "addCol2RuleValue";
 407 
 408         if (name == 0 || rv == 0)
 409                 return (-1);
 410 
 411         return (addVal2RuleValue(myself, 1, 1, type, name, value, valueLen,
 412                                 &rv->numColumns, &rv->colName, &rv->colVal));
 413 }
 414 
 415 int
 416 addSCol2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
 417         return (addCol2RuleValue(vt_string, name, value, slen(value), rv));
 418 }
 419 
 420 /*
 421  * Given a table mapping, a NIS+ DB query, and (optionally) an existing
 422  * and compatible __nis_rule_value_t, return a new __nis_rule_value_t
 423  * with the values from the query added.
 424  */
 425 __nis_rule_value_t *
 426 buildNisPlusRuleValue(__nis_table_mapping_t *t, db_query *q,
 427                         __nis_rule_value_t *rv) {
 428         int                     i;
 429 
 430         if (t == 0 || q == 0)
 431                 return (0);
 432 
 433         rv = initRuleValue(1, rv);
 434         if (rv == 0)
 435                 return (0);
 436 
 437         for (i = 0; i < q->components.components_len; i++) {
 438 
 439                 /* Ignore out-of-range column index */
 440                 if (q->components.components_val[i].which_index >=
 441                                 t->numColumns)
 442                         continue;
 443 
 444                 /*
 445                  * Add the query value. A NULL value indicates deletion,
 446                  * but addCol2RuleValue() takes care of that for us.
 447                  */
 448                 if (addCol2RuleValue(vt_string,
 449                                 t->column[q->components.components_val[i].
 450                                                 which_index],
 451                                 q->components.components_val[i].index_value->
 452                                         itemvalue.itemvalue_val,
 453                                 q->components.components_val[i].index_value->
 454                                         itemvalue.itemvalue_len, rv) != 0) {
 455                         freeRuleValue(rv, 1);
 456                         rv = 0;
 457                         break;
 458                 }
 459         }
 460 
 461         return (rv);
 462 }
 463 
 464 
 465 /*
 466  * Given a LHS rule 'rl', return an array containing the item names,
 467  * and the number of elements in the array in '*numItems'.
 468  *
 469  * If there are 'me_match' __nis_mapping_element_t's, we use the
 470  * supplied '*rval' (if any) to derive values for the items in
 471  * the 'me_match', and add the values thus derived to '*rval' (in
 472  * which case the '*rval' pointer will change; the old '*rval'
 473  * is deleted).
 474  */
 475 __nis_mapping_item_t *
 476 buildLvalue(__nis_mapping_rlhs_t *rl, __nis_value_t **rval, int *numItems) {
 477         __nis_value_t           *val, *r;
 478         __nis_mapping_item_t    *item = 0;
 479         int                     i, n, ni = 0, nv = 0;
 480         int                     repeat = 0;
 481 
 482         if (rl == 0)
 483                 return (0);
 484 
 485         if (rval != 0) {
 486                 r = *rval;
 487                 repeat = r->repeat;
 488         } else
 489                 r = 0;
 490 
 491         /* If there is more than one element, we concatenate the items */
 492         for (i = 0; i < rl->numElements; i++) {
 493                 __nis_mapping_element_t *e = &rl->element[i];
 494                 __nis_mapping_item_t    *olditem, *tmpitem = 0;
 495                 __nis_value_t           **tmp;
 496 
 497                 switch (e->type) {
 498                 case me_item:
 499                         tmpitem = cloneItem(&e->element.item);
 500                         break;
 501                 case me_match:
 502                         /*
 503                          * Obtain values for the items in the 'me_match'
 504                          * element.
 505                          */
 506                         tmp = matchMappingItem(e->element.match.fmt, r, &nv,
 507                                 0, 0);
 508                         if (tmp != 0) {
 509                                 freeValue(r, 1);
 510                                 val = 0;
 511                                 for (n = 0; n < nv; n++) {
 512                                         r = concatenateValues(val, tmp[n]);
 513                                         freeValue(val, 1);
 514                                         freeValue(tmp[n], 1);
 515                                         val = r;
 516                                         if (val == 0) {
 517                                                 for (n++; n < nv; n++) {
 518                                                         freeValue(tmp[n], 1);
 519                                                 }
 520                                                 break;
 521                                         }
 522                                 }
 523                                 free(tmp);
 524                                 if (rval != 0) {
 525                                         if (repeat && val != 0)
 526                                                 val->repeat = repeat;
 527                                         *rval = val;
 528                                 }
 529                                 for (n = 0; n < e->element.match.numItems;
 530                                                 n++) {
 531                                         olditem = item;
 532                                         item = concatenateMappingItem(item, ni,
 533                                                 &e->element.match.item[n]);
 534                                         freeMappingItem(olditem, ni);
 535                                         if (item == 0) {
 536                                                 ni = 0;
 537                                                 break;
 538                                         }
 539                                         ni++;
 540                                 }
 541                         }
 542                         break;
 543                 case me_print:
 544                 case me_split:
 545                 case me_extract:
 546                 default:
 547                         /* These shouldn't show up on the LHS; ignore */
 548                         break;
 549                 }
 550 
 551                 if (tmpitem != 0) {
 552                         olditem = item;
 553                         item = concatenateMappingItem(item, ni, tmpitem);
 554                         freeMappingItem(olditem, ni);
 555                         freeMappingItem(tmpitem, 1);
 556                         ni++;
 557                         if (item == 0) {
 558                                 ni = 0;
 559                                 break;
 560                         }
 561                 }
 562         }
 563 
 564         if (numItems != 0)
 565                 *numItems = ni;
 566 
 567         return (item);
 568 }
 569 
 570 __nis_value_t *
 571 buildRvalue(__nis_mapping_rlhs_t *rl, __nis_mapping_item_type_t native,
 572                 __nis_rule_value_t *rv, int *stat) {
 573         __nis_value_t   *val, *vold = 0, *vnew;
 574         int             i;
 575         char            *myself = "buildRvalue";
 576 
 577         if (rl == 0 || rl->numElements <= 0) {
 578                 /*
 579                  * No RHS indicates deletion, as does a __nis_value_t
 580                  * with numVals == -1, so we return such a creature.
 581                  */
 582                 val = am(myself, sizeof (*val));
 583                 if (val != 0) {
 584                         val->type = vt_string;
 585                         val->numVals = -1;
 586                 }
 587                 return (val);
 588         }
 589 
 590         /* If there is more than one element, we concatenate the values */
 591         for (i = 0; i < rl->numElements; i++) {
 592                 vnew = getMappingElement(&rl->element[i], native, rv, stat);
 593                 val = concatenateValues(vold, vnew);
 594                 freeValue(vnew, 1);
 595                 freeValue(vold, 1);
 596                 vold = val;
 597         }
 598         return (val);
 599 }
 600 
 601 /*
 602  * Derive values for the LDAP attributes specified by the rule 'r',
 603  * and add them to the rule-value 'rv'.
 604  *
 605  * If 'doAssign' is set, out-of-context assignments are performed,
 606  * otherwise not.
 607  */
 608 __nis_rule_value_t *
 609 addLdapRuleValue(__nis_table_mapping_t *t,
 610                         __nis_mapping_rule_t *r,
 611                         __nis_mapping_item_type_t lnative,
 612                         __nis_mapping_item_type_t rnative,
 613                         __nis_rule_value_t *rv,
 614                         int doAssign, int *stat) {
 615         int                     i, j;
 616         __nis_value_t           *rval, *lval;
 617         __nis_mapping_item_t    *litem;
 618         int                     numItems;
 619         char                    **dn = 0;
 620         int                     numDN = 0;
 621         char                    *myself = "addLdapRuleValue";
 622 
 623 
 624         /* Do we have the required values ? */
 625         if (rv == 0)
 626                 return (0);
 627 
 628         /*
 629          * Establish appropriate search base. For rnative == mit_nisplus,
 630          * we're deriving LDAP attribute values from NIS+ columns; in other
 631          * words, we're writing to LDAP, and should use the write.base value.
 632          */
 633         __nisdb_get_tsd()->searchBase = (rnative == mit_nisplus) ?
 634                 t->objectDN->write.base : t->objectDN->read.base;
 635 
 636         /* Set escapeFlag if LHS is "dn" to escape special chars */
 637         if (yp2ldap && r->lhs.numElements == 1 &&
 638                 r->lhs.element->type == me_item &&
 639                 r->lhs.element->element.item.type == mit_ldap &&
 640                 strcasecmp(r->lhs.element->element.item.name, "dn") == 0) {
 641                         __nisdb_get_tsd()->escapeFlag = '1';
 642         }
 643 
 644         /* Build the RHS value */
 645         rval = buildRvalue(&r->rhs, rnative, rv, stat);
 646 
 647         /* Reset escapeFlag */
 648         __nisdb_get_tsd()->escapeFlag = '\0';
 649 
 650         if (rval == 0)
 651                 return (rv);
 652 
 653         /*
 654          * Special case: If we got no value for the RHS (presumably because
 655          * we're missing one or more item values), we don't produce an lval.
 656          * Note that this isn't the same thing as an empty value, which we
 657          * faithfully try to transmit to LDAP.
 658          */
 659         if (rval->numVals == 1 && rval->val[0].value == 0) {
 660                 freeValue(rval, 1);
 661                 return (rv);
 662         }
 663 
 664         /* Obtain the LHS item names */
 665         litem = buildLvalue(&r->lhs, &rval, &numItems);
 666         if (litem == 0) {
 667                 freeValue(rval, 1);
 668                 return (rv);
 669         }
 670 
 671         /* Get string representations of the LHS item names */
 672         lval = 0;
 673         for (i = 0; i < numItems; i++) {
 674                 __nis_value_t   *tmpval, *old;
 675 
 676                 tmpval = getMappingItem(&litem[i], lnative, 0, 0, NULL);
 677 
 678                 /*
 679                  * If the LHS item is out-of-context, we do the
 680                  * assignment right here.
 681                  */
 682                 if (doAssign && litem[i].type == mit_ldap &&
 683                                 litem[i].searchSpec.triple.scope !=
 684                                         LDAP_SCOPE_UNKNOWN &&
 685                                 slen(litem[i].searchSpec.triple.base) > 0 &&
 686                                 (slen(litem[i].searchSpec.triple.attrs) > 0 ||
 687                                 litem[i].searchSpec.triple.element != 0)) {
 688                         int     stat;
 689 
 690                         if (dn == 0)
 691                                 dn = findDNs(myself, rv, 1,
 692                                         t->objectDN->write.base,
 693                                         &numDN);
 694 
 695                         stat = storeLDAP(&litem[i], i, numItems, rval,
 696                                 t->objectDN, dn, numDN);
 697                         if (stat != LDAP_SUCCESS) {
 698                                 char    *iname = "<unknown>";
 699 
 700                                 if (tmpval != 0 &&
 701                                                 tmpval->numVals == 1)
 702                                         iname = tmpval->val[0].value;
 703                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 704                                         "%s: LDAP store \"%s\": %s",
 705                                         myself, iname,
 706                                         ldap_err2string(stat));
 707                         }
 708 
 709                         freeValue(tmpval, 1);
 710                         continue;
 711                 }
 712 
 713                 old = lval;
 714                 lval = concatenateValues(old, tmpval);
 715                 freeValue(tmpval, 1);
 716                 freeValue(old, 1);
 717         }
 718 
 719         /* Don't need the LHS items themselves anymore */
 720         freeMappingItem(litem, numItems);
 721 
 722         /*
 723          * If we don't have an 'lval' (probably because all litem[i]:s
 724          * were out-of-context assignments), we're done.
 725          */
 726         if (lval == 0 || lval->numVals <= 0) {
 727                 freeValue(lval, 1);
 728                 freeValue(rval, 1);
 729                 return (rv);
 730         }
 731 
 732         for (i = 0, j = 0; i < lval->numVals; i++) {
 733                 /* Special case: rval->numVals < 0 means deletion */
 734                 if (rval->numVals < 0) {
 735                         (void) addAttr2RuleValue(rval->type,
 736                                 lval->val[i].value, 0, 0, rv);
 737                         continue;
 738                 }
 739                 /* If we're out of values, repeat the last one */
 740                 if (j >= rval->numVals)
 741                         j = (rval->numVals > 0) ? rval->numVals-1 : 0;
 742                 for (; j < rval->numVals; j++) {
 743                         /*
 744                          * If this is the 'dn', and the value ends in a
 745                          * comma, append the appropriate search base.
 746                          */
 747                         if (strcasecmp("dn", lval->val[i].value) == 0 &&
 748                                         lastChar(&rval->val[j]) == ',' &&
 749                                         t->objectDN->write.scope !=
 750                                                 LDAP_SCOPE_UNKNOWN) {
 751                                 void    *nval;
 752                                 int     nlen = -1;
 753 
 754                                 nval = appendString2SingleVal(
 755                                         t->objectDN->write.base, &rval->val[j],
 756                                         &nlen);
 757                                 if (nval != 0 && nlen >= 0) {
 758                                         sfree(rval->val[j].value);
 759                                         rval->val[j].value = nval;
 760                                         rval->val[j].length = nlen;
 761                                 }
 762                         }
 763                         (void) addAttr2RuleValue(rval->type,
 764                                 lval->val[i].value, rval->val[j].value,
 765                                 rval->val[j].length, rv);
 766                         /*
 767                          * If the lval is multi-valued, go on to the
 768                          * other values; otherwise, quit (but increment
 769                          * the 'rval' value index).
 770                          */
 771                         if (!lval->repeat) {
 772                                 j++;
 773                                 break;
 774                         }
 775                 }
 776         }
 777 
 778         /* Clean up */
 779         freeValue(lval, 1);
 780         freeValue(rval, 1);
 781 
 782         return (rv);
 783 }
 784 
 785 /*
 786  * Remove the indicated attribute, and any values for it, from the
 787  * rule-value.
 788  */
 789 void
 790 delAttrFromRuleValue(__nis_rule_value_t *rv, char *attrName) {
 791         int     i;
 792 
 793         if (rv == 0 || attrName == 0)
 794                 return;
 795 
 796         for (i = 0; i < rv->numAttrs; i++) {
 797                 if (strcasecmp(attrName, rv->attrName[i]) == 0) {
 798                         int     j;
 799 
 800                         for (j = 0; j < rv->attrVal[i].numVals; j++)
 801                                 sfree(rv->attrVal[i].val[j].value);
 802                         if (rv->attrVal[i].numVals > 0)
 803                                 sfree(rv->attrVal[i].val);
 804 
 805                         sfree(rv->attrName[i]);
 806 
 807                         /* Move up the rest of the attribute names/values */
 808                         for (j = i+1; j < rv->numAttrs; j++) {
 809                                 rv->attrName[j-1] = rv->attrName[j];
 810                                 rv->attrVal[j-1] = rv->attrVal[j];
 811                         }
 812 
 813                         rv->numAttrs -= 1;
 814 
 815                         break;
 816                 }
 817         }
 818 }
 819 
 820 /*
 821  * Remove the indicated column, and any values for it, from the
 822  * rule-value.
 823  */
 824 void
 825 delColFromRuleValue(__nis_rule_value_t *rv, char *colName) {
 826         int     i;
 827 
 828         if (rv == 0 || colName == 0)
 829                 return;
 830 
 831         for (i = 0; i < rv->numColumns; i++) {
 832                 if (strcmp(colName, rv->colName[i]) == 0) {
 833                         int     j;
 834 
 835                         for (j = 0; j < rv->colVal[i].numVals; j++)
 836                                 sfree(rv->colVal[i].val[j].value);
 837                         if (rv->colVal[i].numVals > 0)
 838                                 sfree(rv->colVal[i].val);
 839 
 840                         sfree(rv->colName[i]);
 841 
 842                         /* Move up the rest of the column names/values */
 843                         for (j = i+1; j < rv->numColumns; j++) {
 844                                 rv->colName[j-1] = rv->colName[j];
 845                                 rv->colVal[j-1] = rv->colVal[j];
 846                         }
 847 
 848                         rv->numColumns -= 1;
 849 
 850                         break;
 851                 }
 852         }
 853 }
 854 
 855 /*
 856  * Add the write-mode object classes specified by 'objClassAttrs' to the
 857  * rule-value 'rv'.
 858  * If there's an error, 'rv' is deleted, and NULL returned.
 859  */
 860 __nis_rule_value_t *
 861 addObjectClasses(__nis_rule_value_t *rv, char *objClassAttrs) {
 862         char    *filter = 0, **fc = 0;
 863         int     i, nfc = 0;
 864 
 865         /*
 866          * Expect to only use this for existing rule-values, so rv == 0 is
 867          * an error.
 868          */
 869         if (rv == 0)
 870                 return (0);
 871 
 872         /*
 873          * If 'objClassAttrs' is NULL, we trivially have nothing to do.
 874          * Assume the caller knows what it's doing, and return success.
 875          */
 876         if (objClassAttrs == 0)
 877                 return (rv);
 878 
 879         /*
 880          * Make an AND-filter of the object classes, and split into
 881          * components. (Yes, this is a bit round-about, but leverages
 882          * existing functions.)
 883          */
 884         filter = makeFilter(objClassAttrs);
 885         if (filter == 0) {
 886                 freeRuleValue(rv, 1);
 887                 return (0);
 888         }
 889 
 890         fc = makeFilterComp(filter, &nfc);
 891         if (fc == 0 || nfc <= 0) {
 892                 free(filter);
 893                 freeRuleValue(rv, 1);
 894                 return (0);
 895         }
 896 
 897         /* Add the objectClass attributes to the rule-value */
 898         for (i = 0; i < nfc; i++) {
 899                 char    *name, *value;
 900 
 901                 name = fc[i];
 902                 /* Skip if not of the "name=value" form */
 903                 if ((value = strchr(name, '=')) == 0)
 904                         continue;
 905 
 906                 *value = '\0';
 907                 value++;
 908 
 909                 /* Skip if the attribute name isn't "objectClass" */
 910                 if (strcasecmp("objectClass", name) != 0)
 911                         continue;
 912 
 913                 if (addSAttr2RuleValue(name, value, rv) != 0) {
 914                         free(filter);
 915                         freeFilterComp(fc, nfc);
 916                         freeRuleValue(rv, 1);
 917                         return (0);
 918                 }
 919         }
 920 
 921         free(filter);
 922         freeFilterComp(fc, nfc);
 923 
 924         return (rv);
 925 }
 926 
 927 
 928 static char *
 929 valString(__nis_value_t *val) {
 930         int     i;
 931 
 932         if (val == 0 || val->type != vt_string)
 933                 return (0);
 934 
 935         for (i = 0; i < val->numVals; i++) {
 936                 /* Look for a non-NULL, non-zero length value */
 937                 if (val->val[i].value != 0 && val->val[i].length > 0) {
 938                         char    *v = val->val[i].value;
 939 
 940                         /*
 941                          * Check that there's a NUL at the end. True,
 942                          * if there isn't, we may be looking beyond
 943                          * allocated memory. However, we would have done
 944                          * so in any case when the supposed string was
 945                          * traversed (printed, etc.), very possibly by
 946                          * a lot more than one byte. So, it's better to
 947                          * take a small risk here than a large one later.
 948                          */
 949                         if (v[val->val[i].length-1] == '\0' ||
 950                                         v[val->val[i].length] == '\0')
 951                                 return (v);
 952                 }
 953         }
 954 
 955         return (0);
 956 }
 957 
 958 char *
 959 findVal(char *name, __nis_rule_value_t *rv, __nis_mapping_item_type_t type) {
 960         int     i;
 961 
 962         if (type == mit_nisplus) {
 963                 for (i = 0; i < rv->numColumns; i++) {
 964                         if (rv->colName[i] == 0)
 965                                 continue;
 966                         if (strcmp(name, rv->colName[i]) == 0) {
 967                                 return (valString(&rv->colVal[i]));
 968                         }
 969                 }
 970         } else if (type == mit_ldap) {
 971                 for (i = 0; i < rv->numAttrs; i++) {
 972                         if (rv->attrName[i] == 0)
 973                                 continue;
 974                         if (strcasecmp(name, rv->attrName[i]) == 0) {
 975                                 return (valString(&rv->attrVal[i]));
 976                         }
 977                 }
 978         }
 979 
 980         return (0);
 981 }
 982 
 983 static char     *norv = "<NIL>";
 984 static char     *unknown = "<unknown>";
 985 
 986 /*
 987  * Attempt to derive a string identifying the rule-value 'rv'. The
 988  * returned string is a pointer, either into 'rv', or to static
 989  * storage, and must not be freed.
 990  */
 991 char *
 992 rvId(__nis_rule_value_t *rv, __nis_mapping_item_type_t type) {
 993         char    *v;
 994 
 995         if (rv == 0)
 996                 return (norv);
 997 
 998         if (rv->numColumns > 0 && type == mit_nisplus) {
 999                 /*
1000                  * Look for a column called "cname" or "name".
1001                  * If that fails, try "key" or "alias".
1002                  */
1003                 if ((v = findVal("cname", rv, type)) != 0)
1004                         return (v);
1005                 else if ((v = findVal("name", rv, type)) != 0)
1006                         return (v);
1007                 else if ((v = findVal("key", rv, type)) != 0)
1008                         return (v);
1009                 else if ((v = findVal("alias", rv, type)) != 0)
1010                         return (v);
1011         } else if (rv->numAttrs > 0 && type == mit_ldap) {
1012                 /*
1013                  * Look for "dn", or "cn".
1014                  */
1015                 if ((v = findVal("dn", rv, type)) != 0)
1016                         return (v);
1017                 else if ((v = findVal("cn", rv, type)) != 0)
1018                         return (v);
1019         }
1020 
1021         return (unknown);
1022 }
1023 
1024 /*
1025  * Merge the rule-values with the same DN into one. Each rule-value
1026  * in the returned array will have unique 'dn'. On entry, *numVals
1027  * contains the number of rule-values in 'rv'. On exit, it contains
1028  * the number of rule-values in the returned array or -1 on error.
1029  */
1030 __nis_rule_value_t *
1031 mergeRuleValueWithSameDN(__nis_rule_value_t *rv, int *numVals) {
1032         __nis_rule_value_t      *rvq = 0;
1033         char                    *dn, *odn;
1034         int                     count = 0;
1035         int                     i, j;
1036 
1037         if (numVals == 0)
1038                 return (0);
1039 
1040         for (i = 0; i < *numVals; i++) {
1041                 if ((dn = findVal("dn", &rv[i], mit_ldap)) != 0) {
1042                         for (j = 0; j < count; j++) {
1043                                 if ((odn = findVal("dn", &rvq[j],
1044                                                 mit_ldap)) != 0) {
1045                                         /* case sensitive compare */
1046                                         if (strcmp(dn, odn) != 0)
1047                                                 continue;
1048                                         if (mergeRuleValue(&rvq[j],
1049                                                         &rv[i]) == -1) {
1050                                                 freeRuleValue(rvq, count);
1051                                                 *numVals = -1;
1052                                                 return (0);
1053                                         }
1054                                         break;
1055                                 } else {
1056                                         freeRuleValue(rvq, count);
1057                                         *numVals = -1;
1058                                         return (0);
1059                                 }
1060                         }
1061                         /* if no match, then add it to the rulevalue array */
1062                         if (j == count) {
1063                                 rvq = growRuleValue(count, count + 1, rvq,
1064                                                                         &rv[i]);
1065                                 if (rvq == 0) {
1066                                         *numVals = -1;
1067                                         return (0);
1068                                 }
1069                                 count++;
1070                         }
1071                 }
1072         }
1073 
1074         *numVals = count;
1075         return (rvq);
1076 }