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 */
43void
44freeRuleValue(__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 *
96initRuleValue(int count, __nis_rule_value_t *rvIn) {
97	return (growRuleValue(0, count, 0, rvIn));
98}
99
100static 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 *
109growRuleValue(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 */
169int
170mergeRuleValue(__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
202static int
203addVal2RuleValue(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
385int
386addAttr2RuleValue(__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
397int
398addSAttr2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
399	return (addAttr2RuleValue(vt_string, name, value, slen(value), rv));
400}
401
402int
403addCol2RuleValue(__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
414int
415addSCol2RuleValue(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 *
425buildNisPlusRuleValue(__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 *
480buildLvalue(__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 *
575buildRvalue(__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 *
613addLdapRuleValue(__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 */
795void
796delAttrFromRuleValue(__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 */
830void
831delColFromRuleValue(__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 *
867addObjectClasses(__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
934static char *
935valString(__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
964char *
965findVal(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
989static char	*norv = "<NIL>";
990static 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 */
997char *
998rvId(__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 *
1037mergeRuleValueWithSameDN(__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