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 (c) 2001 by Sun Microsystems, Inc.
     24  * All rights reserved.
     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 "ldap_nisplus.h"
     34 #include "ldap_util.h"
     35 #include "ldap_val.h"
     36 #include "ldap_attr.h"
     37 #include "ldap_glob.h"
     38 
     39 
     40 static void
     41 freeColNames(char **name, int numCols) {
     42 	int	i;
     43 
     44 	if (name == 0)
     45 		return;
     46 
     47 	for (i = 0; i < numCols; i++) {
     48 		sfree(name[i]);
     49 	}
     50 	sfree(name);
     51 }
     52 
     53 /*
     54  * Convert the object attributes (zo_owner, etc.) fields of 'o' to
     55  * the corresponding char **'s and __nis_value_t's. The 'name' and
     56  * 'val' arrays are each assumed to have (at least) 'numVals' elements,
     57  * and 'numVals' must be at least five.
     58  *
     59  * Returns zero if successful, non-zero otherwise. Whether successful
     60  * or not, the caller must clean up 'name' and 'val', which may be
     61  * partially allocated even after a failure.
     62  */
     63 static int
     64 objAttr2Value(nis_object *o, char **name, __nis_value_t *val, int numVals) {
     65 	int	i, err;
     66 	char	*myself = "objAttr2Value";
     67 
     68 	if (o == 0 || name == 0 || val == 0 || numVals < 5)
     69 		return (-1);
     70 
     71 	name[0] = sdup(myself, T, "zo_owner");
     72 	name[1] = sdup(myself, T, "zo_group");
     73 	name[2] = sdup(myself, T, "zo_domain");
     74 	name[3] = sdup(myself, T, "zo_access");
     75 	name[4] = sdup(myself, T, "zo_ttl");
     76 
     77 	for (err = 0, i = 0; i < 5; i++) {
     78 		if (name[i] == 0)
     79 			err++;
     80 		val[i].val = am(myself, sizeof (val[i].val[0]));
     81 		if (val[i].val == 0)
     82 			err++;
     83 		val[i].type = vt_string;
     84 	}
     85 	if (err > 0) {
     86 		for (i = 0; i < 5; i++) {
     87 			sfree(name[i]);
     88 			name[i] = 0;
     89 			sfree(val[i].val);
     90 			val[i].val = 0;
     91 		}
     92 		return (-2);
     93 	}
     94 
     95 	val[0].val[0].value = sdup(myself, T, o->zo_owner);
     96 	val[1].val[0].value = sdup(myself, T, o->zo_group);
     97 	val[2].val[0].value = sdup(myself, T, o->zo_domain);
     98 	val[3].val[0].value = am(myself, 2 * sizeof (o->zo_access) + 1);
     99 	val[4].val[0].value = am(myself, 2 * sizeof (o->zo_ttl) + 1);
    100 
    101 	for (err = 0, i = 0; i < 5; i++) {
    102 		if (val[i].val[0].value == 0)
    103 			err++;
    104 		val[i].numVals = 1;
    105 	}
    106 	if (err > 0) {
    107 		for (i = 0; i < 5; i++) {
    108 			sfree(name[i]);
    109 			name[i] = 0;
    110 			sfree(val[i].val[0].value);
    111 			val[i].val[0].value = 0;
    112 			sfree(val[i].val);
    113 			val[i].val = 0;
    114 		}
    115 		return (-3);
    116 	}
    117 
    118 	sprintf(val[3].val[0].value, "%x", o->zo_access);
    119 	sprintf(val[4].val[0].value, "%x", o->zo_ttl);
    120 
    121 	val[0].val[0].length = slen(o->zo_owner);
    122 	val[1].val[0].length = slen(o->zo_group);
    123 	val[2].val[0].length = slen(o->zo_domain);
    124 	val[3].val[0].length = 2 * sizeof (o->zo_access);
    125 	val[4].val[0].length = 2 * sizeof (o->zo_ttl);
    126 
    127 	return (0);
    128 }
    129 
    130 /*
    131  * Convert a __nis_index_t to a string, using the supplied rule-value
    132  * to evaluate any expressions in the index components.
    133  *
    134  * The 'table' is used only to produce a more meaningful error message.
    135  */
    136 static char *
    137 index2string(char *msg, __nis_index_t *index, __nis_rule_value_t *rvIn,
    138 		char *table) {
    139 	__nis_buffer_t	b = {0, 0};
    140 	int		i, frv = 0;
    141 	char		*myself = "index2string";
    142 
    143 	if (index == 0)
    144 		return (0);
    145 
    146 	if (rvIn == 0) {
    147 		rvIn = initRuleValue(1, 0);
    148 		if (rvIn == 0)
    149 			return (0);
    150 		frv = 1;
    151 	}
    152 
    153 	if (msg == 0)
    154 		msg = myself;
    155 
    156 	bp2buf(msg, &b, "[");
    157 	for (i = 0; i < index->numIndexes; i++) {
    158 		char	*fmt;
    159 		__nis_value_t	*tmpval;
    160 
    161 		if (slen(index->name[i]) <= 0 ||
    162 				index->value[i] == 0) {
    163 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    164 				"%s: index spec error for component \"%s\"%s",
    165 				msg, NIL(index->name[i]),
    166 					(index->value[i] == 0) ?
    167 						", <nil> value" : "");
    168 			sfree(b.buf);
    169 			if (frv)
    170 				freeRuleValue(rvIn, 1);
    171 			return (0);
    172 		}
    173 
    174 		/* Derive a value for this index */
    175 		tmpval = getMappingFormatArray(index->value[i], rvIn,
    176 						fa_item, 0, 0);
    177 		if (tmpval == 0 || tmpval->numVals <= 0) {
    178 			char	*ival;
    179 
    180 			freeValue(tmpval, 1);
    181 			tmpval = getMappingFormatArray(index->value[i],
    182 					0, fa_item, 0, 0);
    183 			if (tmpval == 0)
    184 				ival = "<unknown>";
    185 			else if (tmpval->type != vt_string)
    186 				ival = "<non-string>";
    187 			else if (tmpval->numVals != 1)
    188 				ival = "<# val error>";
    189 			else
    190 				ival = tmpval->val[0].value;
    191 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    192 			"%s: No value for index \"%s = %s\" (table = \"%s\")",
    193 				msg, index->name[i], ival,
    194 				NIL(table));
    195 			freeValue(tmpval, 1);
    196 			sfree(b.buf);
    197 			if (frv)
    198 				freeRuleValue(rvIn, 1);
    199 			return (0);
    200 		}
    201 
    202 		/*
    203 		 * There should only be one value, so we ignore
    204 		 * any excess values.
    205 		 */
    206 		if (tmpval->type == vt_string) {
    207 			if (i == 0)
    208 				fmt = "%s=%s";
    209 			else
    210 				fmt = ",%s=%s";
    211 			bp2buf(msg, &b, fmt,
    212 				index->name[i], tmpval->val[0].value);
    213 		} else {
    214 			bc2buf(msg, tmpval->val[0].value,
    215 				tmpval->val[0].length, &b);
    216 		}
    217 		freeValue(tmpval, 1);
    218 	}
    219 	bp2buf(msg, &b, "]");
    220 
    221 	if (frv)
    222 		freeRuleValue(rvIn, 1);
    223 
    224 	return (b.buf);
    225 }
    226 
    227 /*
    228  * Return the rule-value representation of the the entry (or entries)
    229  * specified by 'index' and 'table'.
    230  *
    231  * If 'index' is non-NULL, we evaluate the index->value format using
    232  * the supplied 'rvIn'; should 'index' be NULL, 'rvIn' is unused. In either
    233  * case, 'rvIn' isn't modified.
    234  */
    235 __nis_rule_value_t *
    236 getNisPlusEntry(__nis_index_t *index, char *table, __nis_rule_value_t *rvIn,
    237 		int *numVals) {
    238 	__nis_buffer_t		b = {0, 0};
    239 	__nis_rule_value_t	*rv;
    240 	char			*myself = "getNisPlusEntry";
    241 
    242 	if (table == 0)
    243 		return (0);
    244 
    245 	if (index != 0 && index->numIndexes > 0) {
    246 		b.buf = index2string(myself, index, rvIn, table);
    247 		b.len = slen(b.buf);
    248 
    249 		bp2buf(myself, &b, "%s", table, 0);
    250 
    251 		rv = getNisPlusEntrySimple(b.buf, numVals);
    252 
    253 		sfree(b.buf);
    254 	} else {
    255 		/* Special case: want the zo_* attributes for the 'table' */
    256 		nis_result	*res = 0;
    257 		int		stat;
    258 
    259 		stat = getNisPlusObj(table, 0, &res);
    260 		if (stat != LDAP_SUCCESS)
    261 			return (0);
    262 
    263 		rv = initRuleValue(1, 0);
    264 		if (rv == 0) {
    265 			nis_freeresult(res);
    266 			return (0);
    267 		}
    268 
    269 		/* Allocate space for the object attributes */
    270 		rv->colName = am(myself, 5 * sizeof (rv->colName[0]));
    271 		rv->colVal = am(myself, 5 * sizeof (rv->colVal[0]));
    272 		if (rv->colName == 0 || rv->colVal == 0) {
    273 			freeRuleValue(rv, 1);
    274 			nis_freeresult(res);
    275 			return (0);
    276 		}
    277 
    278 		if (objAttr2Value(NIS_RES_OBJECT(res), rv->colName,
    279 				rv->colVal, 5) == 0) {
    280 			rv->numColumns = 5;
    281 			if (numVals != 0)
    282 				*numVals = 1;
    283 		} else {
    284 			freeRuleValue(rv, 1);
    285 			rv = 0;
    286 		}
    287 
    288 		nis_freeresult(res);
    289 	}
    290 
    291 	return (rv);
    292 }
    293 
    294 /*
    295  * Simple NIS+ entry lookup routine, which accepts an indexed name, and
    296  * returns the corresponding rule-value array, and number of elements in
    297  * said array.
    298  */
    299 __nis_rule_value_t *
    300 getNisPlusEntrySimple(char *name, int *numVals) {
    301 	char			*table;
    302 	__nis_rule_value_t	*rv;
    303 	nis_result		*res;
    304 	int			i, nobj, nv, nc = 0;
    305 	nis_object		*o;
    306 	char			**col = 0;
    307 	zotypes			ttype;
    308 	char			*myself = "getNisPlusEntrySimple";
    309 
    310 	if (name == 0)
    311 		return (0);
    312 
    313 	/* Find the table name proper */
    314 	table = strrchr(name, ']');
    315 	if (table != 0) {
    316 		/* Point to the start of the table name */
    317 		table++;
    318 	} else {
    319 		/*
    320 		 * Presumably no indices; this implies enumeration, and
    321 		 * that's not the intended use of this function, so return
    322 		 * failure.
    323 		 */
    324 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    325 		"%s: un-indexed name \"%s\" used for table entry lookup",
    326 			myself, name);
    327 		return (0);
    328 	}
    329 
    330 	if (LDAP_SUCCESS != initializeColumnNames(table, &col, &nc, &ttype,
    331 			0)) {
    332 		freeColNames(col, nc);
    333 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    334 			"%s: unable to get column names for \"%s\"",
    335 			myself, table);
    336 		return (0);
    337 	}
    338 
    339 	if (ttype != NIS_TABLE_OBJ) {
    340 		freeColNames(col, nc);
    341 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    342 			"%s: \"%s\" is object type %d, not a table",
    343 			myself, table, ttype);
    344 		return (0);
    345 	}
    346 
    347 	if (col == 0 || nc <= 0) {
    348 		/* col!=0 and nc==0 is possible, so free the column array */
    349 		freeColNames(col, nc);
    350 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    351 			"%s: %s for \"%s\"",
    352 			myself, (col == 0) ? "<nil> column name array" :
    353 					"no column name elements",
    354 			table);
    355 		return (0);
    356 	}
    357 
    358 	res = nis_list(name, 0, 0, 0);
    359 	if (res == 0) {
    360 		freeColNames(col, nc);
    361 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    362 			"%s: NIS+ lookup error (no result) for \"%s\"",
    363 			myself, name);
    364 		return (0);
    365 	}
    366 	if (res->status == NIS_NOTFOUND) {
    367 		/*
    368 		 * Not really an error from the POV of this function; we
    369 		 * have no way of knowing if the entry should exist or not.
    370 		 */
    371 		freeColNames(col, nc);
    372 		nis_freeresult(res);
    373 		return (0);
    374 	} else if (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS) {
    375 		freeColNames(col, nc);
    376 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    377 			"%s: NIS+ lookup error (%d) for \"%s\"",
    378 			myself, res->status, name);
    379 		nis_freeresult(res);
    380 		return (0);
    381 	}
    382 
    383 	nobj = res->objects.objects_len;
    384 
    385 	/* One rule-value element for each entry object */
    386 	rv = initRuleValue(nobj, 0);
    387 	if (rv == 0) {
    388 		freeColNames(col, nc);
    389 		nis_freeresult(res);
    390 		return (0);
    391 	}
    392 
    393 	for (i = 0, nv = 0; i < nobj; i++) {
    394 		unsigned int		nec;
    395 		entry_col		*ec;
    396 		int			j;
    397 
    398 		o = &res->objects.objects_val[i];
    399 		if (o->zo_data.zo_type != NIS_ENTRY_OBJ)
    400 			continue;
    401 
    402 		nec = o->zo_data.objdata_u.en_data.en_cols.en_cols_len;
    403 		if (nec == 0)
    404 			continue;
    405 
    406 		if (nec != nc)
    407 			continue;
    408 
    409 		/*
    410 		 * 'nec+5' to account for the object attributes
    411 		 * (zo_owner. etc), of which there are five.
    412 		 */
    413 		rv[nv].colName = am(myself,
    414 				(nec+5) * sizeof (rv[nv].colName[0]));
    415 		rv[nv].colVal = am(myself,
    416 				(nec+5) * sizeof (rv[nv].colVal[0]));
    417 		if (rv[nv].colName == 0 || rv[nv].colVal == 0) {
    418 			freeRuleValue(rv, nv+1);
    419 			freeColNames(col, nc);
    420 			nis_freeresult(res);
    421 			return (0);
    422 		}
    423 		rv[nv].numColumns = nec + 5;
    424 
    425 		ec = o->zo_data.objdata_u.en_data.en_cols.en_cols_val;
    426 
    427 		for (j = 0; j < nec; j++) {
    428 			int	len;
    429 
    430 			rv[nv].colName[j] = sdup(myself, T, col[j]);
    431 			if (rv[nv].colName[j] == 0) {
    432 				freeRuleValue(rv, nv+1);
    433 				freeColNames(col, nc);
    434 				nis_freeresult(res);
    435 				return (0);
    436 			}
    437 
    438 			/* What's the type of column value ? */
    439 			if ((ec[j].ec_flags & TA_BINARY) != 0 ||
    440 					(ec[j].ec_flags & TA_XDR) != 0 ||
    441 					(ec[j].ec_flags & TA_ASN1) != 0)
    442 				rv[nv].colVal[j].type = vt_ber;
    443 			else
    444 				rv[nv].colVal[j].type = vt_string;
    445 
    446 			rv[nv].colVal[j].val = am(myself,
    447 					sizeof (rv[nv].colVal[j].val[0]));
    448 			if (rv[nv].colVal[j].val == 0) {
    449 				freeRuleValue(rv, nv+1);
    450 				freeColNames(col, nc);
    451 				nis_freeresult(res);
    452 				return (0);
    453 			}
    454 			rv[nv].colVal[j].numVals = 1;
    455 
    456 			len = ec[j].ec_value.ec_value_len;
    457 			if (len > 0 &&
    458 				ec[j].ec_value.ec_value_val[len-1] == '\0') {
    459 				/* Don't count NUL in the value length */
    460 				len -= 1;
    461 			}
    462 
    463 			/*
    464 			 * Always allocate memory so that there's a NUL at
    465 			 * the end.
    466 			 */
    467 			rv[nv].colVal[j].val[0].value = am(myself, len+1);
    468 			rv[nv].colVal[j].val[0].length = len;
    469 
    470 			if (rv[nv].colVal[j].val[0].value == 0) {
    471 				freeRuleValue(rv, nv+1);
    472 				freeColNames(col, nc);
    473 				nis_freeresult(res);
    474 				return (0);
    475 			}
    476 
    477 			(void) memcpy(rv[nv].colVal[j].val[0].value,
    478 				ec[j].ec_value.ec_value_val, len);
    479 		}
    480 
    481 		/* Now the object attributes */
    482 		if (objAttr2Value(o, &rv[nv].colName[nec], &rv[nv].colVal[nec],
    483 				5) != 0) {
    484 			freeRuleValue(rv, nv+1);
    485 			freeColNames(col, nc);
    486 			nis_freeresult(res);
    487 			return (0);
    488 		}
    489 
    490 		nv++;
    491 	}
    492 
    493 	freeColNames(col, nc);
    494 	nis_freeresult(res);
    495 
    496 	if (numVals != 0)
    497 		*numVals = nv;
    498 
    499 	return (rv);
    500 }
    501 
    502 /*
    503  * Retrieve a copy of the specified NIS+ object. Upon successful return,
    504  * the return value is LDAP_SUCCESS, *outRes contains the nis_result
    505  * pointer, and there's at least one object in the result.
    506  *
    507  * On error, return a status other than LDAP_SUCCESS.
    508  */
    509 int
    510 getNisPlusObj(char *name, char *msg, nis_result **outRes) {
    511 	nis_result	*res;
    512 	char		*objName;
    513 	char		*myself = "getNisPlusObj";
    514 
    515 	objName = fullObjName(F, name);
    516 	if (objName == 0) {
    517 		return ((name == 0) ? LDAP_PARAM_ERROR : LDAP_NO_MEMORY);
    518 	}
    519 
    520 	if (msg == 0)
    521 		msg = myself;
    522 
    523 	res = nis_lookup(objName, 0);
    524 
    525 	if (res == 0) {
    526 		sfree(objName);
    527 		return (LDAP_NO_MEMORY);
    528 	}
    529 
    530 	if (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS) {
    531 		int	msgtype = MSG_NOTIMECHECK;
    532 
    533 		if (res->status == NIS_COLDSTART_ERR)
    534 			msgtype = MSG_NONPCOLDSTART;
    535 
    536 		logmsg(msgtype, LOG_ERR,
    537 			"%s: nis_lookup(\"%s\", 0) => %d (%s)",
    538 			msg, objName, res->status, nis_sperrno(res->status));
    539 		sfree(objName);
    540 		nis_freeresult(res);
    541 		return (LDAP_OPERATIONS_ERROR);
    542 	}
    543 
    544 	if (res->objects.objects_len <= 0) {
    545 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
    546 			"%s: nis_lookup(\"%s\", 0) => no objects",
    547 			msg, objName);
    548 		sfree(objName);
    549 		nis_freeresult(res);
    550 		return (LDAP_OPERATIONS_ERROR);
    551 	}
    552 
    553 	if (res->objects.objects_len > 1) {
    554 		if (verbose)
    555 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    556 				"%s: Ignoring excess objects (%d) for \"%s\"",
    557 				msg, res->objects.objects_len - 1, objName);
    558 	}
    559 
    560 	sfree(objName);
    561 
    562 	if (outRes != 0) {
    563 		*outRes = res;
    564 	} else {
    565 		nis_freeresult(res);
    566 		return (LDAP_PARAM_ERROR);
    567 	}
    568 
    569 	return (LDAP_SUCCESS);
    570 }
    571 
    572 __nis_value_t *
    573 lookupNisPlus(__nis_obj_spec_t *obj, char *col, __nis_rule_value_t *rvIn) {
    574 	char			*objname;
    575 	__nis_rule_value_t	*rv;
    576 	int			i, nv;
    577 	__nis_value_t		*val;
    578 	char			*myself = "lookupNisPlus";
    579 
    580 	if (obj == 0 || col == 0)
    581 		return (0);
    582 
    583 	objname = fullObjName(F, obj->name);
    584 	if (objname == 0)
    585 		return (0);
    586 
    587 	rv = getNisPlusEntry(&obj->index, objname, rvIn, &nv);
    588 	sfree(objname);
    589 	if (rv == 0)
    590 		return (0);
    591 
    592 	val = am(myself, sizeof (*val));
    593 	if (val == 0) {
    594 		freeRuleValue(rv, nv);
    595 		return (0);
    596 	}
    597 
    598 	for (i = 0, val->numVals = 0; i < nv; i++) {
    599 		int		j;
    600 		__nis_value_t	*oldval;
    601 
    602 		for (j = 0; j < rv[i].numColumns; j++) {
    603 			if (strcmp(col, rv[i].colName[j]) == 0)
    604 				break;
    605 		}
    606 		if (j >= rv[i].numColumns)
    607 			continue;
    608 
    609 		oldval = val;
    610 		val = concatenateValues(val, &rv[i].colVal[j]);
    611 		freeValue(oldval, 1);
    612 		if (val == 0) {
    613 			freeRuleValue(rv, nv);
    614 			return (0);
    615 		}
    616 	}
    617 
    618 	freeRuleValue(rv, nv);
    619 
    620 	if (val->numVals == 0) {
    621 		freeValue(val, 1);
    622 		val = 0;
    623 	}
    624 
    625 	return (val);
    626 }
    627 
    628 /*
    629  * Store the specified NIS+ colname/value in the indicated entry.
    630  * The 'table' is used if the 'item->searchSpec.obj.name' is
    631  * unspecified. If the 'item' doesn't contain an index spec (and
    632  * hence indication of the exact NIS+ entry to update), an index
    633  * spec is constructed from 'rv'.
    634  */
    635 nis_error
    636 storeNisPlus(__nis_mapping_item_t *item, int index, int numItems,
    637 		__nis_rule_value_t *rv, char *table, __nis_value_t *val) {
    638 	__nis_buffer_t	b = {0, 0};
    639 	nis_result	*res, *mres;
    640 	char		**col = 0;
    641 	int		i, err, nc = 0, ic;
    642 	zotypes		ttype;
    643 	nis_object	*o;
    644 	entry_obj	*e;
    645 	uint_t		orgEcFlg;
    646 	uint_t		orgEcLen;
    647 	char		*orgEcVal;
    648 	char		*myself = "storeNisPlus";
    649 
    650 	if (item == 0 || val == 0 || val->numVals != 1 || index < 0 ||
    651 			index >= numItems ||
    652 			item->type != mit_nisplus || item->name == 0 ||
    653 			item->searchSpec.obj.name == 0)
    654 		return (NIS_BADREQUEST);
    655 
    656 	/* Check that the table has column with the desired name */
    657 	if (slen(item->searchSpec.obj.name) > 0)
    658 		table = item->searchSpec.obj.name;
    659 	table = fullObjName(F, table);
    660 	if (slen(table) <= 0)
    661 		return (NIS_NOMEMORY);
    662 	if (LDAP_SUCCESS != initializeColumnNames(table, &col, &nc, &ttype,
    663 			0) || ttype != NIS_TABLE_OBJ) {
    664 		freeColNames(col, nc);
    665 		sfree(table);
    666 		return (NIS_NOSUCHTABLE);
    667 	}
    668 
    669 	for (ic = 0; ic < nc; ic++) {
    670 		if (strcmp(item->name, col[ic]) == 0)
    671 			break;
    672 	}
    673 	freeColNames(col, nc);
    674 	if (ic >= nc) {
    675 		sfree(table);
    676 		return (NIS_BADATTRIBUTE);
    677 	}
    678 
    679 	/* Construct the index entry object name */
    680 	if (item->searchSpec.obj.index.numIndexes > 0) {
    681 		b.buf = index2string(myself, &item->searchSpec.obj.index, 0,
    682 					table);
    683 		b.len = slen(b.buf);
    684 		if (b.buf == 0 || b.len <= 0) {
    685 			sfree(b.buf);
    686 			sfree(table);
    687 			return (NIS_NOMEMORY);
    688 		}
    689 	} else if (rv != 0 && rv->numColumns > 0) {
    690 		/* Construct index value from rule-value */
    691 		bp2buf(myself, &b, "[");
    692 		for (i = 0; i < rv->numColumns; i++) {
    693 			if (slen(rv->colName[i]) <= 0 ||
    694 					rv->colVal[i].type != vt_string ||
    695 					rv->colVal[i].numVals != 1 ||
    696 					slen(rv->colVal[i].val[0].value) <= 0)
    697 				continue;
    698 			if (i == 0)
    699 				bp2buf(myself, &b, "%s=%s", rv->colName[i],
    700 					rv->colVal[i].val[0].value);
    701 			else
    702 				bp2buf(myself, &b, ",s=%s", rv->colName[i],
    703 					rv->colVal[i].val[0].value);
    704 		}
    705 		bp2buf(myself, &b, "]");
    706 	} else {
    707 		/* Can't identify entry to modify */
    708 		sfree(table);
    709 		return (NIS_NOTFOUND);
    710 	}
    711 	if (strcmp("[]", b.buf) == 0) {
    712 		sfree(b.buf);
    713 		sfree(table);
    714 		return (NIS_NOTFOUND);
    715 	}
    716 	bp2buf(myself, &b, "%s", table);
    717 	sfree(table);
    718 
    719 	/* Look it up */
    720 	res = nis_list(b.buf, 0, 0, 0);
    721 	if (res == 0) {
    722 		sfree(b.buf);
    723 		return (NIS_NOMEMORY);	/* Likely guess */
    724 	} else if (res->status != NIS_SUCCESS &&
    725 			res->status != NIS_S_SUCCESS) {
    726 		err = res->status;
    727 		sfree(b.buf);
    728 		nis_freeresult(res);
    729 		return (err);
    730 	}
    731 
    732 	/* We only want one object, and it must be an entry */
    733 	if (res->objects.objects_len != 1 ||
    734 			(o = res->objects.objects_val) == 0 ||
    735 			o->zo_data.zo_type != NIS_ENTRY_OBJ) {
    736 		sfree(b.buf);
    737 		nis_freeresult(res);
    738 		return (NIS_BADOBJECT);
    739 	}
    740 
    741 	/* Verify that the column index 'ic' is in range */
    742 	e = &o->zo_data.objdata_u.en_data;
    743 	if (ic >= e->en_cols.en_cols_len) {
    744 		sfree(b.buf);
    745 		nis_freeresult(res);
    746 		return (NIS_TYPEMISMATCH);
    747 	}
    748 
    749 	/*
    750 	 * Replace the indicated column value, and set the EN_MODIFIED flag.
    751 	 * We keep track of the original value, so that we can restore it
    752 	 * before destroying 'res'.
    753 	 */
    754 	orgEcFlg = e->en_cols.en_cols_val[ic].ec_flags;
    755 	orgEcLen = e->en_cols.en_cols_val[ic].ec_value.ec_value_len;
    756 	orgEcVal = e->en_cols.en_cols_val[ic].ec_value.ec_value_val;
    757 	/*
    758 	 * We always make sure that the val->val[].value's have a NUL
    759 	 * at the end. However, the 'length' usually doesn't include
    760 	 * the NUL. Since NIS+ wants the NUL counted, we increase the
    761 	 * length by one if:
    762 	 *	(a) the val->type is vt_string, and
    763 	 *	(b) val->val[].value[val->val[].length-1] isn't already
    764 	 *	    NUL, and
    765 	 *	(c) val->val[].value[val->val[].length] is NUL.
    766 	 */
    767 	if (val->type == vt_string && val->val[0].length > 0 &&
    768 		((char *)val->val[0].value)[val->val[0].length-1] != '\0' &&
    769 		((char *)val->val[0].value)[val->val[0].length] == '\0')
    770 		val->val[0].length++;
    771 	e->en_cols.en_cols_val[ic].ec_flags |= EN_MODIFIED;
    772 	e->en_cols.en_cols_val[ic].ec_value.ec_value_len = val->val[0].length;
    773 	e->en_cols.en_cols_val[ic].ec_value.ec_value_val = val->val[0].value;
    774 
    775 	mres = nis_modify_entry(b.buf, o, MOD_SAMEOBJ);
    776 
    777 	/* Restore 'res', and destroy it */
    778 	e->en_cols.en_cols_val[ic].ec_flags = orgEcFlg;
    779 	e->en_cols.en_cols_val[ic].ec_value.ec_value_len = orgEcLen;
    780 	e->en_cols.en_cols_val[ic].ec_value.ec_value_val = orgEcVal;
    781 	nis_freeresult(res);
    782 
    783 	/* Don't need the indexed name anymore */
    784 	sfree(b.buf);
    785 
    786 	/* Set the return status, and destroy the modification result */
    787 	if (mres != 0) {
    788 		err = mres->status;
    789 		nis_freeresult(mres);
    790 	} else {
    791 		err = NIS_NOMEMORY;
    792 	}
    793 
    794 	return (err);
    795 }
    796 
    797 int
    798 copyColumnNames(nis_object *o, char ***column, int *numColumns) {
    799 	int		i, nc, stat;
    800 	char		**name;
    801 	char		*myself = "copyColumnNames";
    802 
    803 	if (o == 0 || column == 0 || numColumns == 0)
    804 		return (LDAP_PARAM_ERROR);
    805 
    806 	if (*column == 0 && *numColumns < 0) {
    807 		/*
    808 		 * This table mapping is used to map the table object
    809 		 * itself. Since that's indicated by t->column == 0 and
    810 		 * t->numColumns == -1, we definitely don't want to set
    811 		 * the column names.
    812 		 */
    813 		return (LDAP_SUCCESS);
    814 	}
    815 
    816 	freeColNames(*column, *numColumns);
    817 	*column = 0;
    818 	*numColumns = 0;
    819 
    820 	if (o->zo_data.zo_type != NIS_TABLE_OBJ) {
    821 		/*
    822 		 * Since we can map non-table objects, this isn't really
    823 		 * an error, but we return a special value to tell our
    824 		 * caller that this isn't a table (as opposed to a table
    825 		 * with zero columns).
    826 		 */
    827 		return (LDAP_OBJECT_CLASS_VIOLATION);
    828 	}
    829 
    830 	nc = o->zo_data.objdata_u.ta_data.ta_cols.ta_cols_len;
    831 	if (nc < 0) {
    832 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
    833 			"%s: negative column count (%d) for \"%s.%s\"",
    834 			myself, nc, NIL(o->zo_name), NIL(o->zo_domain));
    835 		return (LDAP_DECODING_ERROR);
    836 	}
    837 
    838 	if (nc == 0 || o->zo_data.objdata_u.ta_data.ta_cols.ta_cols_val == 0) {
    839 		return (LDAP_SUCCESS);
    840 	}
    841 
    842 	name = am(myself, nc * sizeof (name[0]));
    843 	if (name == 0) {
    844 		return (LDAP_NO_MEMORY);
    845 	}
    846 
    847 	for (i = 0; i < nc; i++) {
    848 		if (o->zo_data.objdata_u.ta_data.ta_cols.
    849 				ta_cols_val[i].tc_name == 0)
    850 			continue;
    851 		name[i] = sdup(myself, T, o->zo_data.objdata_u.ta_data.ta_cols.
    852 						ta_cols_val[i].tc_name);
    853 		if (name[i] == 0) {
    854 			for (--i; i >= 0; i--)
    855 				sfree(name[i]);
    856 			free(name);
    857 			return (LDAP_NO_MEMORY);
    858 		}
    859 	}
    860 
    861 	*column = name;
    862 	*numColumns = nc;
    863 
    864 	return (LDAP_SUCCESS);
    865 }
    866 
    867 /*
    868  * Initialize the column name list for the specified table name
    869  * (which must be fully qualified).
    870  *
    871  * Returns an LDAP error status
    872  */
    873 int
    874 initializeColumnNames(char *table, char ***column, int *numColumns,
    875 			zotypes *type, nis_object **obj) {
    876 	nis_result	*res = 0;
    877 	int		stat;
    878 	char		*myself = "initializeColumnNames";
    879 
    880 	if (table == 0 || column == 0 || numColumns == 0 || type == 0)
    881 		return (LDAP_PARAM_ERROR);
    882 
    883 	stat = getNisPlusObj(table, myself, &res);
    884 	if (stat != LDAP_SUCCESS)
    885 		return (stat);
    886 
    887 	*type = res->objects.objects_val->zo_data.zo_type;
    888 	*column = 0;
    889 	*numColumns = 0;
    890 
    891 	stat = copyColumnNames(res->objects.objects_val, column, numColumns);
    892 
    893 	nis_freeresult(res);
    894 
    895 	return (stat);
    896 }
    897 
    898 /*
    899  * Sets the oid (i.e., the creation and modification times) for the
    900  * specified object. In order to avoid retrieving the old incarnation
    901  * (if any) from the DB first, we're punting and setting both mtime
    902  * and ctime to the current time.
    903  */
    904 void
    905 setOid(nis_object *obj) {
    906 	if (obj != 0) {
    907 		obj->zo_oid.ctime = obj->zo_oid.mtime = time(0);
    908 	}
    909 }
    910