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