xref: /illumos-gate/usr/src/lib/libnisdb/ldap_ruleval.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 <lber.h>
30 #include <ldap.h>
31 #include <strings.h>
32 
33 #include "nisdb_mt.h"
34 
35 #include "ldap_util.h"
36 #include "ldap_val.h"
37 #include "ldap_attr.h"
38 #include "ldap_ldap.h"
39 #include "ldap_nisplus.h"
40 #include "ldap_ruleval.h"
41 
42 
43 /*
44  * Free an array of 'count' rule-value elements.
45  */
46 void
47 freeRuleValue(__nis_rule_value_t *rv, int count) {
48 	int	n, i, j;
49 
50 	if (rv == 0)
51 		return;
52 
53 	for (n = 0; n < count; n++) {
54 
55 		if (rv[n].colName != 0) {
56 			for (i = 0; i < rv[n].numColumns; i++) {
57 				sfree(rv[n].colName[i]);
58 			}
59 			free(rv[n].colName);
60 		}
61 		if (rv[n].colVal != 0) {
62 			for (i = 0; i < rv[n].numColumns; i++) {
63 				for (j = 0; j < rv[n].colVal[i].numVals; j++) {
64 					sfree(rv[n].colVal[i].val[j].value);
65 				}
66 				if (rv[n].colVal[i].numVals > 0)
67 					sfree(rv[n].colVal[i].val);
68 			}
69 			free(rv[n].colVal);
70 		}
71 
72 		if (rv[n].attrName != 0) {
73 			for (i = 0; i < rv[n].numAttrs; i++) {
74 				sfree(rv[n].attrName[i]);
75 			}
76 			free(rv[n].attrName);
77 		}
78 		if (rv[n].attrVal != 0) {
79 			for (i = 0; i < rv[n].numAttrs; i++) {
80 				for (j = 0; j < rv[n].attrVal[i].numVals;
81 						j++) {
82 					sfree(rv[n].attrVal[i].val[j].value);
83 				}
84 				if (rv[n].attrVal[i].numVals > 0)
85 					sfree(rv[n].attrVal[i].val);
86 			}
87 			free(rv[n].attrVal);
88 		}
89 
90 	}
91 	sfree(rv);
92 }
93 
94 /*
95  * Return an array of 'count' __nis_rule_value_t elements, initialized
96  * to be copies of 'rvIn' if supplied; empty otherwise.
97  */
98 __nis_rule_value_t *
99 initRuleValue(int count, __nis_rule_value_t *rvIn) {
100 	return (growRuleValue(0, count, 0, rvIn));
101 }
102 
103 static const __nis_rule_value_t	rvZero = {0};
104 
105 /*
106  * Grow 'old' from 'oldCount' to 'newCount' elements, initialize the
107  * new portion to 'rvIn' (empty if not supplied), and return a pointer
108  * to the result. Following a call to this function, the caller must
109  * refer only to the returned array, not to 'old'.
110  */
111 __nis_rule_value_t *
112 growRuleValue(int oldCount, int newCount, __nis_rule_value_t *old,
113 		__nis_rule_value_t *rvIn) {
114 	__nis_rule_value_t	*rv;
115 	int			i, j;
116 	char			*myself = "growRuleValue";
117 
118 	if (newCount <= 0 || newCount <= oldCount)
119 		return (old);
120 
121 	if (oldCount <= 0) {
122 		oldCount = 0;
123 		old = 0;
124 	}
125 
126 	if (rvIn == 0)
127 		rvIn = (__nis_rule_value_t *)&rvZero;
128 
129 	rv = realloc(old, newCount * sizeof (rv[0]));
130 	if (rv == 0) {
131 		logmsg(MSG_NOMEM, LOG_ERR,
132 			"%s: realloc(%d ((%d+%d)*%d)) => 0",
133 			myself, (oldCount+newCount) * sizeof (rv[0]),
134 			oldCount, newCount, sizeof (rv[0]));
135 		freeRuleValue(old, oldCount);
136 		return (0);
137 	}
138 
139 	(void) memset(&rv[oldCount], 0, (newCount-oldCount)*sizeof (rv[0]));
140 
141 	for (i = oldCount; i < newCount; i++) {
142 		rv[i].numColumns = rvIn->numColumns;
143 		if (rv[i].numColumns > 0) {
144 			rv[i].colName = cloneName(rvIn->colName,
145 					rv[i].numColumns);
146 			rv[i].colVal = cloneValue(rvIn->colVal,
147 					rv[i].numColumns);
148 		}
149 		if (rv[i].numColumns > 0 &&
150 				(rv[i].colName == 0 || rv[i].colVal == 0)) {
151 			freeRuleValue(rv, i);
152 			return (0);
153 		}
154 		rv[i].numAttrs = rvIn->numAttrs;
155 		rv[i].attrName = cloneName(rvIn->attrName, rv[i].numAttrs);
156 		rv[i].attrVal = cloneValue(rvIn->attrVal, rv[i].numAttrs);
157 		if (rv[i].numAttrs > 0 &&
158 			(rv[i].attrName == 0 || rv[i].attrVal == 0)) {
159 			freeRuleValue(rv, i);
160 			return (0);
161 		}
162 	}
163 
164 	return (rv);
165 }
166 
167 /*
168  * Merge the source rule-value 's' into the target rule-value 't'.
169  * If successful, unless 's' is a sub-set of 't', 't' will be changed
170  * on exit, and will contain the values from 's' as well.
171  */
172 int
173 mergeRuleValue(__nis_rule_value_t *t, __nis_rule_value_t *s) {
174 	int	i, j;
175 
176 	if (s == 0)
177 		return (0);
178 	else if (t == 0)
179 		return (-1);
180 
181 	for (i = 0; i < s->numColumns; i++) {
182 		for (j = 0; j < s->colVal[i].numVals; j++) {
183 			if (addCol2RuleValue(s->colVal[i].type, s->colName[i],
184 					s->colVal[i].val[j].value,
185 					s->colVal[i].val[j].length,
186 					t))
187 				return (-1);
188 		}
189 	}
190 
191 	for (i = 0; i < s->numAttrs; i++) {
192 		for (j = 0; j < s->attrVal[i].numVals; j++) {
193 			if (addAttr2RuleValue(s->attrVal[i].type,
194 					s->attrName[i],
195 					s->attrVal[i].val[j].value,
196 					s->attrVal[i].val[j].length,
197 					t))
198 				return (-1);
199 		}
200 	}
201 
202 	return (0);
203 }
204 
205 static int
206 addVal2RuleValue(char *msg, int caseSens, int snipNul, __nis_value_type_t type,
207 		char *name, void *value, int valueLen,
208 		int *numP, char ***inNameP, __nis_value_t **inValP) {
209 	int			i, j, copyLen = valueLen;
210 	__nis_single_value_t	*v;
211 	char			**inName = *inNameP;
212 	__nis_value_t		*inVal = *inValP;
213 	int			num = *numP;
214 	int			(*comp)(const char *s1, const char *s2);
215 	char			*myself = "addVal2RuleValue";
216 
217 	/* Internal function, so assume arguments OK */
218 
219 	if (msg == 0)
220 		msg = myself;
221 
222 	/* Should we match the 'inName' value case sensitive or not ? */
223 	if (caseSens)
224 		comp = strcmp;
225 	else
226 		comp = strcasecmp;
227 
228 	/*
229 	 * String-valued NIS+ entries count the concluding NUL in the
230 	 * length, while LDAP entries don't. In order to support this,
231 	 * we implement the following for vt_string value types:
232 	 *
233 	 * If the last byte of the value isn't a NUL, add one to the
234 	 * allocated length, so that there always is a NUL after the
235 	 * value, making it safe to pass to strcmp() etc.
236 	 *
237 	 * If 'snipNul' is set (presumably meaning we're inserting a
238 	 * value derived from a NIS+ entry), and the last byte of the
239 	 * value already is a NUL, decrement the length to be copied by
240 	 * one. This (a) doesn't count the NUL in the value length, but
241 	 * (b) still leaves a NUL following the value.
242 	 *
243 	 * In N2L, for all cases we set 'copyLen' to the number of non-0
244 	 * characters in 'value'.
245 	 */
246 	if (type == vt_string && valueLen > 0) {
247 		char	*charval = value;
248 
249 		if (charval[valueLen-1] != '\0')
250 			valueLen += 1;
251 		else if (yp2ldap || snipNul)
252 			copyLen -= 1;
253 	} else if (valueLen == 0) {
254 		/*
255 		 * If the 'value' pointer is non-NULL, we create a zero-
256 		 * length value with one byte allocated. This takes care
257 		 * of empty strings.
258 		 */
259 		valueLen += 1;
260 	}
261 
262 	/* If we already have values for this attribute, add another one */
263 	for (i = 0; i < num; i++) {
264 		if ((*comp)(inName[i], name) == 0) {
265 
266 			/*
267 			 * Our caller often doesn't know the type of the
268 			 * value; this happens because the type (vt_string
269 			 * or vt_ber) is determined by the format in the
270 			 * rule sets, and we may be invoked as a preparation
271 			 * for evaluating the rules. Hence, we only use the
272 			 * supplied 'type' if we need to create a value.
273 			 * Otherwise, we accept mixed types.
274 			 *
275 			 * Strings are OK in any case, since we always make
276 			 * sure to have a zero byte at the end of any value,
277 			 * whatever the type.
278 			 */
279 
280 			if (inVal[i].numVals < 0) {
281 				/*
282 				 * Used to indicate deletion of attribute,
283 				 * so we honor that and don't add a value.
284 				 */
285 				return (0);
286 			}
287 
288 			/*
289 			 * If 'value' is NULL, we should delete, so
290 			 * remove any existing values, and set the
291 			 * 'numVals' field to -1.
292 			 */
293 			if (value == 0) {
294 				for (j = 0; j < inVal[i].numVals; j++) {
295 					sfree(inVal[i].val[j].value);
296 				}
297 				sfree(inVal[i].val);
298 				inVal[i].val = 0;
299 				inVal[i].numVals = -1;
300 				return (0);
301 			}
302 
303 			/* Is the value a duplicate ? */
304 			for (j = 0; j < inVal[i].numVals; j++) {
305 				if (copyLen == inVal[i].val[j].length &&
306 					memcmp(value, inVal[i].val[j].value,
307 						copyLen) == 0) {
308 					break;
309 				}
310 			}
311 			if (j < inVal[i].numVals)
312 				return (0);
313 
314 			/* Not a duplicate, so add the name/value pair */
315 			v = realloc(inVal[i].val,
316 					(inVal[i].numVals+1) *
317 					sizeof (inVal[i].val[0]));
318 			if (v == 0)
319 				return (-1);
320 			inVal[i].val = v;
321 			v[inVal[i].numVals].length = copyLen;
322 			v[inVal[i].numVals].value = am(msg, valueLen);
323 			if (v[inVal[i].numVals].value == 0 &&
324 					value != 0) {
325 				sfree(v);
326 				return (-1);
327 			}
328 			memcpy(v[inVal[i].numVals].value, value, copyLen);
329 			inVal[i].numVals++;
330 
331 			return (0);
332 		}
333 	}
334 
335 	/* No previous value for this attribute */
336 
337 	/*
338 	 * value == 0 means deletion, in which case we create a
339 	 * __nis_value_t with the numVals field set to -1.
340 	 */
341 	if (value != 0) {
342 		if ((v = am(msg, sizeof (*v))) == 0)
343 			return (-1);
344 		v->length = copyLen;
345 		v->value = am(msg, valueLen);
346 		if (v->value == 0 && value != 0) {
347 			sfree(v);
348 			return (-1);
349 		}
350 		memcpy(v->value, value, copyLen);
351 	}
352 
353 	inVal = realloc(inVal, (num+1)*sizeof (inVal[0]));
354 	if (inVal == 0) {
355 		if (value != 0) {
356 			sfree(v->value);
357 			sfree(v);
358 		}
359 		return (-1);
360 	}
361 	*inValP = inVal;
362 
363 	inName = realloc(inName,
364 		(num+1)*sizeof (inName[0]));
365 	if (inName == 0 || (inName[num] =
366 			sdup(msg, T, name)) == 0) {
367 		sfree(v->value);
368 		sfree(v);
369 		return (-1);
370 	}
371 	*inNameP = inName;
372 
373 	inVal[num].type = type;
374 	inVal[num].repeat = 0;
375 	if (value != 0) {
376 		inVal[num].numVals = 1;
377 		inVal[num].val = v;
378 	} else {
379 		inVal[num].numVals = -1;
380 		inVal[num].val = 0;
381 	}
382 
383 	*numP += 1;
384 
385 	return (0);
386 }
387 
388 int
389 addAttr2RuleValue(__nis_value_type_t type, char *name, void *value,
390 		int valueLen, __nis_rule_value_t *rv) {
391 	char			*myself = "addAttr2RuleValue";
392 
393 	if (name == 0 || rv == 0)
394 		return (-1);
395 
396 	return (addVal2RuleValue(myself, 0, 0, type, name, value, valueLen,
397 				&rv->numAttrs, &rv->attrName, &rv->attrVal));
398 }
399 
400 int
401 addSAttr2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
402 	return (addAttr2RuleValue(vt_string, name, value, slen(value), rv));
403 }
404 
405 int
406 addCol2RuleValue(__nis_value_type_t type, char *name, void *value,
407 		int valueLen, __nis_rule_value_t *rv) {
408 	char *myself = "addCol2RuleValue";
409 
410 	if (name == 0 || rv == 0)
411 		return (-1);
412 
413 	return (addVal2RuleValue(myself, 1, 1, type, name, value, valueLen,
414 				&rv->numColumns, &rv->colName, &rv->colVal));
415 }
416 
417 int
418 addSCol2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
419 	return (addCol2RuleValue(vt_string, name, value, slen(value), rv));
420 }
421 
422 /*
423  * Given a table mapping, a NIS+ DB query, and (optionally) an existing
424  * and compatible __nis_rule_value_t, return a new __nis_rule_value_t
425  * with the values from the query added.
426  */
427 __nis_rule_value_t *
428 buildNisPlusRuleValue(__nis_table_mapping_t *t, db_query *q,
429 			__nis_rule_value_t *rv) {
430 	int			i;
431 	__nis_single_value_t	*sv;
432 	char			*myself = "buildNisPlusRuleValue";
433 
434 	if (t == 0 || q == 0)
435 		return (0);
436 
437 	rv = initRuleValue(1, rv);
438 	if (rv == 0)
439 		return (0);
440 
441 	for (i = 0; i < q->components.components_len; i++) {
442 		int	ic;
443 		int	iv, v, dup;
444 		int	len;
445 
446 		/* Ignore out-of-range column index */
447 		if (q->components.components_val[i].which_index >=
448 				t->numColumns)
449 			continue;
450 
451 		/*
452 		 * Add the query value. A NULL value indicates deletion,
453 		 * but addCol2RuleValue() takes care of that for us.
454 		 */
455 		if (addCol2RuleValue(vt_string,
456 				t->column[q->components.components_val[i].
457 						which_index],
458 				q->components.components_val[i].index_value->
459 					itemvalue.itemvalue_val,
460 				q->components.components_val[i].index_value->
461 					itemvalue.itemvalue_len, rv) != 0) {
462 			freeRuleValue(rv, 1);
463 			rv = 0;
464 			break;
465 		}
466 	}
467 
468 	return (rv);
469 }
470 
471 
472 /*
473  * Given a LHS rule 'rl', return an array containing the item names,
474  * and the number of elements in the array in '*numItems'.
475  *
476  * If there are 'me_match' __nis_mapping_element_t's, we use the
477  * supplied '*rval' (if any) to derive values for the items in
478  * the 'me_match', and add the values thus derived to '*rval' (in
479  * which case the '*rval' pointer will change; the old '*rval'
480  * is deleted).
481  */
482 __nis_mapping_item_t *
483 buildLvalue(__nis_mapping_rlhs_t *rl, __nis_value_t **rval, int *numItems) {
484 	__nis_value_t		*val, *r;
485 	__nis_mapping_item_t	*item = 0;
486 	int			i, n, ni = 0, nv = 0;
487 	int			repeat = 0;
488 
489 	if (rl == 0)
490 		return (0);
491 
492 	if (rval != 0) {
493 		r = *rval;
494 		repeat = r->repeat;
495 	} else
496 		r = 0;
497 
498 	/* If there is more than one element, we concatenate the items */
499 	for (i = 0; i < rl->numElements; i++) {
500 		__nis_mapping_element_t	*e = &rl->element[i];
501 		__nis_mapping_item_t	*olditem, *tmpitem = 0;
502 		__nis_value_t		**tmp;
503 
504 		switch (e->type) {
505 		case me_item:
506 			tmpitem = cloneItem(&e->element.item);
507 			break;
508 		case me_match:
509 			/*
510 			 * Obtain values for the items in the 'me_match'
511 			 * element.
512 			 */
513 			tmp = matchMappingItem(e->element.match.fmt, r, &nv,
514 				0, 0);
515 			if (tmp != 0) {
516 				freeValue(r, 1);
517 				val = 0;
518 				for (n = 0; n < nv; n++) {
519 					r = concatenateValues(val, tmp[n]);
520 					freeValue(val, 1);
521 					freeValue(tmp[n], 1);
522 					val = r;
523 					if (val == 0) {
524 						for (n++; n < nv; n++) {
525 							freeValue(tmp[n], 1);
526 						}
527 						break;
528 					}
529 				}
530 				free(tmp);
531 				if (rval != 0) {
532 					if (repeat && val != 0)
533 						val->repeat = repeat;
534 					*rval = val;
535 				}
536 				for (n = 0; n < e->element.match.numItems;
537 						n++) {
538 					olditem = item;
539 					item = concatenateMappingItem(item, ni,
540 						&e->element.match.item[n]);
541 					freeMappingItem(olditem, ni);
542 					if (item == 0) {
543 						ni = 0;
544 						break;
545 					}
546 					ni++;
547 				}
548 			}
549 			break;
550 		case me_print:
551 		case me_split:
552 		case me_extract:
553 		default:
554 			/* These shouldn't show up on the LHS; ignore */
555 			break;
556 		}
557 
558 		if (tmpitem != 0) {
559 			olditem = item;
560 			item = concatenateMappingItem(item, ni, tmpitem);
561 			freeMappingItem(olditem, ni);
562 			freeMappingItem(tmpitem, 1);
563 			ni++;
564 			if (item == 0) {
565 				ni = 0;
566 				break;
567 			}
568 		}
569 	}
570 
571 	if (numItems != 0)
572 		*numItems = ni;
573 
574 	return (item);
575 }
576 
577 __nis_value_t *
578 buildRvalue(__nis_mapping_rlhs_t *rl, __nis_mapping_item_type_t native,
579 		__nis_rule_value_t *rv, int *stat) {
580 	__nis_value_t	*val, *vold = 0, *vnew;
581 	int		i;
582 	char		*myself = "buildRvalue";
583 
584 	if (rl == 0 || rl->numElements <= 0) {
585 		/*
586 		 * No RHS indicates deletion, as does a __nis_value_t
587 		 * with numVals == -1, so we return such a creature.
588 		 */
589 		val = am(myself, sizeof (*val));
590 		if (val != 0) {
591 			val->type = vt_string;
592 			val->numVals = -1;
593 		}
594 		return (val);
595 	}
596 
597 	/* If there is more than one element, we concatenate the values */
598 	for (i = 0; i < rl->numElements; i++) {
599 		vnew = getMappingElement(&rl->element[i], native, rv, stat);
600 		val = concatenateValues(vold, vnew);
601 		freeValue(vnew, 1);
602 		freeValue(vold, 1);
603 		vold = val;
604 	}
605 	return (val);
606 }
607 
608 /*
609  * Derive values for the LDAP attributes specified by the rule 'r',
610  * and add them to the rule-value 'rv'.
611  *
612  * If 'doAssign' is set, out-of-context assignments are performed,
613  * otherwise not.
614  */
615 __nis_rule_value_t *
616 addLdapRuleValue(__nis_table_mapping_t *t,
617 			__nis_mapping_rule_t *r,
618 			__nis_mapping_item_type_t lnative,
619 			__nis_mapping_item_type_t rnative,
620 			__nis_rule_value_t *rv,
621 			int doAssign, int *stat) {
622 	int			i, j;
623 	char			**new;
624 	__nis_value_t		*rval, *lval;
625 	__nis_buffer_t		b = {0, 0};
626 	__nis_mapping_item_t	*litem;
627 	int			numItems;
628 	char			**dn = 0;
629 	int			numDN = 0;
630 	char			*myself = "addLdapRuleValue";
631 
632 
633 	/* Do we have the required values ? */
634 	if (rv == 0)
635 		return (0);
636 
637 	/*
638 	 * Establish appropriate search base. For rnative == mit_nisplus,
639 	 * we're deriving LDAP attribute values from NIS+ columns; in other
640 	 * words, we're writing to LDAP, and should use the write.base value.
641 	 */
642 	__nisdb_get_tsd()->searchBase = (rnative == mit_nisplus) ?
643 		t->objectDN->write.base : t->objectDN->read.base;
644 
645 	/* Set escapeFlag if LHS is "dn" to escape special chars */
646 	if (yp2ldap && r->lhs.numElements == 1 &&
647 		r->lhs.element->type == me_item &&
648 		r->lhs.element->element.item.type == mit_ldap &&
649 		strcasecmp(r->lhs.element->element.item.name, "dn") == 0) {
650 			__nisdb_get_tsd()->escapeFlag = '1';
651 	}
652 
653 	/* Build the RHS value */
654 	rval = buildRvalue(&r->rhs, rnative, rv, stat);
655 
656 	/* Reset escapeFlag */
657 	__nisdb_get_tsd()->escapeFlag = '\0';
658 
659 	if (rval == 0)
660 		return (rv);
661 
662 	/*
663 	 * Special case: If we got no value for the RHS (presumably because
664 	 * we're missing one or more item values), we don't produce an lval.
665 	 * Note that this isn't the same thing as an empty value, which we
666 	 * faithfully try to transmit to LDAP.
667 	 */
668 	if (rval->numVals == 1 && rval->val[0].value == 0) {
669 		freeValue(rval, 1);
670 		return (rv);
671 	}
672 
673 	/* Obtain the LHS item names */
674 	litem = buildLvalue(&r->lhs, &rval, &numItems);
675 	if (litem == 0) {
676 		freeValue(rval, 1);
677 		return (rv);
678 	}
679 
680 	/* Get string representations of the LHS item names */
681 	lval = 0;
682 	for (i = 0; i < numItems; i++) {
683 		__nis_value_t	*tmpval, *old;
684 
685 		tmpval = getMappingItem(&litem[i], lnative, 0, 0, NULL);
686 
687 		/*
688 		 * If the LHS item is out-of-context, we do the
689 		 * assignment right here.
690 		 */
691 		if (doAssign && litem[i].type == mit_nisplus) {
692 			int	err;
693 
694 			err = storeNisPlus(&litem[i], i, numItems,
695 						rv, t->objName, rval);
696 			if (err != NIS_SUCCESS) {
697 				char	*iname = "<unknown>";
698 
699 				if (tmpval != 0 &&
700 						tmpval->numVals == 1)
701 					iname = tmpval->val[0].value;
702 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
703 					"%s: NIS+ store \"%s\": %s",
704 					myself, iname,
705 					nis_sperrno(err));
706 			}
707 
708 			freeValue(tmpval, 1);
709 			continue;
710 		} else if (doAssign && litem[i].type == mit_ldap &&
711 				litem[i].searchSpec.triple.scope !=
712 					LDAP_SCOPE_UNKNOWN &&
713 				slen(litem[i].searchSpec.triple.base) > 0 &&
714 				(slen(litem[i].searchSpec.triple.attrs) > 0 ||
715 				litem[i].searchSpec.triple.element != 0)) {
716 			int	stat;
717 
718 			if (dn == 0)
719 				dn = findDNs(myself, rv, 1,
720 					t->objectDN->write.base,
721 					&numDN);
722 
723 			stat = storeLDAP(&litem[i], i, numItems, rval,
724 				t->objectDN, dn, numDN);
725 			if (stat != LDAP_SUCCESS) {
726 				char	*iname = "<unknown>";
727 
728 				if (tmpval != 0 &&
729 						tmpval->numVals == 1)
730 					iname = tmpval->val[0].value;
731 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
732 					"%s: LDAP store \"%s\": %s",
733 					myself, iname,
734 					ldap_err2string(stat));
735 			}
736 
737 			freeValue(tmpval, 1);
738 			continue;
739 		}
740 
741 		old = lval;
742 		lval = concatenateValues(old, tmpval);
743 		freeValue(tmpval, 1);
744 		freeValue(old, 1);
745 	}
746 
747 	/* Don't need the LHS items themselves anymore */
748 	freeMappingItem(litem, numItems);
749 
750 	/*
751 	 * If we don't have an 'lval' (probably because all litem[i]:s
752 	 * were out-of-context assignments), we're done.
753 	 */
754 	if (lval == 0 || lval->numVals <= 0) {
755 		freeValue(lval, 1);
756 		freeValue(rval, 1);
757 		return (rv);
758 	}
759 
760 	for (i = 0, j = 0; i < lval->numVals; i++) {
761 		/* Special case: rval->numVals < 0 means deletion */
762 		if (rval->numVals < 0) {
763 			(void) addAttr2RuleValue(rval->type,
764 				lval->val[i].value, 0, 0, rv);
765 			continue;
766 		}
767 		/* If we're out of values, repeat the last one */
768 		if (j >= rval->numVals)
769 			j = (rval->numVals > 0) ? rval->numVals-1 : 0;
770 		for (0; j < rval->numVals; j++) {
771 			/*
772 			 * If this is the 'dn', and the value ends in a
773 			 * comma, append the appropriate search base.
774 			 */
775 			if (strcasecmp("dn", lval->val[i].value) == 0 &&
776 					lastChar(&rval->val[j]) == ',' &&
777 					t->objectDN->write.scope !=
778 						LDAP_SCOPE_UNKNOWN) {
779 				void	*nval;
780 				int	nlen = -1;
781 
782 				nval = appendString2SingleVal(
783 					t->objectDN->write.base, &rval->val[j],
784 					&nlen);
785 				if (nval != 0 && nlen >= 0) {
786 					sfree(rval->val[j].value);
787 					rval->val[j].value = nval;
788 					rval->val[j].length = nlen;
789 				}
790 			}
791 			(void) addAttr2RuleValue(rval->type,
792 				lval->val[i].value, rval->val[j].value,
793 				rval->val[j].length, rv);
794 			/*
795 			 * If the lval is multi-valued, go on to the
796 			 * other values; otherwise, quit (but increment
797 			 * the 'rval' value index).
798 			 */
799 			if (!lval->repeat) {
800 				j++;
801 				break;
802 			}
803 		}
804 	}
805 
806 	/* Clean up */
807 	freeValue(lval, 1);
808 	freeValue(rval, 1);
809 
810 	return (rv);
811 }
812 
813 /*
814  * Remove the indicated attribute, and any values for it, from the
815  * rule-value.
816  */
817 void
818 delAttrFromRuleValue(__nis_rule_value_t *rv, char *attrName) {
819 	int	i;
820 
821 	if (rv == 0 || attrName == 0)
822 		return;
823 
824 	for (i = 0; i < rv->numAttrs; i++) {
825 		if (strcasecmp(attrName, rv->attrName[i]) == 0) {
826 			int	j;
827 
828 			for (j = 0; j < rv->attrVal[i].numVals; j++)
829 				sfree(rv->attrVal[i].val[j].value);
830 			if (rv->attrVal[i].numVals > 0)
831 				sfree(rv->attrVal[i].val);
832 
833 			sfree(rv->attrName[i]);
834 
835 			/* Move up the rest of the attribute names/values */
836 			for (j = i+1; j < rv->numAttrs; j++) {
837 				rv->attrName[j-1] = rv->attrName[j];
838 				rv->attrVal[j-1] = rv->attrVal[j];
839 			}
840 
841 			rv->numAttrs -= 1;
842 
843 			break;
844 		}
845 	}
846 }
847 
848 /*
849  * Remove the indicated column, and any values for it, from the
850  * rule-value.
851  */
852 void
853 delColFromRuleValue(__nis_rule_value_t *rv, char *colName) {
854 	int	i;
855 
856 	if (rv == 0 || colName == 0)
857 		return;
858 
859 	for (i = 0; i < rv->numColumns; i++) {
860 		if (strcmp(colName, rv->colName[i]) == 0) {
861 			int	j;
862 
863 			for (j = 0; j < rv->colVal[i].numVals; j++)
864 				sfree(rv->colVal[i].val[j].value);
865 			if (rv->colVal[i].numVals > 0)
866 				sfree(rv->colVal[i].val);
867 
868 			sfree(rv->colName[i]);
869 
870 			/* Move up the rest of the column names/values */
871 			for (j = i+1; j < rv->numColumns; j++) {
872 				rv->colName[j-1] = rv->colName[j];
873 				rv->colVal[j-1] = rv->colVal[j];
874 			}
875 
876 			rv->numColumns -= 1;
877 
878 			break;
879 		}
880 	}
881 }
882 
883 /*
884  * Add the write-mode object classes specified by 'objClassAttrs' to the
885  * rule-value 'rv'.
886  * If there's an error, 'rv' is deleted, and NULL returned.
887  */
888 __nis_rule_value_t *
889 addObjectClasses(__nis_rule_value_t *rv, char *objClassAttrs) {
890 	char	*filter = 0, **fc = 0;
891 	int	i, nfc = 0;
892 
893 	/*
894 	 * Expect to only use this for existing rule-values, so rv == 0 is
895 	 * an error.
896 	 */
897 	if (rv == 0)
898 		return (0);
899 
900 	/*
901 	 * If 'objClassAttrs' is NULL, we trivially have nothing to do.
902 	 * Assume the caller knows what it's doing, and return success.
903 	 */
904 	if (objClassAttrs == 0)
905 		return (rv);
906 
907 	/*
908 	 * Make an AND-filter of the object classes, and split into
909 	 * components. (Yes, this is a bit round-about, but leverages
910 	 * existing functions.)
911 	 */
912 	filter = makeFilter(objClassAttrs);
913 	if (filter == 0) {
914 		freeRuleValue(rv, 1);
915 		return (0);
916 	}
917 
918 	fc = makeFilterComp(filter, &nfc);
919 	if (fc == 0 || nfc <= 0) {
920 		free(filter);
921 		freeRuleValue(rv, 1);
922 		return (0);
923 	}
924 
925 	/* Add the objectClass attributes to the rule-value */
926 	for (i = 0; i < nfc; i++) {
927 		char	*name, *value;
928 
929 		name = fc[i];
930 		/* Skip if not of the "name=value" form */
931 		if ((value = strchr(name, '=')) == 0)
932 			continue;
933 
934 		*value = '\0';
935 		value++;
936 
937 		/* Skip if the attribute name isn't "objectClass" */
938 		if (strcasecmp("objectClass", name) != 0)
939 			continue;
940 
941 		if (addSAttr2RuleValue(name, value, rv) != 0) {
942 			free(filter);
943 			freeFilterComp(fc, nfc);
944 			freeRuleValue(rv, 1);
945 			return (0);
946 		}
947 	}
948 
949 	free(filter);
950 	freeFilterComp(fc, nfc);
951 
952 	return (rv);
953 }
954 
955 
956 static char *
957 valString(__nis_value_t *val) {
958 	int	i;
959 
960 	if (val == 0 || val->type != vt_string)
961 		return (0);
962 
963 	for (i = 0; i < val->numVals; i++) {
964 		/* Look for a non-NULL, non-zero length value */
965 		if (val->val[i].value != 0 && val->val[i].length > 0) {
966 			char	*v = val->val[i].value;
967 
968 			/*
969 			 * Check that there's a NUL at the end. True,
970 			 * if there isn't, we may be looking beyond
971 			 * allocated memory. However, we would have done
972 			 * so in any case when the supposed string was
973 			 * traversed (printed, etc.), very possibly by
974 			 * a lot more than one byte. So, it's better to
975 			 * take a small risk here than a large one later.
976 			 */
977 			if (v[val->val[i].length-1] == '\0' ||
978 					v[val->val[i].length] == '\0')
979 				return (v);
980 		}
981 	}
982 
983 	return (0);
984 }
985 
986 char *
987 findVal(char *name, __nis_rule_value_t *rv, __nis_mapping_item_type_t type) {
988 	int	i;
989 
990 	if (type == mit_nisplus) {
991 		for (i = 0; i < rv->numColumns; i++) {
992 			if (rv->colName[i] == 0)
993 				continue;
994 			if (strcmp(name, rv->colName[i]) == 0) {
995 				return (valString(&rv->colVal[i]));
996 			}
997 		}
998 	} else if (type == mit_ldap) {
999 		for (i = 0; i < rv->numAttrs; i++) {
1000 			if (rv->attrName[i] == 0)
1001 				continue;
1002 			if (strcasecmp(name, rv->attrName[i]) == 0) {
1003 				return (valString(&rv->attrVal[i]));
1004 			}
1005 		}
1006 	}
1007 
1008 	return (0);
1009 }
1010 
1011 static char	*norv = "<NIL>";
1012 static char	*unknown = "<unknown>";
1013 
1014 /*
1015  * Attempt to derive a string identifying the rule-value 'rv'. The
1016  * returned string is a pointer, either into 'rv', or to static
1017  * storage, and must not be freed.
1018  */
1019 char *
1020 rvId(__nis_rule_value_t *rv, __nis_mapping_item_type_t type) {
1021 	char	*v;
1022 
1023 	if (rv == 0)
1024 		return (norv);
1025 
1026 	if (rv->numColumns > 0 && type == mit_nisplus) {
1027 		/*
1028 		 * Look for a column called "cname" or "name".
1029 		 * If that fails, try "key" or "alias".
1030 		 */
1031 		if ((v = findVal("cname", rv, type)) != 0)
1032 			return (v);
1033 		else if ((v = findVal("name", rv, type)) != 0)
1034 			return (v);
1035 		else if ((v = findVal("key", rv, type)) != 0)
1036 			return (v);
1037 		else if ((v = findVal("alias", rv, type)) != 0)
1038 			return (v);
1039 	} else if (rv->numAttrs > 0 && type == mit_ldap) {
1040 		/*
1041 		 * Look for "dn", or "cn".
1042 		 */
1043 		if ((v = findVal("dn", rv, type)) != 0)
1044 			return (v);
1045 		else if ((v = findVal("cn", rv, type)) != 0)
1046 			return (v);
1047 	}
1048 
1049 	return (unknown);
1050 }
1051 
1052 /*
1053  * Merge the rule-values with the same DN into one. Each rule-value
1054  * in the returned array will have unique 'dn'. On entry, *numVals
1055  * contains the number of rule-values in 'rv'. On exit, it contains
1056  * the number of rule-values in the returned array or -1 on error.
1057  */
1058 __nis_rule_value_t *
1059 mergeRuleValueWithSameDN(__nis_rule_value_t *rv, int *numVals) {
1060 	__nis_rule_value_t	*rvq = 0;
1061 	char			*dn, *odn;
1062 	int			count = 0;
1063 	int			i, j;
1064 
1065 	if (numVals == 0)
1066 		return (0);
1067 
1068 	for (i = 0; i < *numVals; i++) {
1069 		if ((dn = findVal("dn", &rv[i], mit_ldap)) != 0) {
1070 			for (j = 0; j < count; j++) {
1071 				if ((odn = findVal("dn", &rvq[j],
1072 						mit_ldap)) != 0) {
1073 					/* case sensitive compare */
1074 					if (strcmp(dn, odn) != 0)
1075 						continue;
1076 					if (mergeRuleValue(&rvq[j],
1077 							&rv[i]) == -1) {
1078 						freeRuleValue(rvq, count);
1079 						*numVals = -1;
1080 						return (0);
1081 					}
1082 					break;
1083 				} else {
1084 					freeRuleValue(rvq, count);
1085 					*numVals = -1;
1086 					return (0);
1087 				}
1088 			}
1089 			/* if no match, then add it to the rulevalue array */
1090 			if (j == count) {
1091 				rvq = growRuleValue(count, count + 1, rvq,
1092 									&rv[i]);
1093 				if (rvq == 0) {
1094 					*numVals = -1;
1095 					return (0);
1096 				}
1097 				count++;
1098 			}
1099 		}
1100 	}
1101 
1102 	*numVals = count;
1103 	return (rvq);
1104 }
1105