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 }