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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <strings.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <errno.h> 30 #include <stdio.h> 31 #include <rpcsvc/nis.h> 32 #include <rpc/xdr.h> 33 34 #include "ldap_util.h" 35 #include "ldap_attr.h" 36 #include "ldap_ruleval.h" 37 #include "ldap_op.h" 38 #include "ldap_map.h" 39 #include "ldap_glob.h" 40 #include "ldap_xdr.h" 41 #include "ldap_val.h" 42 43 /* From yptol/dit_access_utils.h */ 44 #define N2LKEY "rf_key" 45 #define N2LIPKEY "rf_ipkey" 46 47 __nis_hash_table_mt ldapMappingList = NIS_HASH_TABLE_MT_INIT; 48 extern int yp2ldap; 49 50 51 int 52 setColumnNames(__nis_table_mapping_t *t) { 53 int i, j, nic, noc; 54 char **col; 55 zotypes type; 56 char *myself = "setColumnNames"; 57 58 if (t == 0) 59 return (0); 60 61 type = t->objType; 62 col = t->column; 63 nic = (col != 0) ? t->numColumns : -1; 64 65 t->objType = NIS_BOGUS_OBJ; 66 t->obj = 0; 67 68 /* 69 * If it's a table object, but there are no translation rules, 70 * this mapping is for the table object itself. In that case, 71 * we throw away the column names (if any). 72 */ 73 if (t->objType == NIS_TABLE_OBJ && t->numRulesFromLDAP == 0 && 74 t->numRulesToLDAP == 0) { 75 for (i = 0; i < t->numColumns; i++) 76 sfree(t->column[i]); 77 sfree(t->column); 78 t->column = 0; 79 t->numColumns = 0; 80 noc = 0; 81 } 82 83 /* 84 * Verify that all column names found by the parser 85 * are present in the actual column list. 86 */ 87 if (verbose) { 88 for (i = 0, noc = 0; i < nic; i++) { 89 int found = 0; 90 91 if (col[i] == 0) 92 continue; 93 /* Skip the 'zo_*' special column names */ 94 if (isObjAttrString(col[i])) 95 continue; 96 for (j = 0; j < t->numColumns; j++) { 97 if (strcmp(col[i], t->column[j]) == 0) { 98 noc++; 99 found = 1; 100 break; 101 } 102 } 103 if (!found) { 104 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 105 "%s: No column \"%s\" in \"%s\"", 106 myself, NIL(col[i]), NIL(t->objName)); 107 } 108 } 109 } 110 111 /* Remove any setup by the parser */ 112 for (i = 0; i < nic; i++) { 113 sfree(col[i]); 114 } 115 sfree(col); 116 117 return (0); 118 } 119 120 void 121 freeSingleObjAttr(__nis_obj_attr_t *attr) { 122 if (attr == 0) 123 return; 124 125 sfree(attr->zo_owner); 126 sfree(attr->zo_group); 127 sfree(attr->zo_domain); 128 sfree(attr); 129 } 130 131 void 132 freeObjAttr(__nis_obj_attr_t **attr, int numAttr) { 133 int i; 134 135 if (attr == 0) 136 return; 137 138 for (i = 0; i < numAttr; i++) { 139 freeSingleObjAttr(attr[i]); 140 } 141 142 sfree(attr); 143 } 144 145 __nis_obj_attr_t * 146 cloneObjAttr(__nis_obj_attr_t *old) { 147 __nis_obj_attr_t *new; 148 char *myself = "cloneObjAttr"; 149 150 if (old == 0) 151 return (0); 152 153 new = am(myself, sizeof (*new)); 154 if (new == 0) 155 return (0); 156 157 new->zo_owner = sdup(myself, T, old->zo_owner); 158 if (new->zo_owner == 0 && old->zo_owner != 0) 159 goto cleanup; 160 161 new->zo_group = sdup(myself, T, old->zo_group); 162 if (new->zo_group == 0 && old->zo_group != 0) 163 goto cleanup; 164 165 new->zo_domain = sdup(myself, T, old->zo_domain); 166 if (new->zo_domain == 0 && old->zo_domain != 0) 167 goto cleanup; 168 169 new->zo_access = old->zo_access; 170 new->zo_ttl = old->zo_ttl; 171 172 return (new); 173 174 cleanup: 175 freeSingleObjAttr(new); 176 177 return (0); 178 } 179 180 181 /* 182 * Obtain NIS+ entries (in the form of db_query's) from the supplied table 183 * mapping and db_query. 184 * 185 * If 'qin' is NULL, enumeration is desired. 186 * 187 * On exit, '*numQueries' contains the number of (db_query *)'s in the 188 * return array, '*ldapStat' the LDAP operation status, and '*objAttr' 189 * a pointer to an array (of '*numQueries elements) of object attributes 190 * (zo_owner, etc.). If no object attributes were retrieved, '*objAttr' 191 * is NULL; any and all of the (*objAttr)[i]'s may be NULL. 192 */ 193 db_query ** 194 mapFromLDAP(__nis_table_mapping_t *t, db_query *qin, int *numQueries, 195 char *dbId, int *ldapStat, __nis_obj_attr_t ***objAttr) { 196 __nis_table_mapping_t **tp; 197 db_query **q; 198 __nis_rule_value_t *rv; 199 __nis_ldap_search_t *ls; 200 int n, numVals, numMatches = 0; 201 int stat; 202 __nis_obj_attr_t **attr; 203 char *myself = "mapFromLDAP"; 204 205 if (ldapStat == 0) 206 ldapStat = &stat; 207 208 if (t == 0 || numQueries == 0) { 209 *ldapStat = LDAP_PARAM_ERROR; 210 return (0); 211 } 212 213 /* Select the correct table mapping(s) */ 214 tp = selectTableMapping(t, qin, 0, 0, dbId, &numMatches); 215 if (tp == 0 || numMatches <= 0) { 216 /* 217 * Not really an error; just no matching mapping 218 * for the query. 219 */ 220 *ldapStat = LDAP_SUCCESS; 221 return (0); 222 } 223 224 q = 0; 225 attr = 0; 226 227 /* For each mapping */ 228 for (numVals = 0, n = 0; n < numMatches; n++) { 229 db_query **qt; 230 int i, nqt = 0, filterOnQin, res = 0; 231 232 t = tp[n]; 233 234 if (qin != 0) { 235 rv = buildNisPlusRuleValue(t, qin, 0); 236 if (rv != 0) { 237 /* 238 * Depending on the value of res, we shall 239 * proceed to next table mapping. 240 */ 241 ls = createLdapRequest(t, rv, 0, 1, &res, NULL); 242 } 243 else 244 ls = 0; 245 } else { 246 /* Build enumeration request */ 247 rv = 0; 248 ls = createLdapRequest(t, 0, 0, 1, NULL, NULL); 249 } 250 251 freeRuleValue(rv, 1); 252 253 if (ls == 0) { 254 /* 255 * if the res is NP_LDAP_RULES_NO_VALUE, that means we 256 * have enough NIS+ columns for the rules to produce 257 * values, but none of them did, so continue to the 258 * next table mapping. Otherwise do cleanup and return 259 * error. 260 */ 261 if (res == NP_LDAP_RULES_NO_VALUE) 262 continue; 263 for (i = 0; i < numVals; i++) 264 freeQuery(q[i]); 265 sfree(q); 266 free(tp); 267 *ldapStat = LDAP_OPERATIONS_ERROR; 268 return (0); 269 } 270 271 /* Query LDAP */ 272 nqt = (ls->isDN || qin != 0) ? 0 : -1; 273 rv = ldapSearch(ls, &nqt, 0, ldapStat); 274 275 /* 276 * If qin != 0, then we need to make sure that the 277 * LDAP search is filtered so that only entries that 278 * are compatible with 'qin' are retained. This will 279 * happen automatically if we do a DN search (in which 280 * case, no need to filter on 'qin'). 281 */ 282 if (ls->isDN || qin == 0) 283 filterOnQin = 0; 284 else 285 filterOnQin = 1; 286 287 freeLdapSearch(ls); 288 289 /* Convert rule-values to db_query's */ 290 if (rv != 0 && nqt > 0) { 291 int nrv = nqt; 292 __nis_obj_attr_t **at = 0; 293 294 qt = ruleValue2Query(t, rv, 295 (filterOnQin) ? qin : 0, &at, &nqt); 296 freeRuleValue(rv, nrv); 297 298 if (qt != 0 && q == 0) { 299 q = qt; 300 attr = at; 301 numVals = nqt; 302 } else if (qt != 0) { 303 db_query **tmp; 304 __nis_obj_attr_t **atmp; 305 306 /* Extend the 'q' array */ 307 tmp = realloc(q, 308 (numVals+nqt) * sizeof (q[0])); 309 /* ... and the 'attr' array */ 310 atmp = realloc(attr, 311 (numVals+nqt) * sizeof (attr[0])); 312 if (tmp == 0 || atmp == 0) { 313 logmsg(MSG_NOMEM, LOG_ERR, 314 "%s: realloc(%d) => NULL", 315 myself, 316 (numVals+nqt) * sizeof (q[0])); 317 for (i = 0; i < numVals; i++) 318 freeQuery(q[i]); 319 for (i = 0; i < nqt; i++) 320 freeQuery(qt[i]); 321 sfree(tmp); 322 sfree(atmp); 323 sfree(q); 324 sfree(qt); 325 sfree(tp); 326 freeObjAttr(at, nqt); 327 freeObjAttr(attr, numVals); 328 *ldapStat = LDAP_NO_MEMORY; 329 return (0); 330 } 331 q = tmp; 332 attr = atmp; 333 /* Add the results for this 't' */ 334 (void) memcpy(&q[numVals], qt, 335 nqt * sizeof (qt[0])); 336 (void) memcpy(&attr[numVals], at, 337 nqt * sizeof (at[0])); 338 numVals += nqt; 339 340 sfree(qt); 341 sfree(at); 342 } 343 } 344 } 345 346 *numQueries = numVals; 347 if (objAttr != 0) 348 *objAttr = attr; 349 else 350 freeObjAttr(attr, numVals); 351 sfree(tp); 352 353 return (q); 354 } 355 356 /* 357 * Add the object attributes (zo_owner, etc.) to the rule-value 'rv'. 358 * Returns a pointer to the (possibly newly allocated) rule-value, 359 * or NULL in case of failure. If not returning 'rvIn', the latter 360 * will have been freed. 361 */ 362 __nis_rule_value_t * 363 addObjAttr2RuleValue(nis_object *obj, __nis_rule_value_t *rvIn) { 364 __nis_rule_value_t *rv; 365 char abuf[2 * sizeof (obj->zo_access) + 1]; 366 char tbuf[2 * sizeof (obj->zo_ttl) + 1]; 367 368 if (obj == 0) 369 return (0); 370 371 if (rvIn != 0) { 372 rv = rvIn; 373 } else { 374 rv = initRuleValue(1, 0); 375 if (rv == 0) 376 return (0); 377 } 378 379 if (obj->zo_owner != 0) { 380 if (addSCol2RuleValue("zo_owner", obj->zo_owner, rv) != 0) { 381 freeRuleValue(rv, 1); 382 return (0); 383 } 384 } 385 386 if (obj->zo_group != 0) { 387 if (addSCol2RuleValue("zo_group", obj->zo_group, rv) != 0) { 388 freeRuleValue(rv, 1); 389 return (0); 390 } 391 } 392 393 if (obj->zo_domain != 0) { 394 if (addSCol2RuleValue("zo_domain", obj->zo_domain, rv) != 0) { 395 freeRuleValue(rv, 1); 396 return (0); 397 } 398 } 399 400 (void) memset(abuf, 0, sizeof (abuf)); 401 (void) memset(tbuf, 0, sizeof (tbuf)); 402 403 sprintf(abuf, "%x", obj->zo_access); 404 sprintf(tbuf, "%x", obj->zo_ttl); 405 406 if (addSCol2RuleValue("zo_access", abuf, rv) != 0) { 407 freeRuleValue(rv, 1); 408 return (0); 409 } 410 if (addSCol2RuleValue("zo_ttl", tbuf, rv) != 0) { 411 freeRuleValue(rv, 1); 412 return (0); 413 } 414 415 return (rv); 416 } 417 418 /* 419 * Returns a pointer to (NOT a copy of) the value for the specified 420 * column 'col' in the rule-value 'rv'. 421 */ 422 __nis_value_t * 423 findColValue(char *col, __nis_rule_value_t *rv) { 424 int i; 425 426 if (col == 0 || rv == 0 || rv->numColumns <= 0) 427 return (0); 428 429 for (i = 0; i < rv->numColumns; i++) { 430 if (strcmp(col, rv->colName[i]) == 0) 431 return (&rv->colVal[i]); 432 } 433 434 return (0); 435 } 436 437 /* 438 * Return the NIS+ object attributes (if any) in the rule-value 'rv'. 439 */ 440 __nis_obj_attr_t * 441 ruleValue2ObjAttr(__nis_rule_value_t *rv) { 442 __nis_obj_attr_t *attr; 443 __nis_value_t *val; 444 char *myself = "ruleValue2ObjAttr"; 445 446 if (rv == 0 || rv->numColumns <= 0) 447 return (0); 448 449 attr = am(myself, sizeof (*attr)); 450 451 if ((val = findColValue("zo_owner", rv)) != 0 && 452 val->type == vt_string && val->numVals == 1 && 453 val->val[0].value != 0) { 454 attr->zo_owner = sdup(myself, T, val->val[0].value); 455 if (attr->zo_owner == 0) { 456 freeSingleObjAttr(attr); 457 return (0); 458 } 459 } 460 461 if ((val = findColValue("zo_group", rv)) != 0 && 462 val->type == vt_string && val->numVals == 1 && 463 val->val[0].value != 0) { 464 attr->zo_group = sdup(myself, T, val->val[0].value); 465 if (attr->zo_group == 0) { 466 freeSingleObjAttr(attr); 467 return (0); 468 } 469 } 470 471 if ((val = findColValue("zo_domain", rv)) != 0 && 472 val->type == vt_string && val->numVals == 1 && 473 val->val[0].value != 0) { 474 attr->zo_domain = sdup(myself, T, val->val[0].value); 475 if (attr->zo_domain == 0) { 476 freeSingleObjAttr(attr); 477 return (0); 478 } 479 } 480 481 if ((val = findColValue("zo_access", rv)) != 0 && 482 val->type == vt_string && val->numVals == 1 && 483 val->val[0].value != 0) { 484 if (sscanf(val->val[0].value, "%x", &attr->zo_access) != 1) { 485 freeSingleObjAttr(attr); 486 return (0); 487 } 488 } 489 490 if ((val = findColValue("zo_ttl", rv)) != 0 && 491 val->type == vt_string && val->numVals == 1 && 492 val->val[0].value != 0) { 493 if (sscanf(val->val[0].value, "%x", &attr->zo_ttl) != 1) { 494 freeSingleObjAttr(attr); 495 return (0); 496 } 497 } 498 499 return (attr); 500 } 501 502 /* 503 * If the supplied string is one of the object attributes, return one. 504 * Otherwise, return zero. 505 */ 506 int 507 isObjAttrString(char *str) { 508 if (str == 0) 509 return (0); 510 511 if (strcmp("zo_owner", str) == 0 || 512 strcmp("zo_group", str) == 0 || 513 strcmp("zo_domain", str) == 0 || 514 strcmp("zo_access", str) == 0 || 515 strcmp("zo_ttl", str) == 0) 516 return (1); 517 else 518 return (0); 519 } 520 521 522 /* 523 * If the supplied value is one of the object attribute strings, return 524 * a pointer to the string. Otherwise, return NULL. 525 */ 526 char * 527 isObjAttr(__nis_single_value_t *val) { 528 if (val == 0 || val->length <= 0 || val->value == 0) 529 return (0); 530 531 if (isObjAttrString(val->value)) 532 return (val->value); 533 else 534 return (0); 535 } 536 537 int 538 setObjAttrField(char *attrName, __nis_single_value_t *val, 539 __nis_obj_attr_t **objAttr) { 540 __nis_obj_attr_t *attr; 541 char *myself = "setObjAttrField"; 542 543 if (attrName == 0 || val == 0 || objAttr == 0 || 544 val->value == 0 || val->length <= 0) 545 return (-1); 546 547 if (*objAttr != 0) { 548 attr = *objAttr; 549 } else { 550 attr = am(myself, sizeof (*attr)); 551 if (attr == 0) 552 return (-2); 553 *objAttr = attr; 554 } 555 556 if (strcmp("zo_owner", attrName) == 0) { 557 if (attr->zo_owner == 0) { 558 attr->zo_owner = sdup(myself, T, val->value); 559 if (attr->zo_owner == 0) 560 return (-11); 561 } 562 } else if (strcmp("zo_group", attrName) == 0) { 563 if (attr->zo_group == 0) { 564 attr->zo_group = sdup(myself, T, val->value); 565 if (attr->zo_group == 0) 566 return (-12); 567 } 568 } else if (strcmp("zo_domain", attrName) == 0) { 569 if (attr->zo_domain == 0) { 570 attr->zo_domain = sdup(myself, T, val->value); 571 if (attr->zo_domain == 0) 572 return (-13); 573 } 574 } else if (strcmp("zo_access", attrName) == 0) { 575 if (attr->zo_access == 0) { 576 if (sscanf(val->value, "%x", &attr->zo_access) != 1) 577 return (-14); 578 } 579 } else if (strcmp("zo_ttl", attrName) == 0) { 580 if (attr->zo_ttl == 0) { 581 if (sscanf(val->value, "%x", &attr->zo_ttl) != 1) 582 return (-15); 583 } 584 } 585 586 return (0); 587 } 588 589 /* 590 * Return a DN and rule-value for the supplied mapping, db_query's, and 591 * input rule-value. This function only works on a single mapping. See 592 * mapToLDAP() below for a description of the action depending on the 593 * values of 'old' and 'new'. 594 * 595 * If both 'old' and 'new' are supplied, and the modify would result 596 * in a change to the DN, '*oldDN' will contain the old DN. Otherwise 597 * (and normally), '*oldDN' will be NULL. 598 */ 599 char * 600 map1qToLDAP(__nis_table_mapping_t *t, db_query *old, db_query *new, 601 __nis_rule_value_t *rvIn, __nis_rule_value_t **rvOutP, 602 char **oldDnP) { 603 604 __nis_rule_value_t *rv, *rvt; 605 __nis_ldap_search_t *ls; 606 char *dn = 0, *oldDn = 0; 607 __nis_table_mapping_t del; 608 char *myself = "map1qToLDAP"; 609 610 if (t == 0 || (old == 0 && new == 0) || rvOutP == 0) 611 return (0); 612 613 /* 614 * If entry should be deleted, we look at the delete 615 * policy in the table mapping. Should it specify a 616 * rule set, we use that rule set to build a rule- 617 * value, and the delete actually becomes a modify 618 * operation. 619 */ 620 if (old != 0 && new == 0) { 621 if (t->objectDN->delDisp == dd_perDbId) { 622 /* 623 * The functions that build a rule-value from a 624 * rule set expect a __nis_table_mapping_t, but the 625 * rule set in the __nis_object_dn_t isn't of that 626 * form. So, build a pseudo-__nis_table_mapping_t that 627 * borrows heavily from 't'. 628 */ 629 del = *t; 630 631 del.numRulesToLDAP = del.objectDN->numDbIds; 632 del.ruleToLDAP = del.objectDN->dbId; 633 634 /* 635 * Do a modify with the pseudo-table 636 * mapping, and the 'old' db_query 637 * supplying input to the delete rule 638 * set. 639 */ 640 t = &del; 641 new = old; 642 } else if (t->objectDN->delDisp == dd_always) { 643 644 /* Nothing to do here; all handled below */ 645 646 } else if (t->objectDN->delDisp == dd_never) { 647 648 return (0); 649 650 } else { 651 652 logmsg(MSG_INVALIDDELDISP, LOG_WARNING, 653 "%s: Invalid delete disposition %d for \"%s\"", 654 myself, t->objectDN->delDisp, 655 NIL(t->dbId)); 656 return (0); 657 658 } 659 } 660 661 /* Make a copy of the input rule-value */ 662 if (rvIn != 0) { 663 rv = initRuleValue(1, rvIn); 664 if (rv == 0) 665 return (0); 666 } else { 667 rv = 0; 668 } 669 670 /* First get a rule-value from the supplied NIS+ entry. */ 671 rvt = rv; 672 rv = buildNisPlusRuleValue(t, ((old != 0) ? old : new), rvt); 673 freeRuleValue(rvt, 1); 674 if (rv == 0) { 675 logmsg(MSG_NORULEVALUE, LOG_WARNING, 676 "%s: No in-query rule-value derived for \"%s\"", 677 myself, NIL(t->dbId)); 678 return (0); 679 } 680 681 /* 682 * Create a request (really only care about the DN) from the 683 * supplied NIS+ entry data. 684 */ 685 ls = createLdapRequest(t, rv, &dn, 0, NULL, NULL); 686 if (ls == 0 || dn == 0) { 687 logmsg(MSG_NOTIMECHECK, LOG_ERR, 688 "%s: Unable to create LDAP request for %s: %s", 689 myself, NIL(t->dbId), 690 (dn != 0) ? dn : rvId(rv, mit_nisplus)); 691 sfree(dn); 692 freeLdapSearch(ls); 693 freeRuleValue(rv, 1); 694 return (0); 695 } 696 697 freeLdapSearch(ls); 698 699 if (new != 0) { 700 /* 701 * Create a rule-value from the new NIS+ entry. 702 * Don't want to mix in the rule-value derived 703 * from 'old', so delete it. However, we still 704 * want the owner, group, etc., from 'rvIn'. 705 */ 706 if (old != 0) { 707 freeRuleValue(rv, 1); 708 if (rvIn != 0) { 709 rv = initRuleValue(1, rvIn); 710 if (rv == 0) { 711 sfree(dn); 712 return (0); 713 } 714 } else { 715 rv = 0; 716 } 717 } 718 rvt = rv; 719 rv = buildNisPlusRuleValue(t, new, rvt); 720 freeRuleValue(rvt, 1); 721 if (rv == 0) { 722 logmsg(MSG_NORULEVALUE, LOG_WARNING, 723 "%s: No new rule-value derived for \"%s: %s\"", 724 myself, NIL(t->dbId), dn); 725 sfree(dn); 726 return (0); 727 } 728 /* 729 * Check if the proposed modification would result in a 730 * a change to the DN. 731 */ 732 if (old != 0) { 733 oldDn = dn; 734 dn = 0; 735 ls = createLdapRequest(t, rv, &dn, 0, NULL, NULL); 736 if (ls == 0 || dn == 0) { 737 logmsg(MSG_NOTIMECHECK, LOG_ERR, 738 "%s: Unable to create new DN for \"%s: %s\"", 739 myself, NIL(t->dbId), oldDn); 740 sfree(oldDn); 741 freeLdapSearch(ls); 742 freeRuleValue(rv, 1); 743 return (0); 744 } 745 freeLdapSearch(ls); 746 if (strcasecmp(oldDn, dn) == 0) { 747 sfree(oldDn); 748 oldDn = 0; 749 } 750 } 751 } 752 753 754 *rvOutP = rv; 755 if (oldDnP != 0) 756 *oldDnP = oldDn; 757 758 return (dn); 759 } 760 761 /* 762 * Since the DN hash list is an automatic variable, there's no need for 763 * locking, and we remove the locking overhead by using the libnsl 764 * hash functions. 765 */ 766 #undef NIS_HASH_ITEM 767 #undef NIS_HASH_TABLE 768 769 typedef struct { 770 NIS_HASH_ITEM item; 771 int index; 772 char *oldDn; 773 } __dn_item_t; 774 775 /* 776 * Update LDAP per the supplied table mapping and db_query's. 777 * 778 * 'nq' is the number of elements in the 'old', 'new', and 'rvIn' 779 * arrays. mapToLDAP() generally performs one update for each 780 * element; however, if one or more of the individual queries 781 * produce the same DN, they're merged into a single update. 782 * 783 * There are four cases, depending on the values of 'old[iq]' and 784 * 'new[iq]': 785 * 786 * (1) old[iq] == 0 && new[iq] == 0 787 * No action; skip to next query 788 * 789 * (2) old[iq] == 0 && new[iq] != 0 790 * Attempt to use the 'new' db_query to get a DN, and try to create 791 * the corresponding LDAP entry. 792 * 793 * (3) old[iq] != 0 && new[iq] == 0 794 * Use the 'old' db_query to get a DN, and try to delete the LDAP 795 * entry per the table mapping. 796 * 797 * (4) old[iq] != 0 && new[iq] != 0 798 * Use the 'old' db_query to get a DN, and update (possibly create) 799 * the corresponding LDAP entry per the 'new' db_query. 800 * 801 * If 'rvIn' is non-NULL, it is expected to contain the object attributes 802 * (zo_owner, etc.) to be written to LDAP. 'rvIn' is an array with 'nq' 803 * elements. 804 * 805 * If 'firstOnly' is set, only the first old[iq]/new[iq] pair is used 806 * to perform the actual update. Any additional queries specified will 807 * have their values folded in, but are not used to derive update targets. 808 * This mode is inteded to support the case where multiple NIS+ entries 809 * map to one and the same LDAP entry. Note that 'rvIn' must still be 810 * an array of 'nq' elements, though if 'firstOnly' is set, it should be 811 * OK to leave all but 'rvIn[0]' empty. 812 * 813 * 'dbId' is used to further narow down the selection of mapping candidates 814 * to those matching the 'dbId' value. 815 */ 816 int 817 mapToLDAP(__nis_table_mapping_t *tm, int nq, db_query **old, db_query **new, 818 __nis_rule_value_t *rvIn, int firstOnly, char *dbId) { 819 __nis_table_mapping_t **tp, **tpa; 820 int i, n, rnq, iq, r, ret = LDAP_SUCCESS; 821 int maxMatches, numMatches = 0; 822 __nis_ldap_search_t *ls; 823 char **dn = 0, **odn = 0; 824 __nis_rule_value_t **rv; 825 __dn_item_t *dni; 826 char *myself = "mapToLDAP"; 827 828 829 if (tm == 0 || (old == 0 && new == 0) || nq <= 0) 830 return (LDAP_PARAM_ERROR); 831 832 /* Determine maximum number of table mapping matches */ 833 if (nq == 1) { 834 tp = selectTableMapping(tm, 835 (old != 0 && old[0] != 0) ? old[0] : new[0], 1, 0, 836 dbId, &maxMatches); 837 numMatches = maxMatches; 838 } else { 839 tp = selectTableMapping(tm, 0, 1, 0, dbId, &maxMatches); 840 } 841 842 /* 843 * If no matching mapping, we're not mapping to LDAP in this 844 * particular case. 845 */ 846 if (tp == 0 || maxMatches == 0) { 847 sfree(tp); 848 return (LDAP_SUCCESS); 849 } 850 851 /* 852 * Allocate the 'rv', 'dn', and 'tpa' arrays. Worst case is that 853 * we need nq * maxMatches elements in each array. However, if 854 * 'firstOnly' is set, we only need one element per matching 855 * mapping in each. 856 */ 857 dn = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (dn[0])); 858 odn = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (odn[0])); 859 rv = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (rv[0])); 860 tpa = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (tpa[0])); 861 if (dn == 0 || odn == 0 || rv == 0 || tpa == 0) { 862 sfree(tp); 863 sfree(dn); 864 sfree(odn); 865 sfree(rv); 866 sfree(tpa); 867 return (LDAP_NO_MEMORY); 868 } 869 870 /* Unless nq == 1, we don't need the 'tp' value */ 871 if (nq != 1) 872 sfree(tp); 873 874 logmsg(MSG_NOTIMECHECK, 875 #ifdef NISDB_LDAP_DEBUG 876 LOG_WARNING, 877 #else 878 LOG_INFO, 879 #endif /* NISDB_LDAP_DEBUG */ 880 "%s: %s: %d * %d potential updates", 881 myself, NIL(tm->objName), nq, maxMatches); 882 883 /* 884 * Create DNs, column and attribute values, and merge duplicate DNs. 885 */ 886 for (iq = 0, rnq = 0; iq < nq; iq++) { 887 int idx; 888 889 if ((old == 0 || old[iq] == 0) && 890 (new == 0 || new[iq] == 0)) 891 continue; 892 893 /* 894 * Select matching table mappings; if nq == 1, we've already 895 * got the 'tp' array from above. We expect this to be the 896 * most common case, so it's worth special treatment. 897 */ 898 if (nq != 1) 899 tp = selectTableMapping(tm, 900 (old != 0 && old[iq] != 0) ? old[iq] : new[iq], 1, 0, 901 dbId, &numMatches); 902 if (tp == 0) 903 continue; 904 else if (numMatches <= 0) { 905 sfree(tp); 906 continue; 907 } 908 909 idx = iq * maxMatches; 910 911 if (idx == 0 || !firstOnly) 912 (void) memcpy(&tpa[idx], tp, 913 numMatches * sizeof (tpa[idx])); 914 915 for (n = 0; n < numMatches; n++) { 916 char *dnt, *odnt; 917 __nis_rule_value_t *rvt = 0; 918 919 if (tp[n] == 0) 920 continue; 921 922 dnt = map1qToLDAP(tp[n], 923 (old != 0) ? old[iq] : 0, 924 (new != 0) ? new[iq] : 0, 925 (rvIn != 0) ? &rvIn[iq] : 0, 926 &rvt, &odnt); 927 928 if (dnt == 0) 929 continue; 930 if (rvt == 0) { 931 #ifdef NISDB_LDAP_DEBUG 932 abort(); 933 #else 934 sfree(dnt); 935 sfree(odnt); 936 continue; 937 #endif /* NISDB_LDAP_DEBUG */ 938 } 939 940 /* 941 * Create a request to get a rule-value with 942 * NIS+ data translated to LDAP equivalents. 943 */ 944 ls = createLdapRequest(tp[n], rvt, 0, 0, NULL, NULL); 945 if (ls == 0) { 946 if (ret == LDAP_SUCCESS) 947 ret = LDAP_OPERATIONS_ERROR; 948 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 949 "%s: Unable to map to LDAP attrs for %s:dn=%s", 950 myself, NIL(tp[n]->dbId), dnt); 951 sfree(dnt); 952 freeRuleValue(rvt, 1); 953 continue; 954 } 955 freeLdapSearch(ls); 956 957 /* 958 * If the DN is the same as one we already know 959 * about, merge the rule-values. 960 */ 961 962 if ((iq == 0 || !firstOnly) && dnt != 0) { 963 dni = am(myself, sizeof (*dni)); 964 if (dni != 0) { 965 dni->item.name = dnt; 966 dni->index = idx + n; 967 dni->oldDn = odnt; 968 } else { 969 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 970 "%s: Skipping update for dn=\"%s\"", 971 myself, dnt); 972 sfree(dnt); 973 dnt = 0; 974 } 975 if (dnt != 0) { 976 dn[idx+n] = dnt; 977 odn[idx+n] = odnt; 978 rv[idx+n] = rvt; 979 rnq++; 980 } else { 981 freeRuleValue(rvt, 1); 982 rvt = 0; 983 } 984 } else if (dnt != 0) { 985 sfree(dnt); 986 sfree(odnt); 987 freeRuleValue(rvt, 1); 988 } 989 } 990 sfree(tp); 991 } 992 993 logmsg(MSG_NOTIMECHECK, 994 #ifdef NISDB_LDAP_DEBUG 995 LOG_WARNING, 996 #else 997 LOG_INFO, 998 #endif /* NISDB_LDAP_DEBUG */ 999 "%s: %s: %d update%s requested", 1000 myself, NIL(tm->objName), rnq, rnq != 1 ? "s" : ""); 1001 1002 /* Perform the updates */ 1003 for (i = rnq = 0; i < (firstOnly ? maxMatches : nq*maxMatches); i++) { 1004 int delPerDbId; 1005 1006 if (dn[i] == 0) 1007 continue; 1008 1009 #ifdef NISDB_LDAP_DEBUG 1010 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1011 "%s: %s %s:dn=%s", 1012 myself, 1013 (new != 0 && new[i/maxMatches] != 0) ? 1014 "modify" : "delete", 1015 NIL(tpa[i]->dbId), dn[i]); 1016 #endif /* NISDB_LDAP_DEBUG */ 1017 1018 delPerDbId = (tpa[i]->objectDN->delDisp == dd_perDbId); 1019 if ((new != 0 && new[i/maxMatches] != 0) || delPerDbId) { 1020 /* 1021 * Try to modify/create the specified DN. First, 1022 * however, if the update changes the DN, make 1023 * that change. 1024 */ 1025 if (odn[i] == 0 || (r = ldapChangeDN(odn[i], dn[i])) == 1026 LDAP_SUCCESS) { 1027 int addFirst; 1028 1029 addFirst = (new != 0 && 1030 new[i/maxMatches] != 0 && 1031 !delPerDbId); 1032 r = ldapModify(dn[i], rv[i], 1033 tpa[i]->objectDN->write.attrs, 1034 addFirst); 1035 } 1036 } else { 1037 /* Try to delete the specified DN */ 1038 r = ldapModify(dn[i], 0, 1039 tpa[i]->objectDN->write.attrs, 0); 1040 } 1041 1042 if (r == LDAP_SUCCESS) { 1043 rnq++; 1044 } else { 1045 if (ret == LDAP_SUCCESS) 1046 ret = r; 1047 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1048 "%s: LDAP %s request error %d for %s:dn=%s", 1049 myself, 1050 (new != 0 && new[i/maxMatches] != 0) ? 1051 "modify" : "delete", 1052 r, NIL(tpa[i]->dbId), dn[i]); 1053 } 1054 1055 sfree(dn[i]); 1056 dn[i] = 0; 1057 freeRuleValue(rv[i], 1); 1058 rv[i] = 0; 1059 } 1060 1061 sfree(dn); 1062 sfree(odn); 1063 sfree(rv); 1064 sfree(tpa); 1065 1066 logmsg(MSG_NOTIMECHECK, 1067 #ifdef NISDB_LDAP_DEBUG 1068 LOG_WARNING, 1069 #else 1070 LOG_INFO, 1071 #endif /* NISDB_LDAP_DEBUG */ 1072 "%s: %s: %d update%s performed", 1073 myself, NIL(tm->objName), rnq, rnq != 1 ? "s" : ""); 1074 1075 return (ret); 1076 } 1077 1078 /* 1079 * In nis+2ldap, check if the query 'q' matches the selector index 'x->index'. 1080 * 1081 * In nis2ldap, if 'name' is provided then check if its value in 'val' 1082 * matches the selector index. If 'name' is NULL, then check if rule-value 'rv' 1083 * matches the index. 1084 * To match the selector index, all fieldspecs in the indexlist should match 1085 * (AND). In nis2ldap, an exception is, if there are multiple fieldspecs with 1086 * the same fieldname then only one of them needs to match (OR). 1087 * Example: 1088 * Indexlist = [host="H*", host="I*", user="U*", domain="D*"] 1089 * Then, 1090 * host = "H1", user="U1", domain="D1" ==> pass 1091 * host = "I1", user="U1", domain="D1" ==> pass 1092 * host = "X1", user="U1", domain="D1" ==> fail 1093 * host = "H1", user="X1", domain="D1" ==> fail 1094 * host = "H1", user="U1" ==> fail 1095 * 1096 * Return 1 in case of a match, 0 otherwise. 1097 */ 1098 int 1099 verifyIndexMatch(__nis_table_mapping_t *x, db_query *q, 1100 __nis_rule_value_t *rv, char *name, char *val) { 1101 int i, j, k, match = 1; 1102 char *myself = "verifyIndexMatch"; 1103 1104 /* 1105 * The pass and fail arrays are used by N2L to keep track of 1106 * index matches. This saves us from having matches in a 1107 * nested loop to decide OR or AND. 1108 */ 1109 int ppos, fpos; 1110 char **pass, **fail; 1111 1112 if (x == 0) 1113 return (0); 1114 1115 /* Trivial match */ 1116 if (x->index.numIndexes <= 0 || (!yp2ldap && q == 0)) 1117 return (1); 1118 1119 if (yp2ldap) { 1120 if (!(pass = am(myself, x->index.numIndexes * sizeof (char *)))) 1121 return (0); 1122 if (!(fail = am(myself, 1123 x->index.numIndexes * sizeof (char *)))) { 1124 sfree(pass); 1125 return (0); 1126 } 1127 ppos = fpos = 0; 1128 } 1129 1130 /* Check each index */ 1131 for (i = 0; i < x->index.numIndexes; i++) { 1132 int len = 0; 1133 char *value = 0; 1134 1135 /* Skip NULL index names */ 1136 if (x->index.name[i] == 0) 1137 continue; 1138 1139 /* Check N2L values */ 1140 if (yp2ldap) { 1141 if (name) { 1142 if (strcasecmp(x->index.name[i], name) == 0) 1143 value = val; 1144 else 1145 continue; 1146 } else if (rv) { 1147 if (strcasecmp(x->index.name[i], N2LKEY) == 0 || 1148 strcasecmp(x->index.name[i], N2LIPKEY) 1149 == 0) 1150 continue; 1151 value = findVal(x->index.name[i], rv, 1152 mit_nisplus); 1153 } 1154 1155 if (value && verifyMappingMatch(x->index.value[i], 1156 value)) 1157 pass[ppos++] = x->index.name[i]; 1158 else 1159 fail[fpos++] = x->index.name[i]; 1160 continue; 1161 } 1162 1163 /* If here, means nis+2ldap */ 1164 1165 /* Is the index name a known column ? */ 1166 for (j = 0; j < x->numColumns; j++) { 1167 if (strcmp(x->index.name[i], x->column[j]) == 0) { 1168 /* 1169 * Do we have a value for the column ? 1170 */ 1171 for (k = 0; k < q->components.components_len; 1172 k++) { 1173 if (q->components.components_val[k]. 1174 which_index == j) { 1175 value = q->components. 1176 components_val[k]. 1177 index_value-> 1178 itemvalue. 1179 itemvalue_val; 1180 len = q->components. 1181 components_val[k]. 1182 index_value-> 1183 itemvalue. 1184 itemvalue_len; 1185 break; 1186 } 1187 } 1188 if (value != 0) 1189 break; 1190 } 1191 } 1192 1193 /* 1194 * If we found a value, check if it matches the 1195 * format. If no value found or no match, this 1196 * mapping is _not_ an alternative. Otherwise, 1197 * we continue checking any other indexes. 1198 */ 1199 if (value == 0 || 1200 !verifyMappingMatch(x->index.value[i], 1201 value)) { 1202 match = 0; 1203 break; 1204 } 1205 } 1206 1207 if (yp2ldap) { 1208 for (--fpos; fpos >= 0; fpos--) { 1209 for (i = 0; i < ppos; i++) { 1210 if (strcmp(pass[i], fail[fpos]) == 0) 1211 break; 1212 } 1213 if (i == ppos) { 1214 match = 0; 1215 break; 1216 } 1217 } 1218 sfree(pass); 1219 sfree(fail); 1220 } 1221 1222 return (match); 1223 } 1224 1225 /* 1226 * Return all table mappings that match the column values in 'q'. 1227 * If there's no match, return those alternative mappings that don't 1228 * have an index; if no such mapping exists, return NULL. 1229 * 1230 * If 'wantWrite' is set, we want mappings for writing (i.e., data 1231 * to LDAP); otherwise, we want mappings for reading. 1232 * 1233 * If 'wantObj' is set, we want object mappings only (i.e., _not_ 1234 * those used to map entries in tables). 1235 * 1236 * If 'dbId' is non-NULL, we select mappings with a matching dbId field. 1237 */ 1238 __nis_table_mapping_t ** 1239 selectTableMapping(__nis_table_mapping_t *t, db_query *q, 1240 int wantWrite, int wantObj, char *dbId, 1241 int *numMatches) { 1242 __nis_table_mapping_t *r, *x, **tp; 1243 int i, j, k, nm, numap; 1244 char *myself = "selectTableMapping"; 1245 1246 if (numMatches == 0) 1247 numMatches = &nm; 1248 1249 /* 1250 * Count the number of possible mappings, so that we can 1251 * allocate the 'tp' array up front. 1252 */ 1253 for (numap = 0, x = t; x != 0; numap++, x = x->next); 1254 1255 if (numap == 0) { 1256 *numMatches = 0; 1257 return (0); 1258 } 1259 1260 tp = am(myself, numap * sizeof (tp[0])); 1261 if (tp == 0) { 1262 *numMatches = -1; 1263 return (0); 1264 } 1265 1266 /* 1267 * Special cases: 1268 * 1269 * q == 0 trivially matches any 't' of the correct object type 1270 * 1271 * wantObj != 0 means we ignore 'q' 1272 */ 1273 if (q == 0 || wantObj) { 1274 for (i = 0, x = t, nm = 0; i < numap; i++, x = x->next) { 1275 if (x->objectDN == 0) 1276 continue; 1277 if (wantWrite) { 1278 if (x->objectDN->write.scope == 1279 LDAP_SCOPE_UNKNOWN) 1280 continue; 1281 } else { 1282 if (x->objectDN->read.scope == 1283 LDAP_SCOPE_UNKNOWN) 1284 continue; 1285 } 1286 if (wantObj) { 1287 if (x->numColumns > 0) 1288 continue; 1289 } else { 1290 if (x->numColumns <= 0) 1291 continue; 1292 } 1293 if (dbId != 0 && x->dbId != 0 && 1294 strcmp(dbId, x->dbId) != 0) 1295 continue; 1296 tp[nm] = x; 1297 nm++; 1298 } 1299 *numMatches = nm; 1300 if (nm == 0) { 1301 sfree(tp); 1302 tp = 0; 1303 } 1304 return (tp); 1305 } 1306 1307 /* Scan all mappings, and collect candidates */ 1308 for (nm = 0, r = 0, x = t; x != 0; x = x->next) { 1309 if (x->objectDN == 0) 1310 continue; 1311 if (wantWrite) { 1312 if (x->objectDN->write.scope == LDAP_SCOPE_UNKNOWN) 1313 continue; 1314 } else { 1315 if (x->objectDN->read.scope == LDAP_SCOPE_UNKNOWN) 1316 continue; 1317 } 1318 /* Only want table/entry mappings */ 1319 if (x->numColumns <= 0) 1320 continue; 1321 if (dbId != 0 && x->dbId != 0 && 1322 strcmp(dbId, x->dbId) != 0) 1323 continue; 1324 /* 1325 * It's a match if: there are no indexes, or we actually 1326 * match the query with the indexes. 1327 */ 1328 if (x->index.numIndexes <= 0 || 1329 verifyIndexMatch(x, q, 0, 0, 0)) { 1330 tp[nm] = x; 1331 nm++; 1332 } 1333 } 1334 1335 if (nm == 0) { 1336 free(tp); 1337 tp = 0; 1338 } 1339 1340 *numMatches = nm; 1341 1342 return (tp); 1343 } 1344 1345 /* 1346 * Return 1 if there's an indexed mapping, 0 otherwise. 1347 */ 1348 int 1349 haveIndexedMapping(__nis_table_mapping_t *t) { 1350 __nis_table_mapping_t *x; 1351 1352 for (x = t; x != 0; x = x->next) { 1353 if (x->index.numIndexes > 0) 1354 return (1); 1355 } 1356 1357 return (0); 1358 } 1359 1360 /* 1361 * Given an input string 'attrs' of the form "attr1=val1,attr2=val2,...", 1362 * or a filter, return the value associated with the attribute 'attrName'. 1363 * If no instance of 'attrName' is found, return 'default'. In all cases, 1364 * the return value is a copy, and must be freed by the caller. 1365 * 1366 * Of course, return NULL in case of failure. 1367 */ 1368 static char * 1369 attrVal(char *msg, char *attrName, char *def, char *attrs) { 1370 char *val, *filter, **fc = 0; 1371 int i, nfc; 1372 char *myself = "attrVal"; 1373 1374 if (attrName == 0 || attrs == 0) 1375 return (0); 1376 1377 if (msg == 0) 1378 msg = myself; 1379 1380 val = def; 1381 1382 filter = makeFilter(attrs); 1383 if (filter != 0 && (fc = makeFilterComp(filter, &nfc)) != 0 && 1384 nfc > 0) { 1385 for (i = 0; i < nfc; i++) { 1386 char *name, *value; 1387 1388 name = fc[i]; 1389 /* Skip if not of attr=value form */ 1390 if ((value = strchr(name, '=')) == 0) 1391 continue; 1392 1393 *value = '\0'; 1394 value++; 1395 1396 if (strcasecmp(attrName, name) == 0) { 1397 val = value; 1398 break; 1399 } 1400 } 1401 } 1402 1403 if (val != 0) 1404 val = sdup(msg, T, val); 1405 1406 sfree(filter); 1407 freeFilterComp(fc, nfc); 1408 1409 return (val); 1410 } 1411 1412 extern bool_t xdr_nis_object(register XDR *xdrs, nis_object *objp); 1413 1414 /* 1415 * Copy an XDR:ed version of the NIS+ object 'o' (or the one indicated 1416 * by 't->objName' if 'o' is NULL) to the place indicated by 1417 * 't->objectDN->write'. Return an appropriate LDAP status code. 1418 */ 1419 int 1420 objToLDAP(__nis_table_mapping_t *t, nis_object *o, entry_obj **ea, int numEa) { 1421 __nis_table_mapping_t **tp; 1422 XDR xdr; 1423 char *objName; 1424 int stat, osize, n, numMatches = 0; 1425 void *buf; 1426 __nis_rule_value_t *rv; 1427 __nis_value_t *val; 1428 __nis_single_value_t *sv; 1429 char **attrName, *dn; 1430 char *myself = "objToLDAP"; 1431 1432 if (t == 0) 1433 return (LDAP_PARAM_ERROR); 1434 1435 logmsg(MSG_NOTIMECHECK, 1436 #ifdef NISDB_LDAP_DEBUG 1437 LOG_WARNING, 1438 #else 1439 LOG_INFO, 1440 #endif /* NISDB_LDAP_DEBUG */ 1441 "%s: %s", myself, NIL(t->objName)); 1442 1443 tp = selectTableMapping(t, 0, 1, 1, 0, &numMatches); 1444 if (tp == 0 || numMatches <= 0) { 1445 sfree(tp); 1446 logmsg(MSG_NOTIMECHECK, 1447 #ifdef NISDB_LDAP_DEBUG 1448 LOG_WARNING, 1449 #else 1450 LOG_INFO, 1451 #endif /* NISDB_LDAP_DEBUG */ 1452 "%s: %s (no mapping)", myself, NIL(t->objName)); 1453 return (LDAP_SUCCESS); 1454 } 1455 1456 for (n = 0; n < numMatches; n++) { 1457 1458 t = tp[n]; 1459 1460 if (o == 0) { 1461 sfree(tp); 1462 return (LDAP_OPERATIONS_ERROR); 1463 } 1464 1465 buf = (char *)xdrNisObject(o, ea, numEa, &osize); 1466 if (buf == 0) { 1467 sfree(tp); 1468 return (LDAP_OPERATIONS_ERROR); 1469 } 1470 1471 /* 1472 * Prepare to build a rule-value containing the XDR:ed 1473 * object 1474 */ 1475 rv = am(myself, sizeof (*rv)); 1476 sv = am(myself, sizeof (*sv)); 1477 val = am(myself, sizeof (*val)); 1478 attrName = am(myself, sizeof (attrName[0])); 1479 if (attrName != 0) 1480 attrName[0] = attrVal(myself, "nisplusObject", 1481 "nisplusObject", 1482 t->objectDN->write.attrs); 1483 if (rv == 0 || sv == 0 || val == 0 || attrName == 0 || 1484 attrName[0] == 0) { 1485 sfree(tp); 1486 sfree(buf); 1487 sfree(rv); 1488 sfree(sv); 1489 sfree(val); 1490 sfree(attrName); 1491 return (LDAP_NO_MEMORY); 1492 } 1493 1494 sv->length = osize; 1495 sv->value = buf; 1496 1497 /* 'vt_ber' just means "not a NUL-terminated string" */ 1498 val->type = vt_ber; 1499 val->repeat = 0; 1500 val->numVals = 1; 1501 val->val = sv; 1502 1503 rv->numAttrs = 1; 1504 rv->attrName = attrName; 1505 rv->attrVal = val; 1506 1507 /* 1508 * The 'write.base' is the actual DN of the entry (and the 1509 * scope had better be 'base', but we don't check that). 1510 */ 1511 dn = t->objectDN->write.base; 1512 1513 stat = ldapModify(dn, rv, t->objectDN->write.attrs, 1); 1514 1515 freeRuleValue(rv, 1); 1516 1517 logmsg(MSG_NOTIMECHECK, 1518 #ifdef NISDB_LDAP_DEBUG 1519 LOG_WARNING, 1520 #else 1521 LOG_INFO, 1522 #endif /* NISDB_LDAP_DEBUG */ 1523 "%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat)); 1524 1525 if (stat != LDAP_SUCCESS) 1526 break; 1527 1528 } 1529 1530 sfree(tp); 1531 1532 return (stat); 1533 } 1534 1535 /* 1536 * Retrieve a copy of the 't->objName' object from LDAP, where it's 1537 * stored in XDR:ed form in the place indicated by 't->objectDN->read'. 1538 * Un-XDR the object, and return a pointer to it in '*obj'; it's the 1539 * responsibility of the caller to free the object when it's no 1540 * longer needed. 1541 * 1542 * Returns an appropriate LDAP status. 1543 */ 1544 int 1545 objFromLDAP(__nis_table_mapping_t *t, nis_object **obj, 1546 entry_obj ***eaP, int *numEaP) { 1547 __nis_table_mapping_t **tp; 1548 XDR xdr; 1549 nis_object *o; 1550 __nis_rule_value_t *rv; 1551 __nis_ldap_search_t *ls; 1552 char *attrs[2], *filter, **fc = 0; 1553 void *buf; 1554 int i, j, nfc, nrv, blen, stat = LDAP_SUCCESS; 1555 int n, numMatches; 1556 char *myself = "objFromLDAP"; 1557 1558 if (t == 0) 1559 return (LDAP_PARAM_ERROR); 1560 1561 /* 1562 * If there's nowhere to store the result, we might as 1563 * well pretend all went well, and return right away. 1564 */ 1565 if (obj == 0) 1566 return (LDAP_SUCCESS); 1567 1568 /* Prepare for the worst */ 1569 *obj = 0; 1570 1571 logmsg(MSG_NOTIMECHECK, 1572 #ifdef NISDB_LDAP_DEBUG 1573 LOG_WARNING, 1574 #else 1575 LOG_INFO, 1576 #endif /* NISDB_LDAP_DEBUG */ 1577 "%s: %s", myself, NIL(t->objName)); 1578 1579 tp = selectTableMapping(t, 0, 0, 1, 0, &numMatches); 1580 if (tp == 0 || numMatches <= 0) { 1581 sfree(tp); 1582 logmsg(MSG_NOTIMECHECK, 1583 #ifdef NISDB_LDAP_DEBUG 1584 LOG_WARNING, 1585 #else 1586 LOG_INFO, 1587 #endif /* NISDB_LDAP_DEBUG */ 1588 "%s: %s (no mapping)", myself, NIL(t->objName)); 1589 return (LDAP_SUCCESS); 1590 } 1591 1592 for (n = 0; n < numMatches; n++) { 1593 1594 t = tp[n]; 1595 1596 filter = makeFilter(t->objectDN->read.attrs); 1597 if (filter == 0 || (fc = makeFilterComp(filter, &nfc)) == 0 || 1598 nfc <= 0) { 1599 sfree(tp); 1600 sfree(filter); 1601 freeFilterComp(fc, nfc); 1602 return ((t->objectDN->read.attrs != 0) ? 1603 LDAP_NO_MEMORY : LDAP_PARAM_ERROR); 1604 } 1605 /* Don't need the filter, just the components */ 1606 sfree(filter); 1607 1608 /* 1609 * Look for a "nisplusObject" attribute, and (if found) copy 1610 * the value to attrs[0]. Also remove the "nisplusObject" 1611 * attribute and value from the filter components. 1612 */ 1613 attrs[0] = sdup(myself, T, "nisplusObject"); 1614 if (attrs[0] == 0) { 1615 sfree(tp); 1616 freeFilterComp(fc, nfc); 1617 return (LDAP_NO_MEMORY); 1618 } 1619 attrs[1] = 0; 1620 for (i = 0; i < nfc; i++) { 1621 char *name, *value; 1622 int compare; 1623 1624 name = fc[i]; 1625 /* Skip if not of attr=value form */ 1626 if ((value = strchr(name, '=')) == 0) 1627 continue; 1628 1629 /* Temporarily overWrite the '=' with a '\0' */ 1630 *value = '\0'; 1631 1632 /* Compare with our target attribute name */ 1633 compare = strcasecmp("nisplusObject", name); 1634 1635 /* Put back the '=' */ 1636 *value = '='; 1637 1638 /* Is it the name we're looking for ? */ 1639 if (compare == 0) { 1640 sfree(attrs[0]); 1641 attrs[0] = sdup(myself, T, value+1); 1642 if (attrs[0] == 0) { 1643 sfree(tp); 1644 freeFilterComp(fc, nfc); 1645 return (LDAP_NO_MEMORY); 1646 } 1647 sfree(fc[i]); 1648 if (i < nfc-1) 1649 (void) memmove(&fc[i], &fc[i+1], 1650 (nfc-1-i) * sizeof (fc[i])); 1651 nfc--; 1652 break; 1653 } 1654 } 1655 1656 ls = buildLdapSearch(t->objectDN->read.base, 1657 t->objectDN->read.scope, 1658 nfc, fc, 0, attrs, 0, 1); 1659 sfree(attrs[0]); 1660 freeFilterComp(fc, nfc); 1661 if (ls == 0) { 1662 sfree(tp); 1663 return (LDAP_OPERATIONS_ERROR); 1664 } 1665 1666 nrv = 0; 1667 rv = ldapSearch(ls, &nrv, 0, &stat); 1668 if (rv == 0) { 1669 sfree(tp); 1670 freeLdapSearch(ls); 1671 return (stat); 1672 } 1673 1674 for (i = 0, buf = 0; i < nrv && buf == 0; i++) { 1675 for (j = 0; j < rv[i].numAttrs; j++) { 1676 if (strcasecmp(ls->attrs[0], 1677 rv[i].attrName[j]) == 0) { 1678 if (rv[i].attrVal[j].numVals <= 0) 1679 continue; 1680 buf = rv[i].attrVal[j].val[0].value; 1681 blen = rv[i].attrVal[j].val[0].length; 1682 break; 1683 } 1684 } 1685 } 1686 1687 if (buf != 0) { 1688 o = unXdrNisObject(buf, blen, eaP, numEaP); 1689 if (o == 0) { 1690 sfree(tp); 1691 freeLdapSearch(ls); 1692 freeRuleValue(rv, nrv); 1693 return (LDAP_OPERATIONS_ERROR); 1694 } 1695 stat = LDAP_SUCCESS; 1696 *obj = o; 1697 } else { 1698 stat = LDAP_NO_SUCH_OBJECT; 1699 } 1700 1701 freeLdapSearch(ls); 1702 freeRuleValue(rv, nrv); 1703 1704 logmsg(MSG_NOTIMECHECK, 1705 #ifdef NISDB_LDAP_DEBUG 1706 LOG_WARNING, 1707 #else 1708 LOG_INFO, 1709 #endif /* NISDB_LDAP_DEBUG */ 1710 "%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat)); 1711 1712 if (stat != LDAP_SUCCESS) 1713 break; 1714 1715 } 1716 1717 sfree(tp); 1718 1719 return (stat); 1720 } 1721 1722 int 1723 deleteLDAPobj(__nis_table_mapping_t *t) { 1724 __nis_table_mapping_t **tp; 1725 int n, stat, numMatches = 0; 1726 char *myself = "deleteLDAPobj"; 1727 1728 if (t == 0) 1729 return (LDAP_PARAM_ERROR); 1730 1731 logmsg(MSG_NOTIMECHECK, 1732 #ifdef NISDB_LDAP_DEBUG 1733 LOG_WARNING, 1734 #else 1735 LOG_INFO, 1736 #endif /* NISDB_LDAP_DEBUG */ 1737 "%s: %s", myself, NIL(t->objName)); 1738 1739 tp = selectTableMapping(t, 0, 1, 1, 0, &numMatches); 1740 if (tp == 0 || numMatches <= 0) { 1741 sfree(tp); 1742 logmsg(MSG_NOTIMECHECK, 1743 #ifdef NISDB_LDAP_DEBUG 1744 LOG_WARNING, 1745 #else 1746 LOG_INFO, 1747 #endif /* NISDB_LDAP_DEBUG */ 1748 "%s: %s (no mapping)", myself, NIL(t->objName)); 1749 return (LDAP_SUCCESS); 1750 } 1751 1752 for (n = 0; n < numMatches; n++) { 1753 1754 t = tp[n]; 1755 1756 if (t->objectDN->delDisp == dd_always) { 1757 /* Delete entire entry */ 1758 stat = ldapModify(t->objectDN->write.base, 0, 1759 t->objectDN->write.attrs, 1); 1760 } else if (t->objectDN->delDisp == dd_perDbId) { 1761 /* 1762 * Delete the attribute holding the object. 1763 * First, determine what that attribute is called. 1764 */ 1765 char *attrName = 1766 attrVal(myself, 1767 "nisplusObject", 1768 "nisplusObject", 1769 t->objectDN->write.attrs); 1770 __nis_rule_value_t rv; 1771 __nis_value_t val; 1772 1773 if (attrName == 0) { 1774 sfree(tp); 1775 return (LDAP_NO_MEMORY); 1776 } 1777 1778 /* 1779 * Build a __nis_value_t with 'numVals' < 0 to 1780 * indicate deletion. 1781 */ 1782 val.type = vt_ber; 1783 val.numVals = -1; 1784 val.val = 0; 1785 1786 /* 1787 * Build a rule-value with the name we determined 1788 * above, and the deletion value. 1789 */ 1790 (void) memset(&rv, 0, sizeof (rv)); 1791 rv.numAttrs = 1; 1792 rv.attrName = &attrName; 1793 rv.attrVal = &val; 1794 1795 stat = ldapModify(t->objectDN->write.base, &rv, 1796 t->objectDN->write.attrs, 0); 1797 1798 sfree(attrName); 1799 } else if (t->objectDN->delDisp == dd_never) { 1800 /* Nothing to do, so we're trivially successful */ 1801 stat = LDAP_SUCCESS; 1802 } else { 1803 stat = LDAP_PARAM_ERROR; 1804 } 1805 1806 logmsg(MSG_NOTIMECHECK, 1807 #ifdef NISDB_LDAP_DEBUG 1808 LOG_WARNING, 1809 #else 1810 LOG_INFO, 1811 #endif /* NISDB_LDAP_DEBUG */ 1812 "%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat)); 1813 1814 /* If there were no such object, we've trivially succeeded */ 1815 if (stat == LDAP_NO_SUCH_OBJECT) 1816 stat = LDAP_SUCCESS; 1817 1818 if (stat != LDAP_SUCCESS) 1819 break; 1820 1821 } 1822 1823 sfree(tp); 1824 1825 return (stat); 1826 } 1827