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