xref: /illumos-gate/usr/src/lib/libnisdb/ldap_nisdbquery.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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