xref: /illumos-gate/usr/src/lib/libnisdb/ldap_nisdbquery.c (revision 0245b61fd282e95735b173b8d95be0d6688163b4)
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 
28 #include <strings.h>
29 #include <string.h>
30 #include <lber.h>
31 #include <ldap.h>
32 
33 #include "db_item_c.h"
34 
35 #include "nisdb_mt.h"
36 
37 #include "ldap_util.h"
38 #include "ldap_structs.h"
39 #include "ldap_val.h"
40 #include "ldap_ruleval.h"
41 #include "ldap_op.h"
42 #include "ldap_nisdbquery.h"
43 #include "ldap_attr.h"
44 #include "ldap_xdr.h"
45 #include "ldap_ldap.h"
46 
47 
48 item *
49 buildItem(int len, void *value) {
50 	char	*myself = "buildItem";
51 	item	*i = am(myself, sizeof (*i));
52 	int	mlen = len;
53 
54 	if (i == 0)
55 		return (0);
56 
57 	/*
58 	 * To this function, a NULL value, or a length less than or equal
59 	 * zero means an item with no value. Hence, buildItem(0, 0) is
60 	 * _not_ the right way to create index_value == 0 to indicate
61 	 * deletion.
62 	 */
63 	if (value == 0 || len <= 0) {
64 		i->itemvalue.itemvalue_len = 0;
65 		i->itemvalue.itemvalue_val = 0;
66 		return (i);
67 	}
68 
69 	/*
70 	 * NIS+ usually stores the terminating NUL for strings, so we add
71 	 * it here just in case. This means we usually waste a byte for
72 	 * binary column values...
73 	 */
74 	if (len > 0 && ((char *)value)[len-1] != '\0')
75 		mlen++;
76 
77 	i->itemvalue.itemvalue_len = len;
78 	i->itemvalue.itemvalue_val = am(myself, mlen);
79 	if (mlen > 0 && i->itemvalue.itemvalue_val == 0) {
80 		free(i);
81 		return (0);
82 	}
83 	memcpy(i->itemvalue.itemvalue_val, value, len);
84 
85 	return (i);
86 }
87 
88 void
89 freeItem(item *i) {
90 	if (i != 0) {
91 		sfree(i->itemvalue.itemvalue_val);
92 		free(i);
93 	}
94 }
95 
96 void
97 freeQcomp(db_qcomp *qc, int doFree) {
98 
99 	if (qc == 0)
100 		return;
101 
102 	freeItem(qc->index_value);
103 	if (doFree)
104 		free(qc);
105 }
106 
107 db_query *
108 buildQuery(int num_components, db_qcomp *components) {
109 	char		*myself = "buildQuery";
110 	db_query	*q = am(myself, sizeof (*q));
111 
112 	if (q == 0)
113 		return (0);
114 
115 	q->components.components_len = num_components;
116 	q->components.components_val = components;
117 
118 	return (q);
119 }
120 
121 /*
122  * Clone a db_query. The 'numComps' parameter can be used to specify
123  * the number of db_qcomp's to allocate (in the 'components.components_val'
124  * array), if 'components.components_len' hasn't yet reached its expected
125  * maximum value.
126  */
127 db_query *
128 cloneQuery(db_query *old, int numComps) {
129 	db_query	*new;
130 	int		i;
131 	char		*myself = "cloneQuery";
132 
133 	if (old == 0)
134 		return (0);
135 
136 	new = am(myself, sizeof (*new));
137 	if (new == 0)
138 		return (0);
139 
140 	if (old->components.components_len > numComps)
141 		numComps = old->components.components_len;
142 
143 	new->components.components_val = am(myself,
144 				sizeof (new->components.components_val[0]) *
145 				numComps);
146 	if (numComps > 0 && new->components.components_val == 0) {
147 		free(new);
148 		return (0);
149 	}
150 
151 	for (i = 0; i < old->components.components_len; i++) {
152 		item	*it;
153 
154 		if (old->components.components_val[i].index_value == 0) {
155 			new->components.components_val[i].index_value = 0;
156 			new->components.components_val[i].which_index =
157 				old->components.components_val[i].which_index;
158 			continue;
159 		}
160 
161 		it = buildItem(old->components.components_val[i].index_value->
162 					itemvalue.itemvalue_len,
163 				old->components.components_val[i].index_value->
164 					itemvalue.itemvalue_val);
165 
166 		if (it == 0) {
167 			new->components.components_len = i + 1;
168 			freeQuery(new);
169 			return (0);
170 		}
171 
172 		new->components.components_val[i].index_value = it;
173 		new->components.components_val[i].which_index =
174 			old->components.components_val[i].which_index;
175 	}
176 
177 	new->components.components_len = old->components.components_len;
178 
179 	return (new);
180 }
181 
182 void
183 freeQuery(db_query *q) {
184 	int	i;
185 
186 	if (q == 0)
187 		return;
188 
189 	for (i = 0; i < q->components.components_len; i++) {
190 		freeItem(q->components.components_val[i].index_value);
191 	}
192 
193 	sfree(q->components.components_val);
194 	sfree(q);
195 }
196 
197 void
198 freeQueries(db_query **q, int numQ) {
199 	int	i;
200 
201 	if (q == 0)
202 		return;
203 
204 	for (i = 0; i < numQ; i++)
205 		freeQuery(q[i]);
206 
207 	sfree(q);
208 }
209 
210 /*
211  * Given an array index[0..num-1] of pointers to strings of the form
212  * "name=value", create the corresponding db_queries. "name=" indicates
213  * deletion, which results in a db_query component where index_value == 0.
214  *
215  * The __nis_table_mapping_t structure is used to translate column
216  * names to indices.
217  *
218  * If 'rvP' is non-NULL, the searchable columns from the 'index'
219  * name/value pairs are used to retrieve copies of the corresponding NIS+
220  * entries, and '*rvP' is initialized with the current entry values
221  * and object attributes. Names/values supplied in 'index' override
222  * those from existing NIS+ entries.
223  */
224 db_query **
225 createQuery(int num, char **index, __nis_table_mapping_t *t,
226 		__nis_rule_value_t **rvP, int *numVals) {
227 	db_query		**q;
228 	db_qcomp		*qc;
229 	int			i, j, n, a, nv, niv;
230 	__nis_rule_value_t	*rvq;
231 	__nis_buffer_t		b = {0, 0};
232 	char			*table = 0;
233 	char			*myself = "createQuery";
234 
235 	rvq = initRuleValue(1, 0);
236 	if (rvq == 0)
237 		return (0);
238 
239 	if (numVals == 0)
240 		numVals = &nv;
241 	*numVals = 0;
242 
243 	if (rvP != 0) {
244 		/*
245 		 * Try to obtain a copy of the table object, in order to
246 		 * determine the searchable columns. A failure isn't
247 		 * necessarily fatal; we just try to compose the entire
248 		 * LDAP data from the col=val pairs.
249 		 */
250 		table = fullObjName(F, t->objName);
251 		if (table == 0) {
252 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
253 				"%s: Error converting \"%s\" to FQ object name",
254 				myself, NIL(t->objName));
255 			freeRuleValue(rvq, 1);
256 			return (0);
257 		}
258 	}
259 
260 	/* Create a rule-value from the col=val pairs */
261 	for (n = 0; n < num; n++) {
262 		char	*value;
263 
264 		if ((value = strchr(index[n], '=')) == 0) {
265 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
266 				"%s: no '=' in \"%s\"",
267 				myself, index[n]);
268 			continue;
269 		}
270 
271 		*value = '\0';
272 		value++;
273 
274 		for (a = 0; a < t->numColumns; a++) {
275 			if (strcmp(index[n], t->column[a]) == 0) {
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
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
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 **
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 **
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;
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 **
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 *
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 *
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