1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <strings.h> 30 #include <string.h> 31 #include <lber.h> 32 #include <ldap.h> 33 34 #include "db_item_c.h" 35 36 #include "nisdb_mt.h" 37 38 #include "ldap_util.h" 39 #include "ldap_structs.h" 40 #include "ldap_val.h" 41 #include "ldap_ruleval.h" 42 #include "ldap_op.h" 43 #include "ldap_nisdbquery.h" 44 #include "ldap_attr.h" 45 #include "ldap_nisplus.h" 46 #include "ldap_xdr.h" 47 48 49 item * 50 buildItem(int len, void *value) { 51 char *myself = "buildItem"; 52 item *i = am(myself, sizeof (*i)); 53 int mlen = len; 54 55 if (i == 0) 56 return (0); 57 58 /* 59 * To this function, a NULL value, or a length less than or equal 60 * zero means an item with no value. Hence, buildItem(0, 0) is 61 * _not_ the right way to create index_value == 0 to indicate 62 * deletion. 63 */ 64 if (value == 0 || len <= 0) { 65 i->itemvalue.itemvalue_len = 0; 66 i->itemvalue.itemvalue_val = 0; 67 return (i); 68 } 69 70 /* 71 * NIS+ usually stores the terminating NUL for strings, so we add 72 * it here just in case. This means we usually waste a byte for 73 * binary column values... 74 */ 75 if (len > 0 && ((char *)value)[len-1] != '\0') 76 mlen++; 77 78 i->itemvalue.itemvalue_len = len; 79 i->itemvalue.itemvalue_val = am(myself, mlen); 80 if (mlen > 0 && i->itemvalue.itemvalue_val == 0) { 81 free(i); 82 return (0); 83 } 84 memcpy(i->itemvalue.itemvalue_val, value, len); 85 86 return (i); 87 } 88 89 void 90 freeItem(item *i) { 91 if (i != 0) { 92 sfree(i->itemvalue.itemvalue_val); 93 free(i); 94 } 95 } 96 97 void 98 freeQcomp(db_qcomp *qc, int doFree) { 99 100 if (qc == 0) 101 return; 102 103 freeItem(qc->index_value); 104 if (doFree) 105 free(qc); 106 } 107 108 db_query * 109 buildQuery(int num_components, db_qcomp *components) { 110 char *myself = "buildQuery"; 111 db_query *q = am(myself, sizeof (*q)); 112 113 if (q == 0) 114 return (0); 115 116 q->components.components_len = num_components; 117 q->components.components_val = components; 118 119 return (q); 120 } 121 122 /* 123 * Clone a db_query. The 'numComps' parameter can be used to specify 124 * the number of db_qcomp's to allocate (in the 'components.components_val' 125 * array), if 'components.components_len' hasn't yet reached its expected 126 * maximum value. 127 */ 128 db_query * 129 cloneQuery(db_query *old, int numComps) { 130 db_query *new; 131 int i; 132 char *myself = "cloneQuery"; 133 134 if (old == 0) 135 return (0); 136 137 new = am(myself, sizeof (*new)); 138 if (new == 0) 139 return (0); 140 141 if (old->components.components_len > numComps) 142 numComps = old->components.components_len; 143 144 new->components.components_val = am(myself, 145 sizeof (new->components.components_val[0]) * 146 numComps); 147 if (numComps > 0 && new->components.components_val == 0) { 148 free(new); 149 return (0); 150 } 151 152 for (i = 0; i < old->components.components_len; i++) { 153 item *it; 154 155 if (old->components.components_val[i].index_value == 0) { 156 new->components.components_val[i].index_value = 0; 157 new->components.components_val[i].which_index = 158 old->components.components_val[i].which_index; 159 continue; 160 } 161 162 it = buildItem(old->components.components_val[i].index_value-> 163 itemvalue.itemvalue_len, 164 old->components.components_val[i].index_value-> 165 itemvalue.itemvalue_val); 166 167 if (it == 0) { 168 new->components.components_len = i + 1; 169 freeQuery(new); 170 return (0); 171 } 172 173 new->components.components_val[i].index_value = it; 174 new->components.components_val[i].which_index = 175 old->components.components_val[i].which_index; 176 } 177 178 new->components.components_len = old->components.components_len; 179 180 return (new); 181 } 182 183 void 184 freeQuery(db_query *q) { 185 int i; 186 187 if (q == 0) 188 return; 189 190 for (i = 0; i < q->components.components_len; i++) { 191 freeItem(q->components.components_val[i].index_value); 192 } 193 194 sfree(q->components.components_val); 195 sfree(q); 196 } 197 198 void 199 freeQueries(db_query **q, int numQ) { 200 int i; 201 202 if (q == 0) 203 return; 204 205 for (i = 0; i < numQ; i++) 206 freeQuery(q[i]); 207 208 sfree(q); 209 } 210 211 /* 212 * Given an array index[0..num-1] of pointers to strings of the form 213 * "name=value", create the corresponding db_queries. "name=" indicates 214 * deletion, which results in a db_query component where index_value == 0. 215 * 216 * The __nis_table_mapping_t structure is used to translate column 217 * names to indices. 218 * 219 * If 'rvP' is non-NULL, the searchable columns from the 'index' 220 * name/value pairs are used to retrieve copies of the corresponding NIS+ 221 * entries, and '*rvP' is initialized with the current entry values 222 * and object attributes. Names/values supplied in 'index' override 223 * those from existing NIS+ entries. 224 */ 225 db_query ** 226 createQuery(int num, char **index, __nis_table_mapping_t *t, 227 __nis_rule_value_t **rvP, int *numVals) { 228 db_query **q; 229 db_qcomp *qc; 230 int i, j, n, a, nv, niv, stat, sinum; 231 __nis_rule_value_t *rvq; 232 __nis_buffer_t b = {0, 0}; 233 nis_result *res = 0; 234 char *table = 0; 235 char *myself = "createQuery"; 236 237 rvq = initRuleValue(1, 0); 238 if (rvq == 0) 239 return (0); 240 241 if (numVals == 0) 242 numVals = &nv; 243 *numVals = 0; 244 245 if (rvP != 0) { 246 /* 247 * Try to obtain a copy of the table object, in order to 248 * determine the searchable columns. A failure isn't 249 * necessarily fatal; we just try to compose the entire 250 * LDAP data from the col=val pairs. 251 */ 252 table = fullObjName(F, t->objName); 253 if (table == 0) { 254 logmsg(MSG_NOTIMECHECK, LOG_ERR, 255 "%s: Error converting \"%s\" to FQ object name", 256 myself, NIL(t->objName)); 257 freeRuleValue(rvq, 1); 258 return (0); 259 } 260 261 stat = getNisPlusObj(table, myself, &res); 262 if (stat == LDAP_SUCCESS) { 263 if (res->objects.objects_val->zo_data.zo_type != 264 NIS_TABLE_OBJ) { 265 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 266 "%s: \"%s\" isn't a table object", 267 myself, NIL(table)); 268 nis_freeresult(res); 269 res = 0; 270 } 271 } else { 272 logmsg(MSG_NOTIMECHECK, LOG_INFO, 273 "%s: Unable to retrieve \"%s\" object: %s", 274 myself, NIL(table), ldap_err2string(stat)); 275 } 276 } 277 278 /* Create a rule-value from the col=val pairs */ 279 for (n = 0; n < num; n++) { 280 char *name; 281 char *value; 282 283 if ((value = strchr(index[n], '=')) == 0) { 284 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 285 "%s: no '=' in \"%s\"", 286 myself, index[n]); 287 continue; 288 } 289 290 *value = '\0'; 291 value++; 292 293 for (a = 0; a < t->numColumns; a++) { 294 if (strcmp(index[n], t->column[a]) == 0) { 295 int i, len = slen(value)+1; 296 297 /* Add col=val pair to 'rvq' */ 298 if (addSCol2RuleValue(index[n], value, rvq)) { 299 freeRuleValue(rvq, 1); 300 sfree(table); 301 if (res != 0) 302 nis_freeresult(res); 303 return (0); 304 } 305 306 break; 307 } 308 } 309 if (a >= t->numColumns) { 310 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 311 "%s: Ignoring unknown column \"%s\"", 312 myself, NIL(index[n])); 313 } 314 } 315 316 /* 317 * Find out if any of the columns specified via the 'index' 318 * array are multi-valued. 319 */ 320 for (n = 0, niv = 1; n < rvq->numColumns; n++) { 321 if (rvq->colVal[n].numVals > 1) 322 niv *= rvq->colVal[n].numVals; 323 } 324 325 /* 326 * Generate a NIS+ query, provided that the following conditions 327 * are satisfied: 328 * 329 * We were able to get information about the table, and 330 * 331 * The col=val pairs include at least one searchable 332 * column, and 333 * 334 * At most one value was supplied for each searchable 335 * column. 336 */ 337 if (res != 0 && rvq->numColumns > 0) { 338 bp2buf(myself, &b, "["); 339 340 for (n = 0, sinum = 0; n < rvq->numColumns; n++) { 341 table_obj *to = &(NIS_RES_OBJECT(res)->TA_data); 342 table_col *tc; 343 int si = -1; 344 345 for (i = 0; i < to->ta_cols.ta_cols_len; i++) { 346 tc = &to->ta_cols.ta_cols_val[i]; 347 if (strcmp(rvq->colName[n], tc->tc_name) == 0 && 348 (tc->tc_flags & TA_SEARCHABLE) != 0) { 349 si = i; 350 break; 351 } 352 } 353 354 if (si >= 0) { 355 if (rvq->colVal[n].numVals < 0) 356 continue; 357 if (rvq->colVal[n].numVals > 1) { 358 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 359 "%s: Multi-valued column \"%s\" excluded from NIS+ search", 360 myself, rvq->colName[n]); 361 continue; 362 } 363 if (sinum == 0) 364 bp2buf(myself, &b, "%s=%s", 365 rvq->colName[n], 366 rvq->colVal[n].val[0].value); 367 else 368 bp2buf(myself, &b, ",%s=%s", 369 rvq->colName[n], 370 rvq->colVal[n].val[0].value); 371 sinum++; 372 } else { 373 logmsg(MSG_NOTIMECHECK, LOG_INFO, 374 "%s: \"%s\" not searchable; not included in NIS+ query", 375 myself, index[n]); 376 } 377 } 378 bp2buf(myself, &b, "]"); 379 380 if (strcmp(b.buf, "[]") == 0) { 381 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 382 "%s: No searchable column specified; skipping NIS+ query", 383 myself); 384 nis_freeresult(res); 385 res = 0; 386 } 387 } else if (res != 0) { 388 nis_freeresult(res); 389 res = 0; 390 } 391 392 /* Query NIS+ */ 393 if (res != 0) { 394 __nis_rule_value_t *rv; 395 396 bp2buf(myself, &b, "%s", table); 397 398 logmsg(MSG_NOTIMECHECK, LOG_INFO, 399 "%s: NIS+ query: %s", myself, NIL(b.buf)); 400 401 rv = getNisPlusEntrySimple(b.buf, numVals); 402 403 /* 404 * Add values from the NIS+ entry to the ones passed in by 405 * our caller (in the form of col=value pairs). 406 */ 407 if (rv != 0 && *numVals > 1 && niv > 1) { 408 /* 409 * Since we have both multi-valued columns in the 410 * 'index' array, _and_ multiple NIS+ matches, we 411 * don't know how to combine the two in a meaningful 412 * fashion. Ignore the NIS+ values, and use the 413 * 'index' array only. 414 */ 415 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 416 "%s: At least one multi-valued input column, and multiple NIS+ matches", 417 myself); 418 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 419 "%s: Ignoring NIS+ lookup results", myself); 420 freeRuleValue(rv, *numVals); 421 rv = 0; 422 *numVals = 1; 423 } else if (rv != 0 && *numVals > 0) { 424 /* 425 * Since passed-in values override those from the 426 * NIS+ entries, we first need to delete the passed-in 427 * columns from the NIS+ data, and then add the 428 * passed-in columns/values. 429 */ 430 for (i = 0; i < rvq->numColumns; i++) { 431 for (n = 0; n < *numVals; n++) { 432 delColFromRuleValue(&rv[n], 433 rvq->colName[i]); 434 for (j = 0; j < rvq->colVal[i].numVals; 435 j++) { 436 if (addCol2RuleValue( 437 rvq->colVal[i].type, 438 rvq->colName[i], 439 rvq->colVal[i].val[j]. 440 value, 441 rvq->colVal[i].val[j]. 442 length, 443 &rv[n])) { 444 freeRuleValue(rv, 445 *numVals); 446 freeRuleValue(rvq, 1); 447 sfree(b.buf); 448 sfree(table); 449 nis_freeresult(res); 450 return (0); 451 } 452 } 453 } 454 } 455 freeRuleValue(rvq, 1); 456 rvq = rv; 457 } else { 458 if (rv != 0) { 459 /* 460 * Since we got here, '*numVals' <= 0, 461 * so it's unclear if it's safe to call 462 * freeRuleValue(). 463 */ 464 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 465 "%s: getNisPlusEntrySimple() => non-NULL, but %d elements", 466 myself, *numVals); 467 } else 468 logmsg(MSG_NOTIMECHECK, LOG_INFO, 469 "%s: No NIS+ data for \"%s\"", 470 myself, b.buf); 471 *numVals = 1; 472 } 473 nis_freeresult(res); 474 res = 0; 475 } else { 476 *numVals = 1; 477 } 478 479 sfree(b.buf); 480 sfree(table); 481 482 if (rvq->numColumns <= 0) { 483 freeRuleValue(rvq, *numVals); 484 *numVals = 0; 485 return (0); 486 } 487 488 /* 489 * If any column name was repeated in the col=val pairs (but with 490 * different values), 'rvq' will have one or more multi-valued 491 * column values. We now convert those into an array of rule-values 492 * where every column is single-valued. 493 * 494 * Since we want all combinations of column values, the number 495 * of array elements is the product of all column value counts. 496 * 497 * There are four possible combinations of 'index' and NIS+ data: 498 * 499 * (1) Only single-valued 'index' columns, and at most one NIS+ 500 * entry, so 'rvq' is complete, and '*numVals' == 1. 501 * 502 * (2) Single-valued 'index' columns, but multiple NIS+ entries. 503 * '*numVals' reflects the number of NIS+ entries, and no 504 * expansion of 'index' column values to array elements is 505 * needed. 506 * 507 * (3) At least one multi-valued 'index', and multiple NIS+ 508 * entries. We already rejected the NIS+ data for this case 509 * above, so it is in fact equivalent to case (4). 510 * 511 * (4) At least one multi-valued 'index', but at most one NIS+ 512 * entry. This is the case where we must expand the multi-valued 513 * columns to multiple array elements. 514 */ 515 if (niv > 1 && *numVals == 1) { 516 __nis_rule_value_t *rv; 517 int repeat; 518 519 /* 520 * By using initRuleValue() to create 'rv', and make each 521 * element a clone of 'rvq', we save a lot of code. The 522 * down side is that 'rv' only really needs one element 523 * for each rv[].colVal[].val array, but we know that at 524 * least one rvq->colVal[].val array has more than one 525 * element. Hence, making 'rv' a clone of 'rvq' will waste 526 * memory. 527 * 528 * However, we believe this waste is acceptable, because 529 * we expect that 'niv' will be small. Also, we are executing 530 * in the context of a utility command, not in a daemon. 531 */ 532 rv = initRuleValue(niv, rvq); 533 if (rv == 0) { 534 freeRuleValue(rvq, 1); 535 *numVals = 0; 536 return (0); 537 } 538 539 /* 540 * For each column value in 'rvq', copy to the appropriate 541 * place in 'rv', so that the end result is that all 542 * combinations of values are enumerated, and each 543 * 'rv[n].colVal[i]' is single-valued. 544 * 545 * We do this by traversing the rv[] array 'rvq->numColumns' 546 * times, where each traversal 'i' works on the values 547 * for rvq->colVal[i]. A repeat factor 'repeat' starts out 548 * at '1', and is multiplied by 'rvq->colVal[i].numVals' 549 * at the end of each traversal. Every value 550 * rvq->colVal[i].val[j] is repeated 'repeat' times. 551 * 552 * This algorithm works by regarding the rv[] array as 553 * an I-dimensional array (I = rvq->numColumns), where 554 * each dimension 'i' corresponds to the values for 555 * rvq->colVal[i]. The I-dimensional array is stored 556 * in column-major order. 557 * 558 * Since the 'rv' elements start out as copies of 'rvq', 559 * we achieve the "copy" of the 'rvq' column values by 560 * deleting those we don't want from the 'rv' elements. 561 */ 562 for (i = 0, repeat = 1; i < rvq->numColumns; i++) { 563 int r, k; 564 for (n = 0, j = 0, r = 0; n < niv; n++) { 565 /* 566 * Free all but element 'j' of the 567 * rv[n].colVal[i].val array. 568 */ 569 for (k = 0; k < rv[n].colVal[i].numVals; k++) { 570 /* Leave element 'j' in place */ 571 if (k == j) 572 continue; 573 sfree(rv[n].colVal[i].val[k]. 574 value); 575 } 576 rv[n].colVal[i].numVals = 1; 577 /* Move element 'j' to zero */ 578 if (j != 0) 579 rv[n].colVal[i].val[0] = 580 rv[n].colVal[i].val[j]; 581 582 /* 583 * Increment the repeat index 'r'. If >= 584 * 'repeat', reset 'r' and increment the 585 * value index 'j'. If 'j' >= 586 * rvq->colVal[i].numVals, start over on 587 * the column values for column 'i' (i.e., 588 * reset 'j' to zero). 589 */ 590 r += 1; 591 if (r >= repeat) { 592 r = 0; 593 j += 1; 594 if (j >= rvq->colVal[i].numVals) 595 j = 0; 596 } 597 } 598 repeat *= rvq->colVal[i].numVals; 599 } 600 601 *numVals = niv; 602 freeRuleValue(rvq, 1); 603 rvq = rv; 604 rv = 0; 605 } 606 607 q = am(myself, *numVals * sizeof (q[0])); 608 if (q == 0) { 609 freeRuleValue(rvq, *numVals); 610 return (0); 611 } 612 613 /* 614 * Create queries from the rvq[] array. 615 */ 616 for (a = 0; a < *numVals; a++) { 617 int nn, err = 0; 618 619 qc = am(myself, rvq[a].numColumns * sizeof (*qc)); 620 if (qc != 0) { 621 for (nn = 0, i = 0; i < rvq[a].numColumns; i++) { 622 for (j = 0; j < t->numColumns; j++) { 623 if (strcmp(rvq[a].colName[i], 624 t->column[j]) == 0) { 625 break; 626 } 627 } 628 if (j >= t->numColumns) 629 continue; 630 qc[nn].which_index = j; 631 if (rvq[a].colVal[i].numVals > 0) { 632 qc[nn].index_value = buildItem( 633 rvq[a].colVal[i].val[0].length, 634 rvq[a].colVal[i].val[0].value); 635 if (qc[nn].index_value == 0) 636 err++; 637 } else { 638 logmsg(MSG_NOTIMECHECK, LOG_ERR, 639 "%s: No values for [%d]%s", 640 myself, a, rvq[a].colName[i]); 641 err++; 642 } 643 nn++; 644 } 645 if (err == 0) 646 q[a] = buildQuery(nn, qc); 647 } 648 if (err > 0 || q[a] == 0) { 649 freeQueries(q, a); 650 for (a = 0; a < nn; a++) 651 freeQcomp(&qc[a], F); 652 sfree(qc); 653 freeRuleValue(rvq, *numVals); 654 return (0); 655 } 656 } 657 658 if (rvP != 0) { 659 *rvP = rvq; 660 } else { 661 freeRuleValue(rvq, 1); 662 *numVals = 0; 663 } 664 665 return (q); 666 } 667 668 void 669 printQuery(db_query *q, __nis_table_mapping_t *t) { 670 int i, mc = -1; 671 char *myself = "printQuery"; 672 char *val[NIS_MAXCOLUMNS]; 673 674 if (q == 0) 675 return; 676 677 (void) memset(val, 0, sizeof (val)); 678 679 /* 680 * Collect the values, which may be out of order in 'q'. 681 * Remember the largest index. 682 */ 683 for (i = 0; i < q->components.components_len; i++) { 684 int ix = q->components.components_val[i].which_index; 685 686 if (ix >= NIS_MAXCOLUMNS || 687 (t != 0 && ix >= t->numColumns)) 688 continue; 689 if (ix > mc) 690 mc = ix; 691 val[ix] = q->components.components_val[i].index_value-> 692 itemvalue.itemvalue_val; 693 } 694 695 /* Print the values we collected */ 696 for (i = 0; i <= mc; i++) { 697 p2buf(myself, "%s%s", (i != 0 ? " " : ""), 698 (val[i] != 0 ? val[i] : "")); 699 } 700 /* If we printed anything, add a newline */ 701 if (mc >= 0) 702 p2buf(myself, "\n"); 703 } 704 705 /* 706 * Verify that the db_query's 'q' and 'fq' match, in the sense that if 707 * they both have a value for a certain index, the values are the same. 708 */ 709 int 710 verifyQueryMatch(db_query *q, db_query *fq) { 711 int i, j, match; 712 713 if (fq == 0) 714 return (1); 715 716 if (q == 0) 717 return ((fq == 0) ? 1 : 0); 718 719 for (i = 0, match = 1; match && i < q->components.components_len; 720 i++) { 721 for (j = 0; j < fq->components.components_len; j++) { 722 int len, flen; 723 724 /* Same index ? */ 725 if (q->components.components_val[i].which_index != 726 fq->components.components_val[j]. 727 which_index) 728 continue; 729 /* 730 * If one 'index_value' is NULL, the other one must 731 * be NULL as well. 732 */ 733 if (q->components.components_val[i].index_value == 0) { 734 if (fq->components.components_val[j]. 735 index_value == 0) 736 continue; 737 else { 738 match = 0; 739 break; 740 } 741 } 742 if (fq->components.components_val[j].index_value == 743 0) { 744 match = 0; 745 break; 746 } 747 /* Same value lengths ? */ 748 len = q->components.components_val[i].index_value-> 749 itemvalue.itemvalue_len; 750 flen = fq->components.components_val[j].index_value-> 751 itemvalue.itemvalue_len; 752 if (len != flen) { 753 /* 754 * There's a twist here: the input query 755 * may well _not_ count a concluding NUL 756 * in a string value, while the output 757 * usually will. So, if the difference in 758 * length is one, and the "extra" byte is 759 * a zero-valued one, we accept equality. 760 * 'q' is assumed to be the output, and 761 * 'fq' the input. 762 */ 763 if (!(len > 0 && len == (flen+1) && 764 q->components.components_val[i]. 765 index_value-> 766 itemvalue.itemvalue_val[len-1] == 0)) { 767 match = 0; 768 break; 769 } 770 } 771 /* Same value ? */ 772 if (memcmp(q->components.components_val[i].index_value-> 773 itemvalue.itemvalue_val, 774 fq->components.components_val[j].index_value-> 775 itemvalue.itemvalue_val, 776 flen) != 0) { 777 match = 0; 778 break; 779 } 780 } 781 } 782 783 return (match); 784 } 785 786 /* 787 * Remove those queries in 'q' that don't match t->index. 788 * Returns a pointer to the filtered array, which could be 789 * a compacted version of the original, or a new copy; in 790 * the latter case, the original will have been freed. 791 * 792 * Filtered/removed db_query's are freed. 793 */ 794 db_query ** 795 filterQuery(__nis_table_mapping_t *t, db_query **q, db_query *qin, 796 __nis_obj_attr_t ***objAttr, int *numQueries) { 797 db_query **new; 798 __nis_obj_attr_t **attr; 799 int i, nq, nn; 800 char *myself = "filterQuery"; 801 802 if ((t == 0 && qin == 0) || q == 0 || 803 numQueries == 0 || *numQueries <= 0) 804 return (q); 805 806 nq = *numQueries; 807 new = am(myself, nq * sizeof (new[0])); 808 if (objAttr != 0) 809 attr = am(myself, nq * sizeof (attr[0])); 810 else 811 attr = 0; 812 if (new == 0 || (objAttr != 0 && attr == 0)) { 813 sfree(new); 814 freeQueries(q, nq); 815 sfree(attr); 816 if (objAttr != 0) { 817 freeObjAttr(*objAttr, nq); 818 *objAttr = 0; 819 } 820 *numQueries = -1; 821 return (0); 822 } 823 824 for (i = 0, nn = 0; i < nq; i++) { 825 int retain = 1; 826 827 if (t != 0) 828 retain = verifyIndexMatch(t, q[i], 0, 0, 0); 829 830 if (retain && qin != 0) 831 retain = verifyQueryMatch(q[i], qin); 832 833 if (retain) { 834 new[nn] = q[i]; 835 if (objAttr != 0) 836 attr[nn] = (*objAttr)[i]; 837 nn++; 838 } else { 839 freeQuery(q[i]); 840 q[i] = 0; 841 if (objAttr != 0) { 842 freeSingleObjAttr((*objAttr)[i]); 843 (*objAttr)[i] = 0; 844 } 845 } 846 } 847 848 /* All q[i]'s are either in 'new', or have been deleted */ 849 free(q); 850 if (objAttr != 0) { 851 sfree(*objAttr); 852 *objAttr = attr; 853 } 854 855 *numQueries = nn; 856 857 return (new); 858 } 859 860 db_query ** 861 createNisPlusEntry(__nis_table_mapping_t *t, __nis_rule_value_t *rv, 862 db_query *qin, __nis_obj_attr_t ***objAttr, 863 int *numQueries) { 864 db_query **query = 0; 865 int r, i, j, ir; 866 __nis_value_t *rval, *lval; 867 __nis_mapping_item_t *litem; 868 int numItems; 869 int nq, iqc; 870 __nis_obj_attr_t **attr = 0; 871 char **dn = 0; 872 int numDN = 0; 873 char *myself = "createNisPlusEntry"; 874 875 if (t == 0 || t->objectDN == 0 || rv == 0) 876 return (0); 877 878 /* Establish default, per-thread, search base */ 879 __nisdb_get_tsd()->searchBase = t->objectDN->read.base; 880 881 for (r = 0, nq = 0; r < t->numRulesFromLDAP; r++) { 882 int nrq, ntq, err; 883 db_query **newq; 884 __nis_obj_attr_t **newattr; 885 886 rval = buildRvalue(&t->ruleFromLDAP[r]->rhs, 887 mit_ldap, rv, NULL); 888 if (rval == 0) 889 continue; 890 891 litem = buildLvalue(&t->ruleFromLDAP[r]->lhs, &rval, 892 &numItems); 893 if (litem == 0) { 894 freeValue(rval, 1); 895 /* XXX Should this be a fatal error ? */ 896 continue; 897 } 898 899 lval = 0; 900 for (i = 0; i < numItems; i++) { 901 __nis_value_t *tmpval, *old; 902 903 tmpval = getMappingItem(&litem[i], 904 mit_nisplus, 0, 0, NULL); 905 906 /* 907 * If the LHS specifies an out-of-context LDAP or 908 * NIS+ item, we do the update right here. We 909 * don't add any values to 'lval'; instead, we 910 * skip to the next item. (However, we still 911 * get a string representation of the LHS in case 912 * we need to report an error.) 913 */ 914 if (litem[i].type == mit_nisplus && 915 litem[i].searchSpec.obj.index.numIndexes > 0) { 916 int err; 917 918 err = storeNisPlus(&litem[i], i, numItems, 919 rv, t->objName, rval); 920 if (err != NIS_SUCCESS) { 921 char *iname = "<unknown>"; 922 923 if (tmpval != 0 && 924 tmpval->numVals == 1) 925 iname = tmpval->val[0].value; 926 logmsg(MSG_NOTIMECHECK, LOG_ERR, 927 "%s: NIS+ store \"%s\": %s", 928 myself, iname, 929 nis_sperrno(err)); 930 } 931 932 freeValue(tmpval, 1); 933 continue; 934 } else if (litem[i].type == mit_ldap) { 935 int stat; 936 937 if (dn == 0) 938 dn = findDNs(myself, rv, 1, 939 t->objectDN->write.base, 940 &numDN); 941 942 stat = storeLDAP(&litem[i], i, numItems, rval, 943 t->objectDN, dn, numDN); 944 if (stat != LDAP_SUCCESS) { 945 char *iname = "<unknown>"; 946 947 if (tmpval != 0 && 948 tmpval->numVals == 1) 949 iname = tmpval->val[0].value; 950 logmsg(MSG_NOTIMECHECK, LOG_ERR, 951 "%s: LDAP store \"%s\": %s", 952 myself, iname, 953 ldap_err2string(stat)); 954 } 955 956 freeValue(tmpval, 1); 957 continue; 958 } 959 960 old = lval; 961 lval = concatenateValues(old, tmpval); 962 freeValue(tmpval, 1); 963 freeValue(old, 1); 964 } 965 966 freeMappingItem(litem, numItems); 967 if (lval == 0 || lval->numVals <= 0 || rval->numVals <= 0) { 968 freeValue(lval, 1); 969 freeValue(rval, 1); 970 continue; 971 } 972 973 /* 974 * We now have a number of possible cases. The notation 975 * used in the table is: 976 * 977 * single A single value (numVals == 1) 978 * single/rep A single value with repeat == 1 979 * multi[N] N values 980 * multi[N]/rep M values with repeat == 1 981 * (M) M resulting db_query's 982 * 983 * lval \ rval single single/rep multi[N] multi[N]/rep 984 * single (1) (1) (1) (1) 985 * single/rep (1) (1) (N) (N) 986 * multi[M] (1) (1) (1) 1+(N-1)/M 987 * multi[M]/rep (1) (1) (1) 1+(N-1)/M 988 * 989 * Of course, we already have 'nq' db_query's from previous 990 * rules, so the resulting number of queries is max(1,nq) 991 * times the numbers in the table above. 992 */ 993 994 /* The number of queries resulting from the current rule */ 995 if (rval->numVals > 1) { 996 if (lval->numVals == 1 && lval->repeat) 997 nrq = rval->numVals; 998 else if (lval->numVals > 1 && rval->repeat) 999 nrq = 1 + ((rval->numVals-1)/lval->numVals); 1000 else 1001 nrq = 1; 1002 } else { 1003 nrq = 1; 1004 } 1005 1006 /* Total number of queries after adding the current rule */ 1007 if (nq <= 0) 1008 ntq = nrq; 1009 else 1010 ntq = nq * nrq; 1011 1012 if (ntq > nq) { 1013 newq = realloc(query, ntq * sizeof (query[0])); 1014 newattr = realloc(attr, ntq * sizeof (attr[0])); 1015 if (newq == 0 || newattr == 0) { 1016 logmsg(MSG_NOMEM, LOG_ERR, 1017 "%s: realloc(%d) => NULL", 1018 myself, ntq * sizeof (query[0])); 1019 freeValue(lval, 1); 1020 freeValue(rval, 1); 1021 freeQueries(query, nq); 1022 freeObjAttr(attr, nq); 1023 sfree(newq); 1024 freeDNs(dn, numDN); 1025 return (0); 1026 } 1027 query = newq; 1028 attr = newattr; 1029 } 1030 1031 /* 1032 * Copy/clone the existing queries to the new array, 1033 * remembering that realloc() has done the first 'nq' 1034 * ones. 1035 * 1036 * If there's an error (probably memory allocation), we 1037 * still go through the rest of the array, so that it's 1038 * simple to free the elements when we clean up. 1039 */ 1040 for (i = 1, err = 0; i < nrq; i++) { 1041 for (j = 0; j < nq; j++) { 1042 query[(nq*i)+j] = cloneQuery(query[j], 1043 t->numColumns); 1044 if (query[(nq*i)+j] == 0 && 1045 query[j] != 0) 1046 err++; 1047 attr[(nq*i)+j] = cloneObjAttr(attr[j]); 1048 if (attr[(nq*i)+j] == 0 && 1049 attr[j] != 0) 1050 err++; 1051 } 1052 } 1053 1054 if (err > 0) { 1055 freeValue(lval, 1); 1056 freeValue(rval, 1); 1057 freeQueries(query, ntq); 1058 freeObjAttr(attr, ntq); 1059 freeDNs(dn, numDN); 1060 return (0); 1061 } 1062 1063 /* 1064 * Special case if nq == 0 (i.e., the first time we 1065 * allocated db_query's). If so, we now allocate empty 1066 * db_qcomp arrays, which simplifies subsequent 1067 * copying of values. 1068 */ 1069 if (nq <= 0) { 1070 (void) memset(query, 0, ntq * sizeof (query[0])); 1071 (void) memset(attr, 0, ntq * sizeof (attr[0])); 1072 for (i = 0, err = 0; i < ntq; i++) { 1073 query[i] = am(myself, sizeof (*query[i])); 1074 if (query[i] == 0) { 1075 err++; 1076 break; 1077 } 1078 query[i]->components.components_val = 1079 am(myself, t->numColumns * 1080 sizeof (query[i]->components.components_val[0])); 1081 if (query[i]->components.components_val == 0) { 1082 err++; 1083 break; 1084 } 1085 query[i]->components.components_len = 0; 1086 } 1087 if (err > 0) { 1088 freeValue(lval, 1); 1089 freeValue(rval, 1); 1090 freeQueries(query, ntq); 1091 freeObjAttr(attr, ntq); 1092 freeDNs(dn, numDN); 1093 return (0); 1094 } 1095 } 1096 1097 /* Now we're ready to add the new values */ 1098 for (i = 0, ir = 0; i < lval->numVals; i++) { 1099 char *oaName = 0; 1100 int index; 1101 1102 /* Find column index */ 1103 for (index = 0; index < t->numColumns; 1104 index++) { 1105 if (strncmp(t->column[index], 1106 lval->val[i].value, 1107 lval->val[i].length) == 0) 1108 break; 1109 } 1110 if (index >= t->numColumns) { 1111 /* 1112 * Could be one of the special object 1113 * attributes. 1114 */ 1115 oaName = isObjAttr(&lval->val[i]); 1116 if (oaName == 0) 1117 continue; 1118 } 1119 1120 for (j = i*nrq; j < (i+1)*nrq; j++) { 1121 int k; 1122 1123 /* If we're out of values, repeat last one */ 1124 ir = (j < rval->numVals) ? 1125 j : rval->numVals - 1; 1126 1127 /* 1128 * Step through the query array, adding 1129 * the new value every 'nrq' queries, and 1130 * starting at 'query[j % nrq]'. 1131 */ 1132 for (k = j % nrq, err = 0; k < ntq; k += nrq) { 1133 int ic, c; 1134 1135 if (oaName != 0) { 1136 int fail = setObjAttrField( 1137 oaName, 1138 &rval->val[ir], 1139 &attr[k]); 1140 if (fail) { 1141 err++; 1142 break; 1143 } 1144 continue; 1145 } 1146 1147 ic = query[k]->components. 1148 components_len; 1149 /* 1150 * If we've already filled this 1151 * query, the new value is a dup 1152 * which we'll ignore. 1153 */ 1154 if (ic >= t->numColumns) 1155 continue; 1156 1157 /* 1158 * Do we already have a value for 1159 * this 'index' ? 1160 */ 1161 for (c = 0; c < ic; c++) { 1162 if (query[k]->components. 1163 components_val[c]. 1164 which_index == index) 1165 break; 1166 } 1167 1168 /* If no previous value, add it */ 1169 if (c >= ic) { 1170 int l; 1171 char *v; 1172 1173 query[k]->components. 1174 components_val[ic]. 1175 which_index = index; 1176 l = rval->val[ir].length; 1177 v = rval->val[ir].value; 1178 if (rval->type == vt_string && 1179 l > 0 && 1180 v[l-1] != '\0' && 1181 v[l] == '\0') 1182 l++; 1183 query[k]->components. 1184 components_val[ic]. 1185 index_value = 1186 buildItem(l, v); 1187 if (query[k]-> 1188 components. 1189 components_val[ic]. 1190 index_value == 0) { 1191 err++; 1192 break; 1193 } 1194 query[k]->components. 1195 components_len++; 1196 } 1197 } 1198 if (err > 0) { 1199 freeValue(lval, 1); 1200 freeValue(rval, 1); 1201 freeQueries(query, ntq); 1202 freeObjAttr(attr, ntq); 1203 freeDNs(dn, numDN); 1204 return (0); 1205 } 1206 } 1207 } 1208 freeValue(lval, 1); 1209 freeValue(rval, 1); 1210 1211 nq = ntq; 1212 } 1213 1214 freeDNs(dn, numDN); 1215 1216 if (nq <= 0) { 1217 sfree(query); 1218 query = 0; 1219 } 1220 1221 /* Should we filter on index or input query ? */ 1222 if (query != 0) { 1223 if (t->index.numIndexes > 0) 1224 query = filterQuery(t, query, qin, &attr, &nq); 1225 else if (qin != 0) 1226 query = filterQuery(0, query, qin, &attr, &nq); 1227 } 1228 1229 if (query != 0 && numQueries != 0) 1230 *numQueries = nq; 1231 1232 if (objAttr != 0) 1233 *objAttr = attr; 1234 else 1235 freeObjAttr(attr, nq); 1236 1237 return (query); 1238 } 1239 /* 1240 * Given a table mapping and a rule-value, convert to an array of 1241 * (db_query *), using the fromLDAP ruleset. 1242 * 1243 * On entry, '*numQueries' holds the number of elements in the 'rv' 1244 * array. On exit, it holds the number of (db_query *)'s in the return 1245 * value array. 1246 */ 1247 db_query ** 1248 ruleValue2Query(__nis_table_mapping_t *t, __nis_rule_value_t *rv, 1249 db_query *qin, __nis_obj_attr_t ***objAttr, int *numQueries) { 1250 db_query **q = 0, ***qp = 0; 1251 int i, nqp, nq, *nnp = 0, nv; 1252 __nis_obj_attr_t **attr = 0, ***atp = 0; 1253 char *myself = "ruleValue2Query"; 1254 1255 1256 if (t == 0 || rv == 0 || numQueries == 0) 1257 return (0); 1258 1259 nv = *numQueries; 1260 if (nv <= 0) 1261 return (0); 1262 1263 /* 1264 * 'qp' is an array of (db_query **), and we get one element for 1265 * each call to createNisPlusEntry(); i.e., one for each rule-value. 1266 * 1267 * 'nnp[i]' is the count of (db_query *) in each 'qp[i]'. 1268 */ 1269 qp = am(myself, nv * sizeof (*qp)); 1270 nnp = am(myself, nv * sizeof (*nnp)); 1271 atp = am(myself, nv * sizeof (*atp)); 1272 if (qp == 0 || nnp == 0 || atp == 0) { 1273 sfree(qp); 1274 sfree(nnp); 1275 sfree(atp); 1276 return (0); 1277 } 1278 1279 for (i = 0, nq = 0, nqp = 0; i < nv; i++) { 1280 qp[nqp] = createNisPlusEntry(t, &rv[i], qin, &atp[nqp], 1281 &nnp[nqp]); 1282 /* If we fail, abort (XXX??? or continue ???) */ 1283 if (qp[nqp] == 0) 1284 goto cleanup; 1285 nq += nnp[nqp]; 1286 nqp++; 1287 } 1288 1289 /* If we didn't get any (db_query **)'s, return failure */ 1290 if (nqp == 0 || nq <= 0) 1291 goto cleanup; 1292 1293 q = am(myself, nq * sizeof (q[0])); 1294 attr = am(myself, nq * sizeof (attr[0])); 1295 if (q == 0 || attr == 0) { 1296 nq = 0; 1297 goto cleanup; 1298 } 1299 1300 /* Convert 'qp' to an array of (db_query *)'s */ 1301 for (i = 0, nq = 0; i < nqp; i++) { 1302 (void) memcpy(&q[nq], qp[i], nnp[i] * sizeof (qp[i][0])); 1303 (void) memcpy(&attr[nq], atp[i], nnp[i] * sizeof (atp[i][0])); 1304 nq += nnp[i]; 1305 free(qp[i]); 1306 free(atp[i]); 1307 } 1308 1309 *numQueries = nq; 1310 if (objAttr != 0) 1311 *objAttr = attr; 1312 else 1313 freeObjAttr(attr, nq); 1314 1315 /* Make sure 'cleanup' doesn't free the db_query pointers */ 1316 nqp = 0; 1317 1318 cleanup: 1319 for (i = 0; i < nqp; i++) { 1320 freeQueries(qp[i], nnp[i]); 1321 sfree(atp[i]); 1322 } 1323 sfree(qp); 1324 sfree(nnp); 1325 sfree(atp); 1326 1327 return (q); 1328 } 1329 1330 db_query * 1331 pseudoEntryObj2Query(entry_obj *e, nis_object *tobj, __nis_rule_value_t *rv) { 1332 db_query *qbuf; 1333 db_qcomp *qcbuf; 1334 int nc, i; 1335 __nis_rule_value_t *rvt = 0; 1336 char *myself = "pseudoEntryObj2Query"; 1337 1338 nc = e->en_cols.en_cols_len - 1; 1339 1340 if (e == 0 || nc < 0 || nc > NIS_MAXCOLUMNS) 1341 return (0); 1342 1343 /* 1344 * If 'rvP' is non-NULL, build a rule value from the pseudo- 1345 * nis_object in e->en_cols.en_cols_val[0]. 1346 */ 1347 if (rv != 0) { 1348 nis_object *o; 1349 1350 o = unmakePseudoEntryObj(e, tobj); 1351 if (o == 0) 1352 return (0); 1353 rvt = addObjAttr2RuleValue(o, 0); 1354 nis_destroy_object(o); 1355 if (rvt == 0) 1356 return (0); 1357 } 1358 1359 qbuf = am(myself, sizeof (*qbuf)); 1360 /* 1361 * If there are no columns (other than the pseudo-entry object), 1362 * we're done. 1363 */ 1364 if (nc == 0) 1365 return (qbuf); 1366 1367 qcbuf = am(myself, nc * sizeof (*qcbuf)); 1368 if (qcbuf == 0) { 1369 sfree(qcbuf); 1370 if (rvt != 0) 1371 freeRuleValue(rvt, 1); 1372 return (0); 1373 } 1374 1375 /* 1376 * Build the db_query, remembering that e->en_cols.en_cols_val[0] 1377 * is the pseudo-nis_object. 1378 */ 1379 qbuf->components.components_val = qcbuf; 1380 qbuf->components.components_len = nc; 1381 for (i = 0; i < nc; i++) { 1382 qcbuf[i].which_index = i; 1383 qcbuf[i].index_value = buildItem( 1384 e->en_cols.en_cols_val[i+1].ec_value.ec_value_len, 1385 e->en_cols.en_cols_val[i+1].ec_value.ec_value_val); 1386 if (qcbuf[i].index_value == 0) { 1387 freeQuery(qbuf); 1388 if (rvt != 0) 1389 freeRuleValue(rvt, 1); 1390 return (0); 1391 } 1392 } 1393 1394 if (rvt != 0) { 1395 *rv = *rvt; 1396 sfree(rvt); 1397 } 1398 1399 return (qbuf); 1400 } 1401 1402 /* 1403 * Given an input query 'q', and a db_query work buffer 'qbuf', return 1404 * a pointer to a query with one component corresponding to component 1405 * 'index' in 'q'. 1406 * 1407 * Note that no memory is allocated, and that the returned query has 1408 * pointers into 'q'. 1409 */ 1410 db_query * 1411 queryFromComponent(db_query *q, int index, db_query *qbuf) { 1412 1413 if (q == 0 || index < 0 || index >= q->components.components_len || 1414 qbuf == 0) 1415 return (0); 1416 1417 qbuf->components.components_len = 1; 1418 qbuf->components.components_val = &q->components.components_val[index]; 1419 1420 return (qbuf); 1421 } 1422