Home | History | Annotate | Download | only in libnisdb
      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;