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