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