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 <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