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