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 2005 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 /* 30 * DESCRIPTION: Contains dit_access interface support functions. 31 */ 32 #include <sys/systeminfo.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <sys/systeminfo.h> 38 #include <unistd.h> 39 #include <stdlib.h> 40 #include <syslog.h> 41 #include <ndbm.h> 42 #include <strings.h> 43 #include <errno.h> 44 #include "../ldap_util.h" 45 #include "../ldap_map.h" 46 #include "../ldap_parse.h" 47 #include "../ldap_structs.h" 48 #include "../ldap_val.h" 49 #include "../ldap_ruleval.h" 50 #include "../ldap_op.h" 51 #include "../ldap_attr.h" 52 #include "../ldap_nisdbquery.h" 53 #include "../nisdb_mt.h" 54 #include "shim.h" 55 #include "yptol.h" 56 #include "dit_access_utils.h" 57 58 /* 59 * Returns 'map,domain.' 60 */ 61 char * 62 getFullMapName(char *map, char *domain) { 63 char *myself = "getFullMapName"; 64 char *objPath; 65 if (map == 0 || domain == 0) { 66 return (0); 67 } 68 objPath = scat(myself, T, scat(myself, F, map, ","), 69 scat(myself, F, domain, ".")); 70 71 return (objPath); 72 } 73 74 /* 75 * Convert string to __nis_value_t 76 */ 77 __nis_value_t *stringToValue(char *dptr, int dsize) { 78 char *myself = "stringToValue"; 79 char *emptystr = ""; 80 __nis_value_t *val; 81 82 if ((val = am(myself, sizeof (*val))) == 0) { 83 return (0); 84 } 85 86 val->type = vt_string; 87 val->repeat = 0; 88 val->numVals = 1; 89 if ((val->val = am(myself, sizeof (val->val[0]))) == 0) { 90 sfree(val); 91 return (0); 92 } 93 94 /* 95 * Null strings or strings with length 0 are treated 96 * as empty strings with length 1 97 */ 98 if (dptr == 0 || dsize <= 0) { 99 dptr = emptystr; 100 dsize = 1; 101 } 102 103 val->val->length = dsize; 104 if (dptr[dsize - 1] != '\0') { 105 val->val->length = dsize + 1; 106 } 107 108 val->val->value = am(myself, val->val->length); 109 if (val->val->value == 0) { 110 freeValue(val, 1); 111 return (0); 112 } 113 (void) memcpy(val->val->value, dptr, dsize); 114 115 return (val); 116 } 117 118 /* 119 * Returns an array of rule-values corresponding to the 120 * splitfields. 121 */ 122 __nis_rule_value_t * 123 processSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal, 124 int *nv, int *statP) { 125 126 char *sepset; 127 __nis_rule_value_t *rvq; 128 __nis_mapping_format_t *ftmp; 129 __nis_value_t **valA, *tempVal; 130 int i, j, res, numVals, oldlen, count; 131 char *str, *oldstr; 132 char *myself = "processSplitField"; 133 134 /* sf will be non NULL */ 135 136 if (inVal == 0 || inVal->type != vt_string) { 137 *statP = MAP_PARAM_ERROR; 138 return (0); 139 } 140 141 /* Get the separator list */ 142 sepset = sf->separatorStr; 143 144 /* Initialize rule-value */ 145 rvq = 0; 146 count = 0; 147 148 if ((tempVal = stringToValue(inVal->val->value, 149 inVal->val->length)) == 0) { 150 *statP = MAP_NO_MEMORY; 151 return (0); 152 } 153 154 str = oldstr = tempVal->val->value; 155 oldlen = tempVal->val->length; 156 157 while (str) { 158 tempVal->val->value = str; 159 tempVal->val->length = strlen(str) + 1; 160 161 /* Loop to check which format matches str */ 162 for (i = 0; i <= sf->numSplits; i++) { 163 valA = matchMappingItem(sf->e[i].element.match.fmt, 164 tempVal, &numVals, sepset, &str); 165 if (valA == 0) { 166 /* The format didn't match. Try the next one */ 167 continue; 168 } 169 170 /* 171 * If we are here means we had a match. 172 * Each new set of values obtained from the match is 173 * added to a new rule-value. This is to preserve the 174 * the distinction between each set. 175 */ 176 rvq = growRuleValue(count, count + 1, rvq, 0); 177 if (rvq == 0) { 178 *statP = MAP_INTERNAL_ERROR; 179 for (j = 0; j < numVals; j++) 180 freeValue(valA[j], 1); 181 sfree(valA); 182 tempVal->val->value = oldstr; 183 tempVal->val->length = oldlen; 184 freeValue(tempVal, 1); 185 return (0); 186 } 187 count++; 188 189 for (j = 0; j < numVals; j++) { 190 res = addCol2RuleValue(vt_string, 191 sf->e[i].element.match.item[j].name, 192 valA[j]->val->value, 193 valA[j]->val->length, 194 &rvq[count - 1]); 195 if (res == -1) { 196 *statP = MAP_INTERNAL_ERROR; 197 for (; j < numVals; j++) 198 freeValue(valA[j], 1); 199 sfree(valA); 200 tempVal->val->value = oldstr; 201 tempVal->val->length = oldlen; 202 freeValue(tempVal, 1); 203 freeRuleValue(rvq, count); 204 return (0); 205 } 206 freeValue(valA[j], 1); 207 } 208 sfree(valA); 209 210 /* 211 * Since we had a match, break out of this loop 212 * to parse remainder of str 213 */ 214 break; 215 } 216 217 /* Didn't find any match, so get out of the loop */ 218 if (i > sf->numSplits) { 219 str = 0; 220 break; 221 } 222 223 /* Skip the separators before looping back */ 224 if (str) { 225 str = str + strspn(str, sepset); 226 if (*str == '\0') 227 break; 228 } 229 } 230 231 tempVal->val->value = oldstr; 232 tempVal->val->length = oldlen; 233 freeValue(tempVal, 1); 234 235 if (str == 0) { 236 freeRuleValue(rvq, count); 237 return (0); 238 } 239 240 if (nv != 0) 241 *nv = count; 242 243 return (rvq); 244 } 245 246 /* 247 * Convert the datum to an array of RuleValues 248 */ 249 __nis_rule_value_t * 250 datumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t, 251 int *nv, char *domain, bool_t readonly, int *statP) { 252 253 __nis_rule_value_t *rvq, *subrvq, *newrvq; 254 __nis_value_t *val; 255 __nis_value_t **valA; 256 __nis_table_mapping_t *sf; 257 int valueLen, comLen, numVals, nr, count = 1; 258 int i, j, k, l, af; 259 char *ipaddr, *ipvalue; 260 261 /* At this point, 't' is always non NULL */ 262 263 /* Initialize rule-value */ 264 if ((rvq = initRuleValue(1, 0)) == 0) { 265 *statP = MAP_INTERNAL_ERROR; 266 return (0); 267 } 268 269 /* Add domainname to rule-value */ 270 if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain), 271 rvq)) { 272 freeRuleValue(rvq, 1); 273 *statP = MAP_INTERNAL_ERROR; 274 return (0); 275 } 276 277 /* Handle key */ 278 if (key != 0) { 279 /* Add field=value pair for N2LKEY */ 280 i = addCol2RuleValue(vt_string, N2LKEY, key->dptr, key->dsize, 281 rvq); 282 283 /* For readonly, add field=value pair for N2LSEARCHKEY */ 284 if (readonly == TRUE && i == 0) { 285 i = addCol2RuleValue(vt_string, N2LSEARCHKEY, key->dptr, 286 key->dsize, rvq); 287 } 288 if (i) { 289 freeRuleValue(rvq, 1); 290 *statP = MAP_INTERNAL_ERROR; 291 return (0); 292 } 293 294 /* Add field=value pairs for IP addresses */ 295 if (checkIPaddress(key->dptr, key->dsize, &ipaddr) > 0) { 296 /* If key is IPaddress, use preferred format */ 297 ipvalue = ipaddr; 298 valueLen = strlen(ipaddr); 299 i = addCol2RuleValue(vt_string, N2LIPKEY, ipvalue, 300 valueLen, rvq); 301 } else { 302 /* If not, use original value for N2LSEARCHIPKEY */ 303 ipaddr = 0; 304 ipvalue = key->dptr; 305 valueLen = key->dsize; 306 i = 0; 307 } 308 309 if (readonly == TRUE && i == 0) { 310 i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue, 311 valueLen, rvq); 312 } 313 sfree(ipaddr); 314 if (i) { 315 freeRuleValue(rvq, 1); 316 *statP = MAP_INTERNAL_ERROR; 317 return (0); 318 } 319 } 320 321 /* Handle datum value */ 322 if (value != 0 && t->e) { 323 valueLen = value->dsize; 324 /* 325 * Extract the comment, if any, and add it to 326 * the rule-value. 327 */ 328 if (t->commentChar != '\0') { 329 /* 330 * We loop on value->dsize because value->dptr 331 * may not be NULL-terminated. 332 */ 333 for (i = 0; i < value->dsize; i++) { 334 if (value->dptr[i] == t->commentChar) { 335 valueLen = i; 336 comLen = value->dsize - i - 1; 337 if (comLen == 0) 338 break; 339 if (addCol2RuleValue(vt_string, 340 N2LCOMMENT, value->dptr + i + 1, 341 comLen, rvq)) { 342 freeRuleValue(rvq, 1); 343 *statP = MAP_INTERNAL_ERROR; 344 return (0); 345 } 346 break; 347 } 348 } 349 } 350 351 /* Skip trailing whitespaces */ 352 for (; valueLen > 0 && (value->dptr[valueLen - 1] == ' ' || 353 value->dptr[valueLen - 1] == '\t'); valueLen--); 354 355 /* 356 * At this point valueLen is the effective length of 357 * the data. Convert value into __nis_value_t so that 358 * we can use the matchMappingItem function to break it 359 * into fields. 360 */ 361 if ((val = stringToValue(value->dptr, valueLen)) == 0) { 362 freeRuleValue(rvq, 1); 363 *statP = MAP_NO_MEMORY; 364 return (0); 365 } 366 367 /* Perform namefield match */ 368 valA = matchMappingItem(t->e->element.match.fmt, val, 369 &numVals, 0, 0); 370 if (valA == 0) { 371 freeValue(val, 1); 372 freeRuleValue(rvq, 1); 373 *statP = MAP_NAMEFIELD_MATCH_ERROR; 374 return (0); 375 } 376 377 /* We don't need val anymore, so free it */ 378 freeValue(val, 1); 379 380 /* 381 * Since matchMappingItem only returns us an array of 382 * __nis_value_t's, we need to associate each value 383 * in the array with the corresponding item name. 384 * This code assumes that numVals will be less than or 385 * equal to the number of item names associated with 386 * the format. 387 * These name=value pairs are added to rvq. 388 */ 389 for (i = 0, *statP = SUCCESS; i < numVals; i++) { 390 for (j = 0; j < count; j++) { 391 if (addCol2RuleValue(vt_string, 392 t->e->element.match.item[i].name, 393 valA[i]->val->value, 394 valA[i]->val->length, &rvq[j])) { 395 *statP = MAP_INTERNAL_ERROR; 396 break; 397 } 398 } 399 if (*statP == MAP_INTERNAL_ERROR) 400 break; 401 402 /* 403 * Check if splitField exists for the field. 404 * Since splitfields are also stored as mapping 405 * structures, we need to get the hash table entry 406 * corresponding to the splitfield name 407 */ 408 sf = mappingFromMap(t->e->element.match.item[i].name, 409 domain, statP); 410 if (*statP == MAP_NO_MEMORY) 411 break; 412 *statP = SUCCESS; 413 if (sf == 0) 414 continue; 415 416 /* 417 * Process and add splitFields to rule-value rvq 418 */ 419 subrvq = processSplitField(sf, valA[i], &nr, statP); 420 421 if (subrvq == 0) { 422 /* statP would have been set */ 423 break; 424 } 425 426 /* 427 * We merge 'count' rule-values in rvq with 'nr' 428 * rule-values from subrvq to give us a whopping 429 * 'count * nr' rule-values 430 */ 431 432 /* Initialize the new rule-value array */ 433 if ((newrvq = initRuleValue(count * nr, 0)) == 0) { 434 *statP = MAP_INTERNAL_ERROR; 435 freeRuleValue(subrvq, nr); 436 break; 437 } 438 439 for (j = 0, l = 0; j < nr; j++) { 440 for (k = 0; k < count; k++, l++) { 441 if ((mergeRuleValue(&newrvq[l], 442 &rvq[k]) == -1) || 443 (mergeRuleValue( 444 &newrvq[l], 445 &subrvq[j]) == -1)) { 446 *statP = MAP_INTERNAL_ERROR; 447 for (i = 0; i < numVals; i++) 448 freeValue(valA[i], 1); 449 sfree(valA); 450 freeRuleValue(rvq, count); 451 freeRuleValue(newrvq, 452 count * nr); 453 freeRuleValue(subrvq, nr); 454 return (0); 455 } 456 } 457 } 458 459 freeRuleValue(rvq, count); 460 rvq = newrvq; 461 count = l; 462 freeRuleValue(subrvq, nr); 463 464 } 465 466 /* We don't need valA anymore, so free it */ 467 for (i = 0; i < numVals; i++) 468 freeValue(valA[i], 1); 469 sfree(valA); 470 471 if (*statP != SUCCESS) { 472 freeRuleValue(rvq, count); 473 return (0); 474 } 475 476 } /* if value */ 477 478 if (nv != 0) 479 *nv = count; 480 return (rvq); 481 482 } 483 484 /* 485 * Generate name=values pairs for splitfield names 486 * 487 * Consider Example: 488 * nisLDAPnameFields club: 489 * ("%s %s %s", name, code, members) 490 * nisLDAPsplitField members: 491 * ("(%s,%s,%s)", host, user, domain), 492 * ("%s", group) 493 * On entry, 494 * - rv is an array of numVals rule-values each containing 495 * name=value pairs for names occuring in nisLDAPsplitField. 496 * (i.e host, user, domain, group) 497 * - trv contains name=value pairs for names occuring in 498 * nisLDAPnameFields. (i.e name, code but not members) 499 * 500 * For every name in nisLDAPnamefields that is a splitfield, 501 * this function applies the data in rv to the corresponding 502 * splitfield formats (accessed thru t), to generate a single 503 * string value for the corresponding splitfield (members). 504 * This new name=value pair is then added to trv. 505 * Besides, any uninitialized namefield names are set to empty strings. 506 */ 507 suc_code 508 addSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv, 509 __nis_rule_value_t *trv, int numVals, char *domain) { 510 __nis_table_mapping_t *sf; 511 __nis_value_t *val; 512 int i, j, k, nitems, res, statP; 513 char *str, *tempstr; 514 char delim[2] = {0, 0}; 515 char *emptystr = ""; 516 char *myself = "addSplitFieldValues"; 517 518 if (trv == 0) 519 return (MAP_INTERNAL_ERROR); 520 521 if (t->e == 0) 522 return (SUCCESS); 523 524 nitems = t->e->element.match.numItems; 525 526 /* 527 * Procedure: 528 * - Check each name in nisLDAPnamefield 529 * - if it's a splifield, construct its value and add it to trv 530 * - if not, check if it has a value 531 * - if not, add empty string 532 */ 533 for (i = 0, sf = 0; i < nitems; i++) { 534 if (rv) { 535 /* 536 * str will eventually contain the single string 537 * value for the corresponding splitfield. 538 * No point initializing str if rv == 0 because 539 * splitfield cannot be constructed without rv. 540 * So, only initialized here. 541 */ 542 str = 0; 543 544 /* Check if it's a splitfield name */ 545 sf = mappingFromMap(t->e->element.match.item[i].name, 546 domain, &statP); 547 548 /* 549 * Return only incase of memory allocation failure. 550 * The other error case (MAP_NO_MAPPING_EXISTS), 551 * indicates that the item name is not a splitfieldname 552 * i.e it's a namefieldname. This case is handled by 553 * the following if (sf == 0) 554 */ 555 if (statP == MAP_NO_MEMORY) 556 return (statP); 557 } 558 559 if (sf == 0) { 560 /* 561 * Not a splitfield name. Verify if it has a value 562 */ 563 if (findVal(t->e->element.match.item[i].name, 564 trv, mit_nisplus) == 0) { 565 /* if not, use empty string */ 566 res = addCol2RuleValue(vt_string, 567 t->e->element.match.item[i].name, 568 emptystr, 0, trv); 569 if (res == -1) { 570 return (MAP_INTERNAL_ERROR); 571 } 572 } 573 /* 574 * If rv == 0 then sf == 0 so we will continue here 575 * i.e. does not matter that str is not yet set up. 576 */ 577 continue; 578 } 579 580 /* Code to construct a single value */ 581 582 /* Use the first separator character as the delimiter */ 583 delim[0] = sf->separatorStr[0]; 584 585 for (j = 0; j < numVals; j++) { 586 /* sf->numSplits is zero-based */ 587 for (k = 0; k <= sf->numSplits; k++) { 588 val = getMappingFormatArray( 589 sf->e[k].element.match.fmt, &rv[j], 590 fa_item, 591 sf->e[k].element.match.numItems, 592 sf->e[k].element.match.item); 593 if (val == 0) 594 continue; 595 if (val->numVals > 0) { 596 if (str) { 597 tempstr = scat(myself, 598 0, str, delim); 599 sfree(str); 600 if (tempstr) 601 str = tempstr; 602 else { 603 freeValue(val, 1); 604 return (MAP_NO_MEMORY); 605 } 606 } 607 tempstr = scat(myself, 0, str, 608 val->val->value); 609 sfree(str); 610 if (tempstr) 611 str = tempstr; 612 else { 613 freeValue(val, 1); 614 return (MAP_NO_MEMORY); 615 } 616 } 617 freeValue(val, 1); 618 } 619 } 620 if (str == 0) 621 str = emptystr; 622 623 res = addCol2RuleValue(vt_string, 624 t->e->element.match.item[i].name, 625 str, strlen(str), trv); 626 627 if (str != emptystr) 628 sfree(str); 629 630 if (res == -1) { 631 return (MAP_INTERNAL_ERROR); 632 } 633 } 634 635 return (SUCCESS); 636 } 637 638 /* 639 * Updates 'rv' with NIS name=value pairs suitable to 640 * construct datum from namefield information. 641 * Some part based on createNisPlusEntry (from ldap_nisdbquery.c) 642 * This code assumes that from a given LDAP entry, applying the 643 * mapping rules, would give us one or more NIS entries, differing 644 * only in key. 645 */ 646 suc_code 647 buildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, 648 char *domain) { 649 int r, i, j, k, l, index, nrq, res, len; 650 int numItems, splitname, count, statP; 651 __nis_value_t *rval; 652 __nis_mapping_item_t *litem; 653 __nis_mapping_rule_t *rl; 654 __nis_rule_value_t *rvq; 655 char *value, *emptystr = ""; 656 657 statP = SUCCESS; 658 659 /* Initialize default base */ 660 __nisdb_get_tsd()->searchBase = t->objectDN->read.base; 661 662 /* Initialize rule-value rvq */ 663 rvq = 0; 664 count = 0; 665 666 /* Add domainname to rule-value */ 667 if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain), 668 rv)) { 669 return (MAP_INTERNAL_ERROR); 670 } 671 672 for (r = 0; r < t->numRulesFromLDAP; r++) { 673 rl = t->ruleFromLDAP[r]; 674 675 /* Set escapeFlag if RHS is "dn" to remove escape chars */ 676 if (rl->rhs.numElements == 1 && 677 rl->rhs.element->type == me_item && 678 rl->rhs.element->element.item.type == mit_ldap && 679 strcasecmp(rl->rhs.element->element.item.name, "dn") 680 == 0) { 681 __nisdb_get_tsd()->escapeFlag = '2'; 682 } 683 684 rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL); 685 686 /* Reset escapeFlag */ 687 __nisdb_get_tsd()->escapeFlag = '\0'; 688 689 if (rval == 0) { 690 continue; 691 } 692 693 if (rval->numVals <= 0) { 694 /* Treat as invalid */ 695 freeValue(rval, 1); 696 continue; 697 } 698 699 litem = buildLvalue(&rl->lhs, &rval, &numItems); 700 if (litem == 0) { 701 /* This will take care of numItems == 0 */ 702 freeValue(rval, 1); 703 continue; 704 } 705 706 if (rval->numVals > 1) { 707 if (numItems == 1 && litem->repeat) 708 nrq = rval->numVals; 709 else if (numItems > 1 && rval->repeat) 710 nrq = 1 + ((rval->numVals-1)/numItems); 711 else 712 nrq = 1; 713 } else 714 nrq = 1; 715 716 /* Set splitname if splitfield names are specified */ 717 for (i = 0; i < numItems; i++) { 718 if (strcasecmp(litem[i].name, N2LKEY) == 0 || 719 strcasecmp(litem[i].name, N2LIPKEY) == 0 || 720 strcasecmp(litem[i].name, N2LCOMMENT) == 0) 721 continue; 722 for (j = 0; j < t->numColumns; j++) { 723 if (strcmp(litem[i].name, t->column[j]) == 0) 724 break; 725 } 726 if (j == t->numColumns) 727 break; 728 } 729 730 splitname = (i < numItems)?1:0; 731 732 for (j = 0; j < nrq; j++) { 733 if (splitname == 1) { 734 /* 735 * Put every value of splitfieldname in a new 736 * rule-value. Helps generating splitfields. 737 */ 738 rvq = growRuleValue(count, count + 1, rvq, 0); 739 if (rvq == 0) { 740 freeRuleValue(rvq, count); 741 freeValue(rval, 1); 742 freeMappingItem(litem, numItems); 743 return (MAP_INTERNAL_ERROR); 744 } 745 count++; 746 } 747 748 for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) { 749 /* If we run out of values, use empty strings */ 750 if (k >= rval->numVals) { 751 value = emptystr; 752 len = 0; 753 } else { 754 value = rval->val[k].value; 755 len = rval->val[k].length; 756 } 757 res = (splitname == 1)?addCol2RuleValue( 758 vt_string, litem[l].name, value, 759 len, &rvq[count - 1]):0; 760 if (res != -1) 761 res = addCol2RuleValue(vt_string, 762 litem[l].name, value, len, rv); 763 if (res == -1) { 764 freeRuleValue(rvq, count); 765 freeValue(rval, 1); 766 freeMappingItem(litem, numItems); 767 return (MAP_INTERNAL_ERROR); 768 } 769 } 770 } 771 freeValue(rval, 1); 772 rval = 0; 773 freeMappingItem(litem, numItems); 774 litem = 0; 775 numItems = 0; 776 } /* for r < t->numRulesFromLDAP */ 777 778 statP = addSplitFieldValues(t, rvq, rv, count, domain); 779 780 if (rvq) 781 freeRuleValue(rvq, count); 782 783 if (verifyIndexMatch(t, 0, rv, 0, 0) == 0) 784 return (MAP_INDEXLIST_ERROR); 785 return (statP); 786 787 } /* end of buildNISRuleValue */ 788 789 /* 790 * Convert rule-value to datum using namefield information 791 */ 792 datum * 793 ruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) { 794 __nis_value_t *val; 795 datum *value; 796 char *str, *cstr, commentSep[3] = {' ', 0, 0}; 797 char *myself = "ruleValueToDatum"; 798 799 /* No error yet */ 800 *statP = 0; 801 802 /* Return empty datum if no namefield information available */ 803 if (t->e == 0) { 804 if ((value = am(myself, sizeof (*value))) == 0) 805 *statP = MAP_NO_MEMORY; 806 return (value); 807 } 808 809 val = getMappingFormatArray(t->e->element.match.fmt, rv, 810 fa_item, t->e->element.match.numItems, 811 t->e->element.match.item); 812 813 if (val && val->val && val->val->value) { 814 if ((value = am(myself, sizeof (*value))) == 0) { 815 *statP = MAP_NO_MEMORY; 816 freeValue(val, 1); 817 return (0); 818 } 819 820 /* Strip trailing whitespaces */ 821 cstr = (char *)val->val->value + val->val->length; 822 for (; cstr >= (char *)val->val->value && 823 (*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0'); 824 825 if (t->commentChar != '\0' && 826 (str = findVal(N2LCOMMENT, rv, mit_nisplus)) != 0 && 827 *str != '\0') { 828 commentSep[1] = t->commentChar; 829 cstr = scat(myself, F, commentSep, str); 830 if (cstr) { 831 value->dptr = scat(myself, F, 832 val->val->value, cstr); 833 sfree(cstr); 834 } 835 } else { 836 value->dptr = sdup(myself, T, val->val->value); 837 } 838 freeValue(val, 1); 839 if (value->dptr) { 840 value->dsize = strlen(value->dptr); 841 return (value); 842 } else { 843 *statP = MAP_NO_MEMORY; 844 sfree(value); 845 return (0); 846 } 847 } 848 849 *statP = MAP_NAMEFIELD_MATCH_ERROR; 850 return (0); 851 } 852 853 datum * 854 getKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv, 855 int *statP) { 856 int i, j; 857 datum *key = 0; 858 char *str; 859 char *myself = "getKeyFromRuleValue"; 860 861 /* No error yet */ 862 *statP = 0; 863 864 if (rv == 0 || nv == 0) 865 return (0); 866 867 for (i = 0; i < rv->numColumns; i++) { 868 if (rv->colName[i] == 0) 869 continue; 870 if (strcasecmp(N2LKEY, rv->colName[i]) == 0 || 871 strcasecmp(N2LIPKEY, rv->colName[i]) == 0) { 872 if ((*nv = rv->colVal[i].numVals) == 0) 873 return (0); 874 if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) { 875 *statP = MAP_NO_MEMORY; 876 return (0); 877 } 878 for (j = 0; j < *nv; j++) { 879 if ((str = rv->colVal[i].val[j].value) == 0) { 880 key[j].dsize = 0; 881 key[j].dptr = 0; 882 } else { 883 if (verifyIndexMatch(t, 0, 0, 884 rv->colName[i], 885 str) == 0) { 886 key[j].dsize = 0; 887 key[j].dptr = 0; 888 continue; 889 } 890 key[j].dsize = strlen(str); 891 key[j].dptr = am(myself, 892 key[j].dsize + 1); 893 if (key[j].dptr == 0) { 894 *statP = MAP_NO_MEMORY; 895 for (--j; j >= 0; j--) 896 sfree(key[j].dptr); 897 sfree(key); 898 return (0); 899 } 900 bcopy(str, key[j].dptr, key[j].dsize); 901 } 902 } 903 return (key); 904 } 905 } 906 return (0); 907 } 908 909 /* 910 * Get the mapping structure corresponding to `map,domain.' 911 */ 912 __nis_table_mapping_t * 913 mappingFromMap(char *map, char *domain, int *statP) { 914 char *mapPath; 915 __nis_table_mapping_t *t; 916 917 /* No error yet */ 918 *statP = 0; 919 920 /* Construct map,domain. */ 921 if ((mapPath = getFullMapName(map, domain)) == 0) { 922 *statP = MAP_NO_MEMORY; 923 return (0); 924 } 925 926 /* Get the hash table entry for the mapPath */ 927 if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0)) 928 == 0) { 929 *statP = MAP_NO_MAPPING_EXISTS; 930 } 931 sfree(mapPath); 932 return (t); 933 } 934 935 /* 936 * Verify at least one key value obtained from DIT matches the search key 937 * RETURNS: 1 MATCH 938 * 0 NO MATCH 939 * -1 NO KEY FOUND 940 */ 941 static int 942 verifyKey(char *key, __nis_rule_value_t *rv) { 943 int i, j; 944 char *sipkey, *str; 945 946 for (i = 0; i < rv->numColumns; i++) { 947 if (rv->colName[i] == 0) 948 continue; 949 if (strcasecmp(N2LKEY, rv->colName[i]) == 0) { 950 if (rv->colVal[i].val == 0) 951 return (0); 952 for (j = 0; j < rv->colVal[i].numVals; j++) { 953 str = (char *)rv->colVal[i].val[j].value; 954 if (str && strcmp(str, key) == 0) 955 return (1); 956 } 957 return (0); 958 } else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) { 959 if (checkIPaddress(key, strlen(key), &sipkey) > 0) { 960 if (rv->colVal[i].val == 0) 961 return (0); 962 for (j = 0; j < rv->colVal[i].numVals; j++) { 963 str = rv->colVal[i].val[j].value; 964 if (str && strcmp(str, sipkey) == 0) { 965 sfree(sipkey); 966 return (1); 967 } 968 } 969 sfree(sipkey); 970 } 971 return (0); 972 } 973 } 974 return (-1); 975 } 976 977 /* 978 * Read (i.e get and map) a single NIS entry from the LDAP DIT 979 */ 980 bool_t 981 singleReadFromDIT(char *map, char *domain, datum *key, datum *value, 982 int *statP) { 983 __nis_table_mapping_t *t; 984 __nis_rule_value_t *rv_request = 0, *rv_result = 0; 985 __nis_ldap_search_t *ls; 986 __nis_object_dn_t *objectDN = NULL; 987 int i, rc, nr = 0, nv = 0; 988 datum *datval = 0; 989 char *skey, *str, *sipkey; 990 char *myself = "singleReadFromDIT"; 991 992 *statP = SUCCESS; 993 994 if (!map || !domain || !key || !value) { 995 *statP = MAP_PARAM_ERROR; 996 return (FALSE); 997 } 998 999 1000 /* Get the mapping information for the map */ 1001 if ((t = mappingFromMap(map, domain, statP)) == 0) { 1002 /* 1003 * No problem. We don't handle this map and domain. Maybe it's 1004 * handled by a service other than NIS. 1005 */ 1006 return (FALSE); 1007 } 1008 1009 /* NULL-terminated version of datum key for logging */ 1010 if ((skey = am(myself, key->dsize + 1)) == 0) { 1011 *statP = MAP_NO_MEMORY; 1012 return (FALSE); 1013 } 1014 (void) memcpy(skey, key->dptr, key->dsize); 1015 1016 if ((str = getFullMapName(map, domain)) == 0) { 1017 *statP = MAP_NO_MEMORY; 1018 return (FALSE); 1019 } 1020 1021 /* For each alternate mapping */ 1022 for (; t != 0; t = t->next) { 1023 /* Verify objName */ 1024 if (strcmp(str, t->objName) != 0) { 1025 continue; 1026 } 1027 1028 /* Verify if key matches the index */ 1029 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 || 1030 verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0) 1031 continue; 1032 1033 /* Check if rulesFromLDAP are provided */ 1034 if (t->numRulesFromLDAP == 0) { 1035 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1036 "%s: No rulesFromLDAP information available " 1037 "for %s (%s)", myself, t->dbId, map); 1038 continue; 1039 } 1040 1041 /* Convert key into rule-value */ 1042 if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE, 1043 statP)) == 0) { 1044 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1045 "%s: Conversion error %d (NIS to name=value " 1046 "pairs) for NIS key (%s) for %s (%s)", 1047 myself, *statP, skey, t->dbId, map); 1048 continue; 1049 } 1050 /* Convert rule-value into ldap request */ 1051 for (objectDN = t->objectDN; objectDN && 1052 objectDN->read.base; 1053 objectDN = objectDN->next) { 1054 ls = createLdapRequest(t, rv_request, 0, 1, NULL, 1055 objectDN); 1056 if (ls == 0) { 1057 *statP = MAP_CREATE_LDAP_REQUEST_ERROR; 1058 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1059 "%s: Failed to create ldapSearch " 1060 "request for " 1061 "NIS key (%s) for %s (%s) " 1062 "for base %s", 1063 myself, skey, t->dbId, map, 1064 objectDN->read.base); 1065 continue; 1066 } 1067 ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC; 1068 ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC; 1069 /* Query LDAP */ 1070 nr = (ls->isDN)?0:-1; 1071 rv_result = ldapSearch(ls, &nr, 0, statP); 1072 freeLdapSearch(ls); 1073 if (rv_result == 0) { 1074 if (*statP == LDAP_NO_SUCH_OBJECT) { 1075 /* Entry does not exist in */ 1076 /* the ldap server */ 1077 } 1078 continue; 1079 } 1080 freeRuleValue(rv_request, 1); 1081 rv_request = 0; 1082 1083 /* if result > 1, first match will be returned */ 1084 if (nr > 1) { 1085 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1086 "%s: %d ldapSearch results " 1087 "for NIS key (%s) " 1088 "for %s (%s) for base %s. " 1089 "First match will be returned ", 1090 myself, nr, skey, t->dbId, map, 1091 objectDN->read.base); 1092 } 1093 1094 for (i = 0; i < nr; i++) { 1095 /* Convert LDAP data to NIS equivalents */ 1096 *statP = buildNISRuleValue(t, &rv_result[i], 1097 domain); 1098 if (*statP == MAP_INDEXLIST_ERROR) 1099 continue; 1100 1101 if (*statP != SUCCESS) { 1102 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1103 "%s: Conversion error %d (LDAP to " 1104 "name=value pairs) for NIS key (%s) " 1105 "for %s (%s) for base %s", myself, 1106 *statP, skey, 1107 t->dbId, map, objectDN->read.base); 1108 continue; 1109 } 1110 1111 /* 1112 * Check if 'key' from the ldap result matches the key 1113 * provided by our caller 1114 */ 1115 if ((rc = verifyKey(skey, &rv_result[i])) 1116 == -1) { 1117 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1118 "%s: Cannot verify key from ldap " 1119 "result for NIS key (%s) for %s (%s) " 1120 "for base %s", 1121 myself, skey, t->dbId, map, 1122 objectDN->read.base); 1123 continue; 1124 } 1125 1126 if (rc == 1) { 1127 datval = ruleValueToDatum(t, 1128 &rv_result[i], statP); 1129 if (datval == 0) { 1130 logmsg(MSG_NOTIMECHECK, 1131 LOG_WARNING, 1132 "%s: Conversion error %d " 1133 "(name=value pairs to NIS) " 1134 "for NIS key (%s) for %s (%s)" 1135 " for base %s", 1136 myself, 1137 *statP, skey, t->dbId, map, 1138 objectDN->read.base); 1139 continue; 1140 } 1141 if (value) { 1142 value->dptr = datval->dptr; 1143 value->dsize = datval->dsize; 1144 } 1145 sfree(datval); 1146 sfree(skey); 1147 freeRuleValue(rv_result, nr); 1148 rv_result = 0; 1149 *statP = SUCCESS; 1150 1151 /* Free full map name */ 1152 sfree(str); 1153 1154 return (TRUE); 1155 } 1156 } 1157 freeRuleValue(rv_result, nr); 1158 rv_result = 0; 1159 } /* end of for over objectDN */ 1160 1161 if (rv_request != 0) { 1162 freeRuleValue(rv_request, 1); 1163 rv_request = 0; 1164 } 1165 if (rv_result != 0) { 1166 freeRuleValue(rv_result, nr); 1167 rv_result = 0; 1168 } 1169 } 1170 sfree(skey); 1171 *statP = MAP_NO_MATCHING_KEY; 1172 1173 /* Free full map name */ 1174 sfree(str); 1175 1176 return (FALSE); 1177 } 1178 1179 1180 /* 1181 * Maps and writes a single NIS entry to the LDAP DIT 1182 */ 1183 int 1184 singleWriteToDIT(char *map, char *domain, datum *key, datum *value, 1185 bool_t replace) { 1186 __nis_table_mapping_t *t; 1187 __nis_rule_value_t *rv, *frv; 1188 __nis_ldap_search_t *ls; 1189 int statP = SUCCESS, flag; 1190 int nv, nr, i, rc, collapse; 1191 char *dn = 0, *skey, *svalue, *str; 1192 char *myself = "singleWriteToDIT"; 1193 1194 if (!map || !domain || !key || !value) { 1195 return (MAP_PARAM_ERROR); 1196 } 1197 1198 /* Return SUCCESS for empty or whitespace key */ 1199 for (i = 0; i < key->dsize && (key->dptr[i] == 0 || 1200 key->dptr[i] == ' ' || key->dptr[i] == '\t'); i++); 1201 if (i >= key->dsize) 1202 return (SUCCESS); 1203 1204 /* Get the mapping information for the map */ 1205 if ((t = mappingFromMap(map, domain, &statP)) == 0) { 1206 /* 1207 * No problem. We don't handle this map and domain. Maybe it's 1208 * handled by a service other than NIS. 1209 */ 1210 return (statP); 1211 } 1212 1213 /* NULL-terminated version of key and value for logging */ 1214 if ((skey = am(myself, key->dsize + 1)) == 0) 1215 return (MAP_NO_MEMORY); 1216 (void) memcpy(skey, key->dptr, key->dsize); 1217 1218 if ((svalue = am(myself, value->dsize + 1)) == 0) { 1219 sfree(skey); 1220 return (MAP_NO_MEMORY); 1221 } 1222 (void) memcpy(svalue, value->dptr, value->dsize); 1223 1224 if ((str = getFullMapName(map, domain)) == 0) { 1225 sfree(skey); 1226 sfree(svalue); 1227 return (MAP_NO_MEMORY); 1228 } 1229 1230 /* For each alternate mapping */ 1231 for (flag = 0; t != 0; t = t->next) { 1232 /* Verify objName */ 1233 if (strcmp(str, t->objName) != 0) { 1234 continue; 1235 } 1236 1237 /* Verify if key matches the index */ 1238 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 || 1239 verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0) 1240 continue; 1241 1242 /* Check the writespecs */ 1243 if (t->objectDN->write.base == 0) { 1244 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1245 "%s: No baseDN in writespec. Write disabled " 1246 "for %s (%s)", myself, t->dbId, map); 1247 continue; 1248 } 1249 1250 /* Check if rulesToLDAP are provided */ 1251 if (t->numRulesToLDAP == 0) { 1252 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1253 "%s: No rulesToLDAP. Write disabled for " 1254 "%s (%s)", myself, t->dbId, map); 1255 continue; 1256 } 1257 1258 /* Set flag to indicate write is enabled */ 1259 flag = 1; 1260 1261 /* Convert key and value into an array of rule-values */ 1262 if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE, 1263 &statP)) == 0) { 1264 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1265 "%s: Conversion error %d (NIS to name=value " 1266 "pairs) for NIS data (key=%s, value=%s) " 1267 "for %s (%s)", 1268 myself, statP, skey, svalue, t->dbId, map); 1269 sfree(skey); 1270 sfree(svalue); 1271 1272 /* Free full map name */ 1273 sfree(str); 1274 1275 return (statP); 1276 } 1277 1278 /* Convert NIS data to LDAP equivalents for each rule-value */ 1279 for (i = 0; i < nv; i++) { 1280 /* Verify indexlist with name=value pairs */ 1281 if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0) 1282 break; 1283 1284 /* Create LDAP request and LDAP name=value pairs */ 1285 if ((ls = createLdapRequest(t, &rv[i], 1286 0, 0, NULL, NULL)) == 0) { 1287 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1288 "%s: Conversion error (name=value pairs" 1289 " to LDAP) for NIS data " 1290 "(key=%s, value=%s) for %s (%s)", 1291 myself, skey, svalue, t->dbId, map); 1292 freeRuleValue(rv, nv); 1293 sfree(skey); 1294 sfree(svalue); 1295 1296 /* Free full map name */ 1297 sfree(str); 1298 1299 return (MAP_CREATE_LDAP_REQUEST_ERROR); 1300 } 1301 freeLdapSearch(ls); 1302 /* printRuleValue(&rv[i]); */ 1303 } 1304 1305 /* If i < nv then this alternate mapping isn't the one */ 1306 if (i < nv) 1307 continue; 1308 1309 /* 1310 * Merge rule-values with the same DN so that we have 1311 * one ldap write request for each DN 1312 */ 1313 nr = nv; 1314 frv = mergeRuleValueWithSameDN(rv, &nr); 1315 freeRuleValue(rv, nv); 1316 if (frv == 0) { 1317 if (nr == -1) { 1318 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1319 "%s: Unable to merge LDAP write " 1320 "requests to same DN for NIS data " 1321 "(key=%s, value=%s) for %s (%s)", 1322 myself, skey, svalue, t->dbId, map); 1323 statP = MAP_INTERNAL_ERROR; 1324 } else if (nr == 0) { 1325 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1326 "%s: Cannot generate write DN due to " 1327 "missing information for NIS data " 1328 "(key=%s, value=%s) for %s (%s)", 1329 myself, skey, svalue, t->dbId, map); 1330 statP = MAP_NO_DN; 1331 } 1332 sfree(skey); 1333 sfree(svalue); 1334 1335 /* Free full map name */ 1336 sfree(str); 1337 1338 return (statP); 1339 } 1340 1341 /* Write to the LDAP server */ 1342 for (collapse = 0, i = 0; i < nr; i++) { 1343 if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) { 1344 if (replace == FALSE) { 1345 /* ldap add */ 1346 rc = ldapAdd(dn, &frv[i], 1347 t->objectDN->write.attrs, 0); 1348 } else { 1349 /* ldap modify with addFirst set */ 1350 rc = ldapModify(dn, &frv[i], 1351 t->objectDN->write.attrs, 1); 1352 } 1353 1354 /* if we get err=20, collapse and try again */ 1355 if (!collapse && 1356 (rc == LDAP_TYPE_OR_VALUE_EXISTS) && 1357 (collapseRuleValue(&frv[i]) == 1)) { 1358 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1359 "%s: Ignoring values differing " 1360 "in case from NIS data (key=%s," 1361 " value=%s) for (dn: %s) for " 1362 "%s (%s)", myself, skey, 1363 svalue, dn, t->dbId, map); 1364 collapse = 1; 1365 i--; 1366 continue; 1367 } 1368 1369 collapse = 0; 1370 if (rc != LDAP_SUCCESS) { 1371 /* Log error */ 1372 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1373 "%s: %s error %d (%s) for " 1374 "(dn: %s) for NIS data " 1375 "(key=%s, value=%s) " 1376 "for %s (%s)", 1377 myself, (replace == TRUE) ? 1378 "ldapModify" : "ldapAdd", rc, 1379 ldap_err2string(rc), dn, skey, 1380 svalue, t->dbId, map); 1381 1382 /* Dumping failed call may be useful */ 1383 /* printRuleValue(&frv[i]); */ 1384 1385 /* 1386 * Return the error code and let wrapper 1387 * sort out if mapping should continue 1388 * or abort. 1389 */ 1390 statP = rc; 1391 sfree(skey); 1392 sfree(svalue); 1393 freeRuleValue(frv, nr); 1394 1395 /* Free full map name */ 1396 sfree(str); 1397 1398 return (statP); 1399 } 1400 } 1401 } 1402 1403 freeRuleValue(frv, nr); 1404 } 1405 1406 sfree(skey); 1407 sfree(svalue); 1408 1409 /* Free full map name */ 1410 sfree(str); 1411 1412 return ((flag)?SUCCESS:MAP_WRITE_DISABLED); 1413 } 1414 1415 suc_code 1416 collapseRuleValue(__nis_rule_value_t *rv) { 1417 int i, j, k, flag; 1418 1419 /* Using 'val' to appease cstyle's 80 chars/line limit */ 1420 __nis_value_t *val; 1421 1422 for (i = 0, flag = 0; i < rv->numAttrs; i++) { 1423 val = &rv->attrVal[i]; 1424 for (j = 1; j < val->numVals; j++) { 1425 for (k = 0; k < j; k++) { 1426 if (val->val[j].length != val->val[k].length) 1427 continue; 1428 if (val->val[k].length == 0) 1429 continue; 1430 if (strncasecmp(val->val[j].value, 1431 val->val[k].value, 1432 val->val[j].length) != 0) 1433 continue; 1434 flag = 1; 1435 sfree(val->val[j].value); 1436 1437 #ifdef ORDER_NOT_IMPORTANT 1438 val->val[j--] = val->val[--val->numVals]; 1439 #else 1440 /* Order needs to be maintained */ 1441 for (k = j + 1; k < val->numVals; k++) 1442 val->val[k - 1] = val->val[k]; 1443 j--; 1444 val->numVals--; 1445 #endif 1446 break; 1447 } 1448 } 1449 } 1450 return (flag); 1451 } 1452 1453 /* ObjectClass lookup table */ 1454 static struct { 1455 const char *attrType; 1456 const char *objectClass; 1457 } oc_lookup[] = { 1458 { "o", "objectclass=organization"}, 1459 { "organizationname", "objectclass=organization"}, 1460 { "2.5.4.10", "objectclass=organization"}, 1461 { "ou", "objectclass=organizationalunit"}, 1462 { "organizationalunitname", "objectclass=organizationalunit"}, 1463 { "2.5.4.11", "objectclass=organizationalunit"}, 1464 { "c", "objectclass=country"}, 1465 { "countryname", "objectclass=country"}, 1466 { "2.5.4.6", "objectclass=country"}, 1467 { "dc", "objectclass=domain"}, 1468 { "domaincomponent", "objectclass=domain"}, 1469 { "0.9.2342.19200300.100.1.25", "objectclass=domain"}, 1470 { "nismapname", "objectclass=nismap"}, 1471 { "1.3.6.1.1.1.1.26", "objectclass=nismap"}, 1472 { "automountmapname", "objectclass=automountmap"}, 1473 { "1.3.6.1.1.1.1.31", "objectclass=automountmap"}, 1474 { 0, 0} 1475 }; 1476 1477 /* 1478 * Returns the name of the objectclass to which the object 1479 * represented by the given 'rdn' will most likely belong to. 1480 * The return value is in static memory so it should not be 1481 * freed 1482 */ 1483 const char * 1484 getObjectClass(char *rdn) { 1485 1486 char *attrtype, *p; 1487 int len, i; 1488 1489 /* Skip leading whitespaces */ 1490 for (p = rdn; *p == ' ' || *p == '\t'; p++); 1491 if (*p == '\0') 1492 return (0); 1493 attrtype = p; 1494 1495 /* Find '=' */ 1496 if ((p = strchr(attrtype, '=')) == 0 || p == attrtype || 1497 *(p - 1) == '\\') 1498 return (0); 1499 1500 /* 1501 * Skip trailing whitespaces in attrtype 1502 * Don't worry, p won't decrease beyond attrtype 1503 */ 1504 for (--p; *p == ' ' || *p == '\t'; p--); 1505 len = p - attrtype + 1; 1506 1507 for (i = 0; oc_lookup[i].attrType; i++) 1508 if (!strncasecmp(oc_lookup[i].attrType, attrtype, len)) 1509 /* Check length is right */ 1510 if (len == strlen(oc_lookup[i].attrType)) 1511 return (oc_lookup[i].objectClass); 1512 1513 return (0); 1514 } 1515 1516 /* 1517 * Split 'dn' into rdn and parentdn based on the first 1518 * occurrence of unescaped 'comma' or 'semicolon'. rdn 1519 * lies on the LHS while parentdn lies on the RHS of the 1520 * split. If none found, then an empty string ("") is 1521 * assigned to parentdn 1522 */ 1523 int 1524 splitDN(char *dn, char **rdn, char **parentdn) { 1525 char *value, *name; 1526 char *myself = "splitDN"; 1527 1528 if ((name = sdup(myself, T, dn)) == 0) 1529 return (-1); 1530 1531 for (value = name; *value != '\0'; value++) { 1532 if (*value == ',' || *value == ';') 1533 if (value == name || *(value - 1) != '\\') 1534 break; 1535 } 1536 1537 if (*value != '\0') { 1538 *value = '\0'; 1539 value++; 1540 } else 1541 value = 0; 1542 1543 if (parentdn) { 1544 if ((*parentdn = sdup(myself, T, value)) == 0) { 1545 sfree(name); 1546 return (-1); 1547 } 1548 } 1549 if (rdn) 1550 *rdn = name; 1551 else 1552 sfree(name); 1553 1554 return (1); 1555 } 1556 1557 /* 1558 * FUNCTION : makeNISObject() 1559 * 1560 * DESCRIPTION: Sets up a nis Object in the DIT. 1561 * 1562 * GIVEN : 1563 * Case 1: Both 'domain' and 'dn' are non-NULL 1564 * Create nisDomainObject with the given information 1565 * Case 2: Only 'domain' is non-NULL 1566 * Obtain the 'dn' from the nisLDAPdomainContext list 1567 * Create nisDomainObject with the above information 1568 * Case 3: Only 'dn' is non-NULL 1569 * Create an object with the 'dn' 1570 * Here we guess the objectclass attribute, based on 1571 * oc_lookup table 1572 * Case 4: Both 'domain' and 'dn' are NULL 1573 * Error 1574 * 1575 * RETURNS : SUCCESS = It worked 1576 * FAILURE = There was a problem. 1577 */ 1578 suc_code 1579 makeNISObject(char *domain, char *dn) { 1580 __nis_rule_value_t *rv; 1581 __nis_ldap_search_t *ls; 1582 int i, rc, nr, add_rc; 1583 char *val; 1584 char *myself = "makeNISObject"; 1585 1586 if (!dn && !domain) 1587 return (FAILURE); 1588 1589 /* 1590 * If only 'domain' name is provided, then 1591 * try to find dn from the nisLDAPdomainContext 1592 * list generated by the parser 1593 */ 1594 if (!dn) { 1595 for (i = 0; i < ypDomains.numDomains; i++) { 1596 if (ypDomains.domainLabels[i] == 0) 1597 continue; 1598 if (strcasecmp(domain, ypDomains.domainLabels[i]) 1599 == 0) { 1600 dn = ypDomains.domains[i]; 1601 break; 1602 } 1603 } 1604 if (!dn) 1605 return (FAILURE); 1606 } 1607 1608 /* 1609 * If only 'dn' is given, then it means that the 1610 * caller simply wants to a create an entry for 1611 * that 'dn'. 1612 * 1613 * If 'domain' is given, then check if the 'dn' 1614 * has already been set up as a nis domain object. 1615 * If not, see if we can make it become one. 1616 */ 1617 if (domain) { 1618 /* 1619 * Check to see if the nis domain object has 1620 * already been set up 1621 */ 1622 ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0, 1623 "objectclass=*", 0, 0, 0); 1624 if (ls == 0) { 1625 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1626 "%s: Unable to create ldapSearch " 1627 "request for dn: %s", myself, dn); 1628 return (FAILURE); 1629 } 1630 nr = -1; 1631 rv = ldapSearch(ls, &nr, 0, &rc); 1632 freeLdapSearch(ls); 1633 if (rc == LDAP_SUCCESS) { 1634 val = findVal("nisDomain", rv, mit_ldap); 1635 if (val != NULL) { 1636 /* 1637 * Yes, nis domain object found. Check 1638 * to see if the domain names match. 1639 * If so, we are done. If not, log 1640 * a warning message, and return SUCCESS. 1641 */ 1642 if (strcasecmp(val, domain) == 0) { 1643 freeRuleValue(rv, nr); 1644 return (SUCCESS); 1645 } else { 1646 logmsg(MSG_NOTIMECHECK, 1647 LOG_WARNING, 1648 "%s: Entry (dn: %s) already " 1649 "contains a nis domain name " 1650 "(%s). The domain name (%s) " 1651 "is not added.", 1652 myself, dn, val, domain); 1653 freeRuleValue(rv, nr); 1654 return (SUCCESS); 1655 } 1656 } else { 1657 freeRuleValue(rv, nr); 1658 /* 1659 * Entry for the 'dn' exists, but it 1660 * is not a nis domain object yet. 1661 * Add the nisDoamin attribute and 1662 * the nisDomainObject objectclass to 1663 * the entry. 1664 */ 1665 if ((rv = initRuleValue(1, 0)) == 0) 1666 return (FAILURE); 1667 1668 if (addSAttr2RuleValue("nisDomain", 1669 domain, rv) == -1) { 1670 freeRuleValue(rv, 1); 1671 return (FAILURE); 1672 } 1673 rc = ldapModify(dn, rv, 1674 "objectclass=nisDomainObject", 1675 0); 1676 freeRuleValue(rv, 1); 1677 if (rc == LDAP_SUCCESS) { 1678 logmsg(MSG_NOTIMECHECK, 1679 LOG_INFO, 1680 "%s: entry (dn: %s) " 1681 "modified to be an " 1682 "nis domain object", 1683 myself, dn); 1684 return (SUCCESS); 1685 } else { 1686 logmsg(MSG_NOTIMECHECK, 1687 LOG_ERR, 1688 "%s: unable to modify " 1689 "entry (dn: %s) to be " 1690 "a nis domain object: " 1691 "ldapModify error %d (%s)", 1692 myself, dn, rc, 1693 ldap_err2string(rc)); 1694 return (FAILURE); 1695 } 1696 } 1697 } else { /* search for 'dn' failed */ 1698 freeRuleValue(rv, nr); 1699 1700 /* 1701 * It is OK if no such object, otherwise 1702 * log an error. 1703 */ 1704 if (rc != LDAP_NO_SUCH_OBJECT) { 1705 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1706 "%s: unable to retrieve " 1707 "entry (dn: %s): " 1708 "ldapSearch error %d (%s)", 1709 myself, dn, rc, 1710 ldap_err2string(rc)); 1711 return (FAILURE); 1712 } 1713 } 1714 1715 /* 1716 * If the 'dn' is actually the naming context of 1717 * the DIT, we should be able to make it a nis domain 1718 * object without worrying about missing parent 1719 * entries. If unable to add the entry for the 'dn' 1720 * due to missing parent entries, fall through 1721 * to create them and then add the nis domain object. 1722 */ 1723 if (addNISObject(domain, dn, &add_rc) == SUCCESS) 1724 return (SUCCESS); 1725 else if (add_rc != LDAP_NO_SUCH_OBJECT) 1726 return (FAILURE); 1727 } 1728 1729 /* Create parent */ 1730 if (addParent(dn, NULL) == FAILURE) 1731 return (FAILURE); 1732 1733 if (addNISObject(domain, dn, NULL) == FAILURE) 1734 return (FAILURE); 1735 1736 return (SUCCESS); 1737 } 1738 1739 suc_code 1740 addParent(char *dn, char **attr) { 1741 __nis_rule_value_t *rv; 1742 __nis_ldap_search_t *ls; 1743 int rc, nr; 1744 char *parentdn = 0, *rdn = 0; 1745 char *myself = "addParent"; 1746 1747 /* Obtain parentdn */ 1748 if (splitDN(dn, &rdn, &parentdn) == -1) 1749 return (FAILURE); 1750 if (!parentdn) { 1751 sfree(rdn); 1752 return (FAILURE); 1753 } 1754 1755 /* Check if parentdn exists */ 1756 ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0, 1757 "objectclass=*", 0, 0, 0); 1758 if (ls == 0) { 1759 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1760 "%s: Unable to create ldapSearch request for " 1761 "parent (dn: %s) of (dn: %s)", 1762 myself, parentdn, dn); 1763 sfree(parentdn); 1764 sfree(rdn); 1765 return (FAILURE); 1766 } 1767 nr = -1; 1768 rv = ldapSearch(ls, &nr, 0, &rc); 1769 freeLdapSearch(ls); 1770 freeRuleValue(rv, nr); 1771 1772 /* Create parent if it doesn't exists */ 1773 if (rc == LDAP_NO_SUCH_OBJECT) { 1774 if (makeNISObject(0, parentdn) == FAILURE) { 1775 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1776 "%s: Unable to create parent (dn: %s) of " 1777 "(dn: %s) in the DIT", myself, parentdn, dn); 1778 sfree(parentdn); 1779 sfree(rdn); 1780 return (FAILURE); 1781 } 1782 } 1783 sfree(parentdn); 1784 1785 if (attr && rdn) 1786 *attr = (char *)getObjectClass(rdn); 1787 sfree(rdn); 1788 1789 return (SUCCESS); 1790 } 1791 1792 1793 1794 /* 1795 * FUNCTION : is_fatal_error() 1796 * 1797 * DESCRIPTION: Works out if a failed mapping operation should be retried. 1798 * 1799 * INPUTS : Result code from operation 1800 * 1801 * OUTPUTS : TRUE = Fatal error, don't retry. 1802 * FALSE = Temporary error, retry. 1803 */ 1804 bool_t 1805 is_fatal_error(int res) 1806 { 1807 1808 if (0 > res) 1809 /* An internal mapping error. Not going to go away. */ 1810 return (TRUE); 1811 1812 switch (res) { 1813 case (LDAP_PROTOCOL_ERROR): 1814 case (LDAP_TIMELIMIT_EXCEEDED): 1815 case (LDAP_PARTIAL_RESULTS): 1816 case (LDAP_BUSY): 1817 case (LDAP_UNAVAILABLE): 1818 case (LDAP_UNWILLING_TO_PERFORM): 1819 case (LDAP_OTHER): 1820 case (LDAP_SERVER_DOWN): 1821 case (LDAP_LOCAL_ERROR): 1822 case (LDAP_TIMEOUT): 1823 case (LDAP_NO_MEMORY): 1824 /* Probably worth a retry */ 1825 return (FALSE); 1826 1827 default: 1828 return (TRUE); 1829 } 1830 } 1831 1832 /* 1833 * FUNCTION : addNISObject() 1834 * 1835 * DESCRIPTION: Add a nis Object in the DIT. 1836 * 1837 * GIVEN : 1838 * Case 1: 'dn' is NULL 1839 * Error 1840 * Case 2: 'domain' is non-NULL 1841 * Create nisDomainObject with the given information 1842 * Case 3: 'domain' is NULL 1843 * Create an object with the 'dn' 1844 * Here we guess the objectclass attribute, based on 1845 * oc_lookup table 1846 * 1847 * RETURNS : SUCCESS = It worked 1848 * FAILURE = There was a problem. If the ldap add 1849 * operation failed, ldap_rc will be set 1850 * to the ldap error code. 1851 */ 1852 suc_code 1853 addNISObject(char *domain, char *dn, int *ldap_rc) { 1854 __nis_rule_value_t *rv; 1855 int rc; 1856 char *objClassAttrs = NULL, *attrs; 1857 char *value, *svalue, *rdn = NULL; 1858 char *myself = "addNISObject"; 1859 1860 if (!dn) 1861 return (FAILURE); 1862 1863 if ((rv = initRuleValue(1, 0)) == 0) 1864 return (FAILURE); 1865 1866 if (ldap_rc) 1867 *ldap_rc = -1; 1868 1869 /* 1870 * Add name=value pairs from RDN. Although this is not required 1871 * for SunOne Directory Server, during openldap interoperabilty 1872 * tests, it was found out that openldap server returned object 1873 * class violation errors if MUST attributes were not specified 1874 * explicitly. 1875 */ 1876 if (splitDN(dn, &rdn, 0) == -1) 1877 return (FAILURE); 1878 if (rdn != NULL) { 1879 objClassAttrs = (char *)getObjectClass(rdn); 1880 if (objClassAttrs == NULL) { 1881 sfree(rdn); 1882 return (FAILURE); 1883 } 1884 1885 /* 1886 * RDN can be composed of multiple name=value pairs 1887 * concatenated by '+'. Hence, we need to determine each 1888 * pair and add it to 'rv' 1889 */ 1890 for (value = rdn, svalue = NULL; *value != '\0'; value++) { 1891 if (*value == '+') { 1892 /* Make sure it's not escaped */ 1893 if (value == rdn || *(value - 1) != '\\') { 1894 /* 1895 * We are at the start of the new 1896 * pair. 'svalue' now contains the 1897 * value for the previous pair. Add 1898 * the previous pair to 'rv' 1899 */ 1900 *value = '\0'; 1901 if (svalue && 1902 addSAttr2RuleValue(rdn, svalue, rv) 1903 == -1) { 1904 sfree(rdn); 1905 freeRuleValue(rv, 1); 1906 return (FAILURE); 1907 } 1908 svalue = NULL; 1909 rdn = value + 1; 1910 continue; 1911 } 1912 } 1913 1914 if (*value == '=') { 1915 if (value == rdn || *(value - 1) != '\\') { 1916 /* 1917 * 'rdn' now contains the name. 1918 * Whatever follows till the next 1919 * unescaped '+' or '\0' is the 1920 * value for this pair. 1921 */ 1922 *value = '\0'; 1923 svalue = value + 1; 1924 continue; 1925 } 1926 } 1927 } 1928 1929 /* 1930 * End of String. Add the previous name=value pair to 'rv' 1931 */ 1932 if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) { 1933 sfree(rdn); 1934 freeRuleValue(rv, 1); 1935 return (FAILURE); 1936 } 1937 sfree(rdn); 1938 } else /* rdn == NULL */ 1939 return (FAILURE); 1940 1941 /* Create the entry */ 1942 if (domain) { 1943 if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) { 1944 freeRuleValue(rv, 1); 1945 return (FAILURE); 1946 } 1947 attrs = scat(myself, F, "objectclass=nisdomainobject,", 1948 objClassAttrs); 1949 if (!attrs) { 1950 freeRuleValue(rv, 1); 1951 return (FAILURE); 1952 } 1953 rc = ldapAdd(dn, rv, attrs, 0); 1954 sfree(attrs); 1955 } else { 1956 rc = ldapAdd(dn, rv, objClassAttrs, 0); 1957 } 1958 1959 if (rc == LDAP_SUCCESS) 1960 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1961 "%s: Entry (dn: %s) added to DIT", 1962 myself, dn); 1963 else if (rc == LDAP_ALREADY_EXISTS) 1964 /* Treat this as success */ 1965 rc = LDAP_SUCCESS; 1966 else 1967 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1968 "%s: ldapAdd error %d (%s) for (dn: %s)", 1969 myself, rc, ldap_err2string(rc), dn); 1970 1971 freeRuleValue(rv, 1); 1972 if (ldap_rc) 1973 *ldap_rc = rc; 1974 return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE); 1975 } 1976