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