Home | History | Annotate | Download | only in rpc.nisd
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 
     28 /*
     29  * Ported from
     30  * "@(#)nis_ib_proc.c 1.37 91/03/21 Copyr 1990 Sun Micro";
     31  *
     32  *	nis_ib_proc.c
     33  *
     34  * This module contains information base function of NIS Version 3
     35  * NB : It provides the routines that the dispatch function in nis_svc.c
     36  * call. That file, nis_svc.c, is automatically generated and reflects the
     37  * interface definition that is described in the nis.x file. When the
     38  * nis.x file changes, you must make sure that any parameters that change
     39  * get reflected in these routines.
     40  *
     41  */
     42 
     43 #include <stdio.h>
     44 #include <syslog.h>
     45 #include <stdlib.h>
     46 #include <rpc/rpc.h>
     47 #include <rpc/clnt.h>
     48 #include <rpc/svc.h>
     49 #include <rpc/auth.h>
     50 #include <rpc/auth_des.h>
     51 #include <rpcsvc/nis.h>
     52 #include <rpcsvc/nis_callback.h>
     53 #include "nis_proc.h"
     54 #include "nisdb_mt.h"
     55 
     56 #include <string.h>
     57 #include <netdir.h>
     58 #include <netconfig.h>
     59 #include <shadow.h>
     60 
     61 extern bool_t 	xdr_nis_result();
     62 extern bool_t 	xdr_nis_error();
     63 extern bool_t 	xdr_nis_object();
     64 extern void	(*_svc_getreqset_proc)();
     65 extern nis_server *__nis_server_dup(nis_server *, nis_server *);
     66 
     67 #define	NO_ENTRY_OBJS	96
     68 
     69 /*
     70  * nis_censor_object[_attr]
     71  *
     72  * This function enforces the access rights policies for objects.
     73  * It is called with a candidate ENTRY object and a principal name.
     74  * The result is either NULL (object not readable) or a pointer to
     75  * an object that has been "censored" by this code to remove sensitive
     76  * columns or encrypt encrypted columns.
     77  * nis_censor_object_attr adds a fix which ensures that
     78  * users cannot infer information from the database by using special
     79  * search criteria.
     80  */
     81 nis_object *
     82 nis_censor_object_attr(o, tc, p, zn, za)
     83 	nis_object	*o;	/* The object to censor 	*/
     84 	table_col	*tc;	/* Table column descriptor 	*/
     85 	nis_name	p;	/* The principal in question 	*/
     86 	uint_t		zn;	/* Number of attributes 	*/
     87 	nis_attr	*za;	/* The search attributes 	*/
     88 {
     89 	nis_object		zo;	/* our censored object */
     90 	int			mc;	/* Number of columns */
     91 	int			i, j,
     92 				somedata;
     93 #define	buf			__nis_get_tsd()->censor_object_buf
     94 	entry_col		*oec,	/* The old columns pointer */
     95 				*ec;	/* Some temporary buffer */
     96 
     97 
     98 	/* copy the object */
     99 	zo = *o;
    100 
    101 	mc = o->EN_data.en_cols.en_cols_len;
    102 	/* get some temporay space. */
    103 	ec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc);
    104 	if (! ec)
    105 		return (NULL);
    106 
    107 	/* Use our static version of the columns */
    108 	zo.EN_data.en_cols.en_cols_val = ec;
    109 
    110 	/* Get a pointer to the original columns */
    111 	oec = o->EN_data.en_cols.en_cols_val;
    112 	somedata = 0;
    113 	for (j = 0; j < mc; j++) {
    114 		ec[j] = oec[j]; /* copy the pointers */
    115 		ec[j].ec_flags = 0;
    116 		/* Transfer the flags values */
    117 		if (tc[j].tc_flags & TA_BINARY)
    118 			ec[j].ec_flags |= EN_BINARY;
    119 		if (tc[j].tc_flags & TA_XDR)
    120 			ec[j].ec_flags |= EN_XDR;
    121 #ifdef NIS_CRYPT_SUPPORT
    122 		if (tc[j].tc_flags & TA_CRYPT)
    123 			ec[j].ec_flags |= EN_CRYPT;
    124 #endif
    125 
    126 		/* Check to see if we can read this column */
    127 		if (! __can_do(NIS_READ_ACC, tc[j].tc_rights, o, p)) {
    128 		    /* close inference holes */
    129 		    for (i = 0; i < zn; i++)
    130 			if (za[i].zattr_ndx &&
    131 			    (strcasecmp(tc[j].tc_name, za[i].zattr_ndx) == 0))
    132 			    return (NULL);
    133 		    /* Replace with No Permission value */
    134 		    ec[j].ENLEN = 5;
    135 		    ec[j].ENVAL = NOPWDRTR;
    136 		} else {
    137 			somedata++;
    138 #ifdef NIS_CRYPT_SUPPORT
    139 			if (ec[j].ec_flags & EN_CRYPT) {
    140 				/* Encrypt the data using the session key */
    141 				/* XXX */
    142 			}
    143 #endif
    144 		}
    145 	}
    146 	if (somedata == NULL)
    147 		return (NULL);
    148 
    149 	return (nis_clone_object(&zo, 0));
    150 }
    151 
    152 #undef	buf
    153 
    154 /*
    155  * The original function (nis_censor_object) takes
    156  * the old arguments, and passes them on with new placeholder arguments to
    157  * the new function (nis_censor_object_attr).  Only YP compat functions
    158  * still call nis_censor_object.
    159  */
    160 nis_object
    161 *nis_censor_object(o, tc, p)
    162 	nis_object	*o;	/* The object to censor 	*/
    163 	table_col	*tc;	/* Table column descriptor 	*/
    164 	nis_name	p;	/* The principal in question 	*/
    165 {
    166 	return (nis_censor_object_attr(o, tc, p, 0, NULL));
    167 }
    168 
    169 /*
    170  * This is the free function for the list results that are returned "enmasse"
    171  * without a callback.
    172  */
    173 static void
    174 free_rlist(entries)
    175 	nis_object *entries;
    176 {
    177 	XFREE(entries);
    178 }
    179 
    180 /*
    181  * nis_return_list()
    182  *
    183  * This function converts a full list of entries returned by the database
    184  * into a list that can be returned to the user. It has two main functions,
    185  * 	#1) Remove those entries from the list which the client does not
    186  *	    have read access too.
    187  *	#2) If the client has read access to the entry, censor those
    188  *	    columns which the client does not have read access to.
    189  */
    190 
    191 nis_object *
    192 nis_return_list(obj, list, num, p, got, ar, zn, za)
    193 	nis_object	*obj; 	/* Information base object */
    194 	obj_list	*list;	/* Array of objects	   */
    195 	int		num;	/* Size of the array	   */
    196 	nis_name	p;	/* principal making reqst  */
    197 	int		*got;	/* Number actually returned */
    198 	int		ar;	/* All readable flag	    */
    199 	uint_t		zn;	/* All readable flag	*/
    200 	nis_attr	*za;	/* The search attributes    */
    201 {
    202 	register table_col	*tc;		/* Table column pointer    */
    203 	nis_object		*eo;		/* Temporary object	   */
    204 	nis_object		*entries;	/* our result pointer	   */
    205 	int			i, 		/* Temporary counters	   */
    206 				cur_entry;	/* The current entry	   */
    207 
    208 	*got = 0;
    209 	/* Get some parameters from the table object */
    210 	if (__type_of(obj) == NIS_TABLE_OBJ)
    211 		tc = obj->TA_data.ta_cols.ta_cols_val;
    212 	else
    213 		tc = tbl_prototype.ta_cols.ta_cols_val;
    214 
    215 	/* This may allocate more than we need but it is faster this way */
    216 	entries = (nis_object *)XCALLOC(num+1, sizeof (nis_object));
    217 	if (! entries)
    218 		return (NULL);
    219 
    220 	add_cleanup(free_rlist, (char *)entries, "nis_return_list array.");
    221 
    222 	if (entries == NULL)
    223 		return (NULL);
    224 
    225 	for (i = 0, cur_entry = 0; i < num; i++) {
    226 		if (! list[i].o)
    227 			continue;
    228 		if (ar ||
    229 		__can_do(NIS_READ_ACC, list[i].o->zo_access, list[i].o, p)) {
    230 			entries[cur_entry] = *list[i].o;
    231 			cur_entry++;
    232 		} else {
    233 			eo = nis_censor_object_attr(list[i].o, tc, p, zn, za);
    234 			if (eo) {
    235 				*(entries+cur_entry) = *eo;
    236 				add_cleanup(nis_destroy_object, eo,
    237 					"nis_censor_object_attr result");
    238 				cur_entry++;
    239 			}
    240 		}
    241 	}
    242 
    243 	*got = cur_entry;
    244 	return (entries);
    245 }
    246 
    247 /*
    248  * __search_ok()
    249  *
    250  * This function does a sanity check on the request (which has attribute
    251  * value pairs) and the table which defines the names of searchable columns.
    252  * if an attribute is passed that doesn't match up to a column the request
    253  * is rejected.
    254  */
    255 static int
    256 __search_ok(zn, za, tobj)
    257 	uint_t		zn;	/* Number of attributes 	*/
    258 	nis_attr	*za;	/* The search attributes 	*/
    259 	nis_object	*tobj;	/* A Table object		*/
    260 {
    261 	register table_col	*tc;
    262 	short			acm[256]; /* XXX HARD LIMIT on columns XXX */
    263 	int			i, j, mc;
    264 
    265 	if (__type_of(tobj) == NIS_TABLE_OBJ) {
    266 		mc = tobj->TA_data.ta_maxcol;
    267 		tc = tobj->TA_data.ta_cols.ta_cols_val;
    268 	} else {
    269 		/* Can't specify anything for directories */
    270 		return (zn == 0);
    271 	}
    272 	for (i = 0; i < zn; i++) {
    273 		acm[i] = -1; /* Entry column map */
    274 		for (j = 0; j < mc; j++) {
    275 			if ((tc[j].tc_flags & TA_SEARCHABLE) &&
    276 			    (strcasecmp(tc[j].tc_name, za[i].zattr_ndx)
    277 								    == 0)) {
    278 				if (acm[i] == -1)
    279 					acm[i] = j;
    280 				else
    281 					acm[i] = -2;
    282 				break;
    283 			}
    284 		}
    285 
    286 		/*
    287 		 *  If we didn't find a searchable column with the name,
    288 		 *  check to see if it is a multival column.  If it is,
    289 		 *  then continue on to the other attributes.  We don't
    290 		 *  update acm because multival columns can be specified
    291 		 *  multiple times.
    292 		 */
    293 		if (acm[i] == -1) {
    294 			if (multival_check(tobj, &za[i], tc, mc))
    295 				continue;
    296 		}
    297 
    298 		/* Didn't match or matched twice */
    299 		if (acm[i] < 0) {
    300 			return (0);
    301 		}
    302 
    303 	}
    304 	return (1);
    305 }
    306 
    307 /*
    308  * __nis_listinit()
    309  *
    310  * This function set's up either a list or first/next operation.
    311  * If it is successful, it returns a pointer to the object to be
    312  * listed (either a table or directory object), if it is unsuccessful
    313  * it returns NULL and fills in the result structure appropriately.
    314  */
    315 static nis_object *
    316 __nis_listinit(res, argp, princp)
    317 	nis_result	*res;
    318 	ib_request	*argp;
    319 	nis_name	princp;
    320 {
    321 	nis_object	*d_obj, *ib_obj = NULL;
    322 	struct ticks	t;
    323 	nis_db_result	*dbres;
    324 	nis_error	xx;
    325 	enum name_pos	p;
    326 
    327 	NIS_RES_NUMOBJ(res) = 0;
    328 	p = nis_dir_cmp(argp->ibr_name, __nis_rpc_domain());
    329 	if ((p != SAME_NAME) && (p != LOWER_NAME)) {
    330 		res->status = NIS_BADNAME;
    331 		return (NULL);
    332 	}
    333 
    334 	/* Make sure we serve this directory. */
    335 	xx = __directory_object(nis_domain_of(argp->ibr_name), &t, 0, &d_obj);
    336 	res->aticks = t.aticks;
    337 	res->dticks = t.dticks;
    338 	res->cticks = t.cticks;
    339 	res->zticks = t.zticks;
    340 
    341 	if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) {
    342 		/*
    343 		 * Hmm, we didn't find it. Maybe it _is_ our directory
    344 		 * object.
    345 		 */
    346 		xx = __directory_object(argp->ibr_name, &t, 0, &d_obj);
    347 		res->aticks += t.aticks;
    348 		res->dticks += t.dticks;
    349 		res->cticks += t.cticks;
    350 		res->zticks += t.zticks;
    351 		if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) {
    352 			/* They really blew it */
    353 			syslog(LOG_INFO, "Can't find directory for %s",
    354 						argp->ibr_name);
    355 			res->status = xx;
    356 			return (NULL);
    357 		} else {
    358 			ib_obj = d_obj;
    359 		}
    360 	}
    361 
    362 	/* Now get the information base object from the database */
    363 	if (! ib_obj) {
    364 		dbres = db_lookup(argp->ibr_name);
    365 		res->dticks += dbres->ticks;
    366 		if (dbres->status == NIS_NOSUCHTABLE) {
    367 			/* This means table of directory does not exist. */
    368 			dbres->status =	recover_from_no_table(d_obj,
    369 						nis_domain_of(argp->ibr_name));
    370 			/* for sure, argp->ibr_name will not be there */
    371 			if (dbres->status == NIS_SUCCESS)
    372 				dbres->status = NIS_NOTFOUND;
    373 		}
    374 		if (dbres->status != NIS_SUCCESS) {
    375 			if (dbres->status == NIS_NOTFOUND)
    376 				res->status = NIS_NOSUCHTABLE;
    377 			else
    378 				res->status = dbres->status;
    379 			res->zticks += __stop_clock(0);
    380 			return (NULL);
    381 		}
    382 		ib_obj = dbres->obj;
    383 	} else {
    384 		/*
    385 		 * If its a directory, make sure we've got a database
    386 		 * for it. (if not create one and synchronize)
    387 		 */
    388 		nis_error status = db_find_table(argp->ibr_name);
    389 		switch (status) {
    390 		case NIS_SUCCESS:
    391 			break;
    392 		case NIS_NOSUCHTABLE:
    393 			res->status = recover_from_no_table(d_obj,
    394 							    argp->ibr_name);
    395 			if (res->status != NIS_SUCCESS) {
    396 				return (NULL);
    397 			}
    398 			break;
    399 		default:
    400 			res->status = status;
    401 			return (NULL);
    402 		}
    403 	}
    404 
    405 	switch (__type_of(ib_obj)) {
    406 	    case NIS_TABLE_OBJ:
    407 	    case NIS_DIRECTORY_OBJ:
    408 		break;
    409 
    410 	    case NIS_LINK_OBJ:
    411 		if (verbose)
    412 			syslog(LOG_INFO, "__nis_listinit: Object is a link.");
    413 		/*
    414 		 * we return a partial result indicating that the LINK was
    415 		 * found and the client can follow it if they choose to.
    416 		 */
    417 		res->status = NIS_PARTIAL;
    418 		NIS_RES_NUMOBJ(res) = 1;
    419 		NIS_RES_OBJECT(res) = ib_obj;
    420 		return (NULL);
    421 
    422 	    default:
    423 		/*
    424 		 * XXX this hard codes the "kinds" of objects that can be
    425 		 * information bases. This is not nice if we want to support
    426 		 * user designed objects.
    427 		 */
    428 		res->status = NIS_INVALIDOBJ;
    429 		if (verbose)
    430 			syslog(LOG_INFO,
    431 			    "__nis_listinit: Nonsearchable object (type = %d).",
    432 							__type_of(ib_obj));
    433 		return (NULL);
    434 	}
    435 
    436 	/* Check to see is we have a valid search criteria */
    437 	if (!__search_ok(argp->ibr_srch.ibr_srch_len,
    438 			argp->ibr_srch.ibr_srch_val, ib_obj)) {
    439 		res->status = NIS_BADATTRIBUTE;
    440 		if (verbose)
    441 			syslog(LOG_INFO,
    442 			    "__nis_listinit: Unparsable attribute failure.");
    443 		return (NULL);
    444 	}
    445 
    446 	res->status = NIS_SUCCESS;
    447 	return (ib_obj);
    448 }
    449 
    450 /*
    451  * Does the callback part of iblist
    452  */
    453 static nis_result *
    454 nis_iblist_callback(argp, pname, ib_obj, all_read, res)
    455 	ib_request	*argp;
    456 	char		*pname;		/* caller's name */
    457 	nis_object	*ib_obj;
    458 	int		all_read;	/* all readable flag		*/
    459 	nis_result	*res;
    460 {
    461 	nis_error	err;
    462 	nis_object	*d_obj;
    463 	int		pid = -1; /* Process ID of child making call back */
    464 	int		i, queued;
    465 	cback_data	cbarg;		/* Callback arguments		*/
    466 				/* An array of object pointers	*/
    467 	struct timeval	tv;		/* Timeout for callback		*/
    468 	nis_fn_result	*fnr;
    469 	netobj		cookie;		/* Cookie storage for first/nxt	*/
    470 	enum clnt_stat	status = RPC_SUCCESS;
    471 	nis_error	result;
    472 	table_col	*tc;
    473 	char		tblbuf[NIS_MAXNAMELEN * 2];
    474 	char		*table;
    475 	nis_attr	*a;
    476 	int		na;	/* number of regular attributes */
    477 	int		nm;	/* number of multival attributes */
    478 
    479 
    480 	/*
    481 	 * Used to create the callback handle here. We now delay
    482 	 * until we know that we actually intend to perform the callback.
    483 	 */
    484 
    485 	table = internal_table_name(argp->ibr_name, tblbuf);
    486 	if (! table) {
    487 		res->status = NIS_BADNAME;
    488 		return (res);
    489 	}
    490 
    491 	na = argp->ibr_srch.ibr_srch_len;
    492 	a = argp->ibr_srch.ibr_srch_val;
    493 	err = multival_attr(ib_obj, a, &na, &nm);
    494 	if (err != NIS_SUCCESS) {
    495 		res->status = err;
    496 		return (res);
    497 	}
    498 
    499 	/*
    500 	 * This will force the database to be loaded.
    501 	 */
    502 	fnr = db_firstib(argp->ibr_name, na, a, FN_MANGLE+FN_NORAGS, table);
    503 
    504 	if (fnr->status != NIS_SUCCESS) {
    505 		if (fnr->status == NIS_NOTFOUND) {
    506 			res->status = NIS_PARTIAL;
    507 			res->objects.objects_val = ib_obj;
    508 			res->objects.objects_len = 1;
    509 		} else {
    510 			res->status = fnr->status;
    511 			res->objects.objects_val = NULL;
    512 			res->objects.objects_len = 0;
    513 		}
    514 		res->dticks += fnr->ticks;
    515 		res->zticks += __stop_clock(0);
    516 		XFREE(fnr);
    517 		fnr = NULL;
    518 		return (res);
    519 	}
    520 
    521 	/* Create a new thread to perform the callback */
    522 	{
    523 		pthread_t		tid;
    524 		pthread_attr_t		attr;
    525 		int			stat;
    526 		callback_thread_arg_t	*cbtarg;
    527 		int			attrsize = na * sizeof (cbtarg->a[0]);
    528 		nis_server		*srvl;
    529 
    530 		if ((cbtarg = calloc(1, sizeof (*cbtarg))) == 0) {
    531 			syslog(LOG_WARNING,
    532 		"nis_iblist_callback: memory allocation failed for %d bytes",
    533 				sizeof (*cbtarg));
    534 			res->status = NIS_NOMEMORY;
    535 			res->zticks = __stop_clock(0);
    536 			XFREE(fnr);
    537 			return (res);
    538 		}
    539 		cbtarg->fnr = fnr;
    540 		cbtarg->ib_obj = nis_clone_object(ib_obj, 0);
    541 		if (na <= (sizeof (cbtarg->a)/sizeof (cbtarg->a[0]))) {
    542 			cbtarg->na = na;
    543 		} else {
    544 			syslog(LOG_WARNING,
    545 	"nis_iblist_callback: too many attributes; max %d expected, %d found",
    546 			(sizeof (cbtarg->a)/sizeof (cbtarg->a[0])), na);
    547 			cbtarg->na = sizeof (cbtarg->a)/sizeof (cbtarg->a[0]);
    548 #ifdef	NIS_MT_DEBUG
    549 			abort();
    550 #endif	/* NIS_MT_DEBUG */
    551 		}
    552 		memcpy(cbtarg->a, a, cbtarg->na * sizeof (cbtarg->a[0]));
    553 		cbtarg->nm = nm;
    554 		cbtarg->all_read = all_read;
    555 		strncpy(cbtarg->pname, pname, sizeof (cbtarg->pname));
    556 		cbtarg->pname[sizeof (cbtarg->pname) - 1] = '\0';
    557 		cbtarg->cbarg = cbarg;
    558 		srvl = (nis_server *) __nis_server_dup(
    559 		    (nis_server *)argp->ibr_cbhost.ibr_cbhost_val, NULL);
    560 		if (srvl == NULL) {
    561 			syslog(LOG_WARNING,
    562 			    "nis_iblist_callback: out of memory");
    563 			res->status = NIS_NOMEMORY;
    564 			res->zticks = __stop_clock(0);
    565 			XFREE(fnr);
    566 			free(cbtarg);
    567 			return (res);
    568 		}
    569 		cbtarg->nserver = srvl;
    570 		strncpy(cbtarg->cbhostname,
    571 			argp->ibr_cbhost.ibr_cbhost_val[0].name,
    572 			sizeof (cbtarg->cbhostname));
    573 		cbtarg->cbhostname[sizeof (cbtarg->cbhostname) - 1] = '\0';
    574 		strncpy(cbtarg->ibr_name,
    575 			argp->ibr_name, sizeof (cbtarg->ibr_name));
    576 		cbtarg->ibr_name[sizeof (cbtarg->ibr_name) - 1] = '\0';
    577 
    578 		(void) pthread_attr_init(&attr);
    579 		(void) pthread_attr_setdetachstate(&attr,
    580 						PTHREAD_CREATE_DETACHED);
    581 		if ((stat = pthread_create(&tid, &attr, callback_thread,
    582 			cbtarg)) == 0) {
    583 			if (verbose)
    584 				syslog(LOG_INFO,
    585 		"nis_iblist_callback: created callback thread %d", tid);
    586 			res->status = NIS_CBRESULTS;
    587 		} else {
    588 			if (verbose)
    589 				syslog(LOG_WARNING,
    590 		"nis_iblist_callback: callback thread create failed: %d",
    591 					stat);
    592 			res->status = NIS_TRYAGAIN;
    593 		}
    594 		res->zticks += __stop_clock(0);
    595 
    596 		(void) pthread_attr_destroy(&attr);
    597 
    598 		res->cookie.n_bytes = (char *)malloc(sizeof (anonid_t));
    599 		if (res->cookie.n_bytes == NULL) {
    600 			syslog(LOG_WARNING,
    601 			"Couldn't allocate memory for callback id");
    602 			/* what error to return in case of no memory */
    603 			res->status = NIS_NOMEMORY;
    604 		} else {
    605 			anonid_t	anonid = tid;
    606 			add_cleanup((void (*)())XFREE,
    607 				(char *)res->cookie.n_bytes, "callback id");
    608 			res->cookie.n_len = sizeof (anonid);
    609 			memcpy(res->cookie.n_bytes, &anonid,
    610 							sizeof (anonid));
    611 			if (verbose)
    612 				syslog(LOG_INFO,
    613 			"returning, callback id = %d",
    614 					*((anonid_t *)res->cookie.n_bytes));
    615 		}
    616 		return (res);
    617 	}
    618 }
    619 
    620 
    621 /*
    622  * __nis_alt_callback_server()
    623  *
    624  * Return a pointer to a new chunk of memory containing a copy of the
    625  * org_endpoint argument, with the rpc_origin address substituted (merged,
    626  * since the port number is retained) for the first endpoint whose family
    627  * matches an NC_TPI_COTS netconfig entry.
    628  *
    629  * If (*uaddr != 0), it is assumed to point to a piece of memory of length
    630  * uaddrlen, where the caller wants a copy of the uaddr.
    631  *
    632  * It is the responsibility of the caller to free() the returned endpoint
    633  * structure, as well as the uaddr if (*uaddr == 0).
    634  */
    635 endpoint *
    636 __nis_alt_callback_server(endpoint	*org_endpoint,
    637 			uint_t		count,
    638 			struct netbuf	*rpc_origin,
    639 			char		**uaddr,
    640 			int		uaddrlen)
    641 {
    642 	endpoint		*alt_endpoint;
    643 	uint_t			size, i, j, ei;
    644 	struct netconfig	*nc, *match = 0;
    645 	struct netbuf		*taddr;
    646 	void			*newaddr;
    647 	void			*rpcaddr;
    648 	int			addrlen;
    649 	sa_family_t		af, rpcaf;
    650 	char			*tmpuaddr;
    651 
    652 	/* Sanity check arguments */
    653 	if (org_endpoint == 0 || count == 0 || rpc_origin == 0) {
    654 		return (0);
    655 	}
    656 
    657 	/* We only know how to deal with IP */
    658 	rpcaf = ((struct sockaddr *)rpc_origin->buf)->sa_family;
    659 	if (rpcaf != AF_INET && rpcaf != AF_INET6) {
    660 		return (0);
    661 	}
    662 
    663 	/* Retrieve a netconfig entry for "inet" that matches an endpoint */
    664 	for (i = 0; i < count; i++) {
    665 		if ((nc = __nis_get_netconfig(&org_endpoint[i])) != 0 &&
    666 		    nc->nc_semantics == NC_TPI_COTS_ORD) {
    667 			if (strcasecmp(nc->nc_protofmly, NC_INET) == 0 &&
    668 			    rpcaf == AF_INET) {
    669 				af = AF_INET;
    670 			} else if (strcasecmp(
    671 			    nc->nc_protofmly, NC_INET6) == 0 &&
    672 			    rpcaf == AF_INET6) {
    673 				af = AF_INET6;
    674 			} else {
    675 				continue;
    676 			}
    677 			match = nc;
    678 			ei = i;
    679 			break;
    680 		}
    681 	}
    682 
    683 	/* Did we find one ? */
    684 	if (match == 0) {
    685 		return (0);
    686 	}
    687 
    688 	/*
    689 	 * Allocate memory for the alternate endpoint array.
    690 	 * Must be freed by caller, if we return successfully.
    691 	 */
    692 	size = count * sizeof (endpoint);
    693 	if ((alt_endpoint = malloc(size)) == 0) {
    694 		return (0);
    695 	}
    696 
    697 	for (j = 0; j < count; j++) {
    698 		alt_endpoint[j] = org_endpoint[j];
    699 	}
    700 
    701 	/* Get the address suggested by the remote caller */
    702 	if ((taddr = uaddr2taddr(match, alt_endpoint[ei].uaddr)) == 0) {
    703 		free(alt_endpoint);
    704 		return (0);
    705 	}
    706 
    707 	/*
    708 	 * The ugly part: merge port number and RPC source address.
    709 	 * This requires knowledge about the internals of a netbuf.
    710 	 */
    711 
    712 	if (af == AF_INET) {
    713 		struct in_addr dummy;
    714 		newaddr =
    715 		    &((struct sockaddr_in *)taddr->buf)->sin_addr.s_addr;
    716 		rpcaddr =
    717 		    &((struct sockaddr_in *)rpc_origin->buf)->sin_addr.s_addr;
    718 		addrlen = sizeof (dummy.s_addr);
    719 	} else {
    720 		struct in6_addr dummy;
    721 		newaddr =
    722 		    &((struct sockaddr_in6 *)taddr->buf)->sin6_addr.s6_addr;
    723 		rpcaddr = &((struct sockaddr_in6 *)
    724 		    rpc_origin->buf)->sin6_addr.s6_addr;
    725 		addrlen = sizeof (dummy.s6_addr);
    726 	}
    727 
    728 	/*
    729 	 * If addresses are the same, there is no need to try an alternate
    730 	 * address, so just pretend that we failed.
    731 	 */
    732 	if (!memcmp(newaddr, rpcaddr, addrlen)) {
    733 		free(alt_endpoint);
    734 		netdir_free(taddr, ND_ADDR);
    735 		return (0);
    736 	}
    737 
    738 	/* Alternate address differs; worth trying */
    739 	memcpy(newaddr, rpcaddr, addrlen);
    740 	if ((tmpuaddr = taddr2uaddr(match, taddr)) == 0) {
    741 		free(alt_endpoint);
    742 		netdir_free(taddr, ND_ADDR);
    743 		return (0);
    744 	}
    745 
    746 	if (*uaddr == 0) {
    747 		*uaddr = tmpuaddr;
    748 	} else if (strlen(tmpuaddr) < uaddrlen) {
    749 		(void) strcpy(*uaddr, tmpuaddr);
    750 		free(tmpuaddr);
    751 	} else {
    752 		free(alt_endpoint);
    753 		netdir_free(taddr, ND_ADDR);
    754 		free(tmpuaddr);
    755 		return (0);
    756 	}
    757 
    758 	/* Clean up */
    759 	netdir_free(taddr, ND_ADDR);
    760 
    761 	alt_endpoint[ei].uaddr = *uaddr;
    762 
    763 	return (alt_endpoint);
    764 }
    765 
    766 /*
    767  * nis_iblist, this function will take the search criteria passed,
    768  * and pass it on to the database. If the callback structure is
    769  * null then the results are simply returned, otherwise the server
    770  * forks and and begins returning results one by one. In a multithreaded
    771  * environment this is handled by a thread. The library will check
    772  * a resource limit variable before forking.
    773  */
    774 nis_result *
    775 nis_iblist_svc(argp, reqstp)
    776 	ib_request *argp;
    777 	struct svc_req *reqstp;
    778 {
    779 	nis_result	*res = NULL;
    780 	nis_object	*ib_obj,
    781 			*list;
    782 	int		got;
    783 	char		*s;
    784 	int		all_read;	/* all readable flag		*/
    785 	char		pname[1024];
    786 	nis_db_list_result	*ib_list;
    787 	int		zn;
    788 	nis_attr	*za;
    789 
    790 	__start_clock(0);
    791 	if (verbose)
    792 		syslog(LOG_INFO, "LIST_SVC: Listing table %s",
    793 							argp->ibr_name);
    794 
    795 	res = (nis_result *)XCALLOC(1, sizeof (nis_result));
    796 	add_cleanup((void (*)())XFREE, (char *)res, "iblist result");
    797 
    798 	/* If reqstp == NULL then we're recursing */
    799 	if (reqstp)
    800 		nis_getprincipal(pname, reqstp);
    801 	else
    802 		pname[0] = '\0';
    803 
    804 	ib_obj = __nis_listinit(res, argp, pname);
    805 
    806 	if (res->status != NIS_SUCCESS) {
    807 		res->zticks = __stop_clock(0);
    808 		return (res);
    809 	}
    810 	/* If we have read access on the table, we can read all of it */
    811 	all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, pname);
    812 
    813 	/*
    814 	 * Now we actually do the list operation. If there is a callback,
    815 	 * then we use the first/next operations to send the objects back
    816 	 * one at a time.
    817 	 */
    818 	if (argp->ibr_cbhost.ibr_cbhost_len) {
    819 		/* Process the callback request. */
    820 		/*
    821 		 * The client supplied an address for the callback service.
    822 		 * However, we may not be able to reach that address, so
    823 		 * try the source address of the RPC request first.
    824 		 *
    825 		 * Note: The alt_endpoint array (which usually contains just
    826 		 * a single element) is a copy of of org_endpoint, including
    827 		 * pointers, except for the uaddr of one element. This uaddr
    828 		 * contains a merged version of the RPC source address and the
    829 		 * port number specified by the client in the callback data.
    830 		 */
    831 		{
    832 			struct netbuf	*rpc_origin;
    833 			endpoint	*org_endpoint, *alt_endpoint;
    834 			char		*uaddr = 0;
    835 
    836 			org_endpoint =
    837 			    argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val;
    838 			rpc_origin = svc_getrpccaller(reqstp->rq_xprt);
    839 			if ((alt_endpoint = __nis_alt_callback_server(
    840 				org_endpoint,
    841 				argp->ibr_cbhost.ibr_cbhost_val->ep.ep_len,
    842 				rpc_origin,
    843 				&uaddr, 0)) != 0) {
    844 				argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val =
    845 				    alt_endpoint;
    846 				res = nis_iblist_callback(argp, pname, ib_obj,
    847 							all_read, res);
    848 				argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val =
    849 				    org_endpoint;
    850 				free(alt_endpoint);
    851 				free(uaddr);
    852 				if (res->status != NIS_NOCALLBACK) {
    853 					return (res);
    854 				}
    855 			}
    856 		}
    857 		return (nis_iblist_callback(argp, pname, ib_obj,
    858 					    all_read, res));
    859 	}
    860 	/*
    861 	 * If the client didn't ask for a callback, we process the
    862 	 * entire list at once and return it. (implicit else clause)
    863 	 */
    864 	ib_list = db_list(argp->ibr_name, argp->ibr_srch.ibr_srch_len,
    865 				argp->ibr_srch.ibr_srch_val);
    866 
    867 	res->dticks += ib_list->ticks;
    868 	if (ib_list->status != NIS_SUCCESS) {
    869 		if (ib_list->status == NIS_NOTFOUND) {
    870 			res->status = NIS_PARTIAL;
    871 			NIS_RES_OBJECT(res) = ib_obj;
    872 			NIS_RES_NUMOBJ(res) = 1;
    873 		} else {
    874 			if (__type_of(ib_obj) == NIS_TABLE_OBJ) {
    875 				s = "table";
    876 			} else
    877 				s = "directory";
    878 			syslog(LOG_ERR, "Unable to list %s '%s' err=%d",
    879 				    s, argp->ibr_name, ib_list->status);
    880 			res->status = ib_list->status;
    881 			NIS_RES_OBJECT(res) = NULL;
    882 			NIS_RES_NUMOBJ(res) = 0;
    883 		}
    884 		res->zticks = __stop_clock(0);
    885 		if (verbose)
    886 			syslog(LOG_INFO, "nis_iblist_svc: return status %s",
    887 						    nis_sperrno(res->status));
    888 		return (res);
    889 	}
    890 	/* Now process the list of objects we've got to return */
    891 	zn = argp->ibr_srch.ibr_srch_len;
    892 	za = argp->ibr_srch.ibr_srch_val;
    893 	list = nis_return_list(ib_obj, ib_list->objs, ib_list->numo, pname,
    894 							&got, all_read,
    895 							zn, za);
    896 
    897 	/* Construct the result */
    898 	if (got) {
    899 		res->status = NIS_SUCCESS;
    900 		NIS_RES_NUMOBJ(res) = got;
    901 		NIS_RES_OBJECT(res) = list;
    902 	} else {
    903 		res->status = NIS_NOTFOUND;
    904 		NIS_RES_NUMOBJ(res) = 0;
    905 		NIS_RES_OBJECT(res) = NULL;
    906 	}
    907 	res->zticks = __stop_clock(0);
    908 	return (res);
    909 }
    910 
    911 /*
    912  * This is an internal Information Base operations function. It is collected
    913  * here for clarity. It makes the actual database calls to manipulate the
    914  * namespace.
    915  */
    916 static void
    917 add_entry(name, zn, za, obj, tbl, flags, princp, xid, res)
    918 	nis_name		name;	/* Table name		    */
    919 	int			zn;	/* Number of attributes	    */
    920 	nis_attr		*za;	/* NIS+ attribute list.	    */
    921 	nis_object		*obj,	/* The entry we're adding   */
    922 				*tbl;	/* Table we're adding to    */
    923 	ulong_t			flags;	/* Semantic flags	    */
    924 	nis_name		princp;	/* Principal making request */
    925 	int			*xid;	/* XID for transaction	    */
    926 	nis_result		*res;	/* Place for results.	    */
    927 {
    928 	nis_db_list_result 	*db;
    929 	nis_db_result		*ars;
    930 	int			add_ok, mod_ok;
    931 	time_t			currtime;
    932 
    933 	/*
    934 	 * Check to see if we can actually create an entry in this
    935 	 * table.
    936 	 */
    937 	add_ok = __can_do(NIS_CREATE_ACC, tbl->zo_access, tbl, princp);
    938 	mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp);
    939 	if (auth_verbose) {
    940 		syslog(LOG_INFO,
    941 			"add_entry: creation is %s by the table.",
    942 				(add_ok) ? "ALLOWED" : "DISALLOWED");
    943 
    944 	}
    945 
    946 	db = db_list(name, zn, za);
    947 
    948 	*xid = begin_transaction(princp);
    949 	if (*xid == 0) {
    950 		res->status = NIS_TRYAGAIN;
    951 		return;
    952 	}
    953 
    954 	currtime = time(0);	/* Make sure REMOVE/ADD has same time */
    955 	obj->zo_oid.ctime = (ulong_t)currtime;
    956 	obj->zo_oid.mtime = obj->zo_oid.ctime;
    957 	if (db->status == NIS_SUCCESS) {
    958 		if (db->numo != 1) {
    959 			res->status = NIS_NOTUNIQUE;
    960 		} else if ((flags & ADD_OVERWRITE) == 0) {
    961 			res->status = NIS_NAMEEXISTS;
    962 		} else if (! mod_ok &&
    963 			    ! __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access,
    964 				    db->objs[0].o, princp)) {
    965 			res->status = NIS_PERMISSION;
    966 		} else {
    967 			nisdb_tsd_t	*tsd;
    968 
    969 			/* Act like a modify on the OID */
    970 			obj->zo_oid.ctime = db->objs->o->zo_oid.ctime;
    971 
    972 			/*
    973 			 * Tell the DB that we're doing a modify, so that
    974 			 * it can merge the remove/add to a single LDAP
    975 			 * update.
    976 			 */
    977 			tsd = __nisdb_get_tsd();
    978 			tsd->doingModify = 1;
    979 
    980 			/* This updates the log */
    981 			db_remib(name, zn, za, db->objs, db->numo, tbl,
    982 							(ulong_t)currtime);
    983 			/* As does this, a virtual modify operation */
    984 			ars = db_addib(name, zn, za, obj, tbl);
    985 
    986 			tsd->doingModify = 0;
    987 
    988 			res->dticks += ars->ticks;
    989 			res->status = ars->status;
    990 		}
    991 	} else if (db->status == NIS_NOTFOUND) {
    992 		/* Ok, it doesn't exist so lets add it to the table */
    993 		if (! add_ok) {
    994 			res->status = NIS_PERMISSION;
    995 			return;
    996 		}
    997 
    998 		ars = db_addib(name, zn, za, obj, tbl);
    999 		if ((ars->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) {
   1000 			res->objects.objects_val = nis_clone_object(obj, NULL);
   1001 			res->objects.objects_len = 1;
   1002 		}
   1003 		res->dticks += ars->ticks;
   1004 		res->status = ars->status;
   1005 	} else
   1006 		res->status = db->status;
   1007 }
   1008 
   1009 /*
   1010  * This is an internal Information Base operations function that implements
   1011  * the remove operation. It is collected here for clarity.
   1012  */
   1013 static void
   1014 remove_entry(name, zn, za, obj, tbl, flags, princp, xid, res)
   1015 	nis_name		name;	/* Table name		    */
   1016 	int			zn;	/* Number of attributes	    */
   1017 	nis_attr		*za;	/* NIS attribute list.	    */
   1018 	nis_object		*obj,	/* The entry we're removing */
   1019 				*tbl;	/* Table we're adding to    */
   1020 	ulong_t			flags;	/* Semantic flags	    */
   1021 	nis_name		princp;	/* Principal making request */
   1022 	int			*xid;	/* XID for transaction	    */
   1023 	nis_result		*res;	/* Place for results.	    */
   1024 {
   1025 	nis_db_list_result 	*db;
   1026 	nis_db_result		*rrs;
   1027 	int			i, rem_ok;
   1028 
   1029 	/*
   1030 	 * Access control, if we have remove access granted at the
   1031 	 * table level then we can remove all entries.
   1032 	 */
   1033 	rem_ok =  __can_do(NIS_DESTROY_ACC, tbl->zo_access, tbl, princp);
   1034 	if (auth_verbose) {
   1035 		syslog(LOG_INFO,
   1036 			"remove_entry: removal is %s by the table.",
   1037 				(rem_ok) ? "ALLOWED" : "DISALLOWED");
   1038 
   1039 	}
   1040 
   1041 
   1042 	db = db_list(name, zn, za);
   1043 	res->dticks += db->ticks;
   1044 
   1045 	/*
   1046 	 * Check for errors, first to see if we actually found an
   1047 	 * object to remove.
   1048 	 */
   1049 	if (db->status != NIS_SUCCESS) {
   1050 		res->status = db->status;
   1051 		return;
   1052 	}
   1053 
   1054 	/*
   1055 	 * next to see if more than one object may be removed.
   1056 	 */
   1057 	if ((db->numo > 1) && ((flags & REM_MULTIPLE) == 0)) {
   1058 		res->status = NIS_NOTUNIQUE;
   1059 		return;
   1060 	}
   1061 
   1062 	/*
   1063 	 * Third to see if we need the same object to remove
   1064 	 */
   1065 	if (obj && (! same_oid(obj, db->objs->o))) {
   1066 		res->status = NIS_NOTSAMEOBJ;
   1067 		return;
   1068 	}
   1069 
   1070 	/*
   1071 	 * Fourth check to see if we have the right to remove
   1072 	 * these entrie(s)
   1073 	 */
   1074 	if (! rem_ok) {
   1075 		for (i = 0; i < db->numo; i++)
   1076 			if (! __can_do(NIS_DESTROY_ACC,
   1077 					db->objs[i].o->zo_access,
   1078 					db->objs[i].o, princp))
   1079 				break;
   1080 		if (i < db->numo) {
   1081 			res->status = NIS_PERMISSION;
   1082 			return;
   1083 		}
   1084 	}
   1085 
   1086 	*xid = begin_transaction(princp);
   1087 	if (*xid == 0) {
   1088 		res->status = NIS_TRYAGAIN;
   1089 		return;
   1090 	}
   1091 
   1092 	/*
   1093 	 * Finally, remove the object in question.
   1094 	 */
   1095 	rrs = db_remib(name, zn, za, db->objs, db->numo, tbl,
   1096 							(ulong_t)(time(0)));
   1097 	res->dticks += rrs->ticks;
   1098 	res->status = rrs->status;
   1099 }
   1100 
   1101 /*
   1102  * This is the internal Information base function that implements the modify
   1103  * operation. It is put here for clarity.
   1104  */
   1105 static void
   1106 modify_entry(name, zn, za, obj, tbl, flags, princp, xid, res)
   1107 	nis_name		name;	/* Table name		    */
   1108 	int			zn;	/* Number of attributes	    */
   1109 	nis_attr		*za;	/* NIS attribute list.	    */
   1110 	nis_object		*obj,	/* The entry we're removing */
   1111 				*tbl;	/* Table we're adding to    */
   1112 	ulong_t			flags;	/* Semantic flags	    */
   1113 	nis_name		princp;	/* Principal making request */
   1114 	int			*xid;	/* XID for transaction	    */
   1115 	nis_result		*res;	/* Place for results.	    */
   1116 {
   1117 #define	buf			__nis_get_tsd()->modify_entry_buf
   1118 	nis_db_list_result 	*db;
   1119 	nis_db_result		*rrs;
   1120 	int			i, mc;
   1121 	entry_col		*o_ec,	/* Old entry columns */
   1122 				*n_ec;	/* New entry columns */
   1123 	nis_object		mobj; 	/* Modified object ...	*/
   1124 	int			clone;	/* True if new object is a clone */
   1125 	entry_col		*mec;	/* ... and it's columns	*/
   1126 	int			mod_ok;
   1127 	table_col		*tcol;
   1128 	nis_db_list_result	*o_db;
   1129 	nis_attr		*test_attr;
   1130 	int			na, mod_attrs = 0;
   1131 	time_t			currtime;
   1132 	nisdb_tsd_t		*tsd;
   1133 
   1134 	/*
   1135 	 * Set the global modify access bit for the objects by checking
   1136 	 * the table's access rights.
   1137 	 */
   1138 	mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp);
   1139 
   1140 	if (auth_verbose) {
   1141 		syslog(LOG_INFO,
   1142 			"modify_entry: modification is %s by the table.",
   1143 				(mod_ok) ? "ALLOWED" : "DISALLOWED");
   1144 
   1145 	}
   1146 
   1147 	mc = tbl->TA_data.ta_maxcol;
   1148 	tcol = tbl->TA_data.ta_cols.ta_cols_val;
   1149 	mec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc);
   1150 	if (! mec) {
   1151 		res->status = NIS_NOMEMORY;
   1152 		return;
   1153 	}
   1154 
   1155 	db = db_list(name, zn, za);
   1156 
   1157 	/* If we didn't find the original just return the error */
   1158 	if (db->status != NIS_SUCCESS) {
   1159 		res->status = db->status;
   1160 		return;
   1161 	}
   1162 
   1163 	/* If the object isn't unique we can't modify it. */
   1164 	if (db->numo > 1) {
   1165 		res->status = NIS_NOTUNIQUE;
   1166 		return;
   1167 	}
   1168 
   1169 	clone = same_oid(obj, db->objs->o);
   1170 
   1171 	if ((flags & MOD_SAMEOBJ) && (! clone)) {
   1172 		res->status = NIS_NOTSAMEOBJ;
   1173 		return;
   1174 	}
   1175 
   1176 	/*
   1177 	 * Now check the permissions on the object itself.
   1178 	 * If we don't have modify access to the table we check
   1179 	 * to see if we have modify access  to the object itself,
   1180 	 * if that fails we check to see if we have modify access
   1181 	 * to the column we are modifying. If that fails we return
   1182 	 * a permission error.
   1183 	 */
   1184 	o_ec = db->objs->o->EN_data.en_cols.en_cols_val;
   1185 	n_ec = obj->EN_data.en_cols.en_cols_val;
   1186 	if (! mod_ok)
   1187 		mod_ok = __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access,
   1188 					db->objs[0].o, princp);
   1189 	if (! mod_ok) {
   1190 		for (i = 0; i < mc; i++)
   1191 			if (((n_ec[i].ec_flags & EN_MODIFIED) != 0) &&
   1192 				! __can_do(NIS_MODIFY_ACC,
   1193 						tcol[i].tc_rights,
   1194 						db->objs[0].o, princp)) {
   1195 				res->status = NIS_PERMISSION;
   1196 				return;
   1197 			}
   1198 	}
   1199 
   1200 	/*
   1201 	 * Check the MOD_EXCLUSIVE flag. If a searchable column has been
   1202 	 * modified, the key index will be changed, which may conflict with
   1203 	 * an existing entry.
   1204 	 */
   1205 
   1206 	if (flags & MOD_EXCLUSIVE) {
   1207 		/* Check for a searchable column that has been modified */
   1208 		for (i = 0; i < mc; i++) {
   1209 			if (mod_attrs = ((tcol[i].tc_flags & TA_SEARCHABLE) &&
   1210 				(n_ec[i].ec_flags & EN_MODIFIED))) {
   1211 				break;
   1212 			}
   1213 		}
   1214 		/*
   1215 		 * If key modified, then create new attr list and see if
   1216 		 * it matches an existing entry
   1217 		 */
   1218 		if (mod_attrs) {
   1219 			test_attr = __get_attrs(mc);
   1220 			if (test_attr == NULL) {
   1221 				res->status = NIS_NOMEMORY;
   1222 				return;
   1223 			}
   1224 			for (i = 0, na = 0; i < mc; i++) {
   1225 				if (tcol[i].tc_flags & TA_SEARCHABLE) {
   1226 					test_attr[na].zattr_ndx =
   1227 								tcol[i].tc_name;
   1228 					if ((n_ec[i].ec_flags & EN_MODIFIED) ==
   1229 					    0) {
   1230 					test_attr[na].ZAVAL = o_ec[i].ENVAL;
   1231 					test_attr[na].ZALEN = o_ec[i].ENLEN;
   1232 					} else {
   1233 					test_attr[na].ZAVAL = n_ec[i].ENVAL;
   1234 					test_attr[na].ZALEN = n_ec[i].ENLEN;
   1235 					}
   1236 					na++;
   1237 				}
   1238 			}
   1239 			o_db = db_list(name, na, test_attr);
   1240 			if (o_db->status == NIS_SUCCESS) {
   1241 				res->status = NIS_NAMEEXISTS;
   1242 				return;
   1243 			}
   1244 		}
   1245 	}
   1246 
   1247 	mobj = *(db->objs->o);
   1248 	currtime = time(0);	/* Make sure REMOVE/ADD has same time */
   1249 	mobj.zo_oid.mtime = (ulong_t)currtime;
   1250 
   1251 	/* Now check to see if the attributes need to change */
   1252 	/*
   1253 	 * XXX this can fail silently when attributes change
   1254 	 * but mod_ok isn't set, we should figure out how to
   1255 	 * return a permission error. XXX
   1256 	 */
   1257 	if (clone && mod_ok) {
   1258 		mobj.zo_owner = obj->zo_owner;
   1259 		mobj.zo_group = obj->zo_group;
   1260 		mobj.zo_access = obj->zo_access;
   1261 		mobj.zo_ttl = obj->zo_ttl;
   1262 	}
   1263 	mobj.EN_data.en_cols.en_cols_val = mec;
   1264 
   1265 	for (i = 0; i < mc; i++) {
   1266 		if ((n_ec[i].ec_flags & EN_MODIFIED) == 0)
   1267 			mec[i] = o_ec[i];
   1268 		else
   1269 			mec[i] = n_ec[i];
   1270 	}
   1271 
   1272 	*xid = begin_transaction(princp);
   1273 	if (*xid == 0) {
   1274 		res->status = NIS_TRYAGAIN;
   1275 		return;
   1276 	}
   1277 
   1278 	/*
   1279 	 * If changes are written through to LDAP, we want to make
   1280 	 * just one LDAP update, not one remove and one add, which
   1281 	 * causes problems when more than one NIS+ table maps to
   1282 	 * one and the same LDAP container. Hence, inform the DB
   1283 	 * that this removal is part of a modify operation.
   1284 	 */
   1285 	tsd = __nisdb_get_tsd();	/* Never returns NULL */
   1286 	tsd->doingModify = 1;
   1287 
   1288 	/* Remove the old version of the entry */
   1289 	rrs = db_remib(name, zn, za, db->objs, db->numo, tbl,
   1290 						(ulong_t)currtime);
   1291 	res->dticks += rrs->ticks;
   1292 
   1293 	if (res->status != NIS_SUCCESS)
   1294 		syslog(LOG_ERR, "modify_entry: Unable to remove object %s",
   1295 							name);
   1296 	/*
   1297 	 * Now do the modify by overwriting the existing
   1298 	 * object.
   1299 	 */
   1300 	rrs = db_addib(name, zn, za, &mobj, tbl);
   1301 	if ((rrs->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) {
   1302 		res->objects.objects_val = nis_clone_object(&mobj, NULL);
   1303 		res->objects.objects_len = 1;
   1304 	}
   1305 
   1306 	/* Reset modification indication to the DB */
   1307 	tsd->doingModify = 0;
   1308 
   1309 	res->dticks += rrs->ticks;
   1310 	res->status = rrs->status;
   1311 }
   1312 
   1313 #undef	buf
   1314 
   1315 /*
   1316  * __nis_ibops()
   1317  * This is the common information base ops routine. It implements the
   1318  * various operations that can be done on an information base. It is
   1319  * analagous to the nameops() routine above.
   1320  */
   1321 static nis_result *
   1322 __nis_ibops(op, argp, princp)
   1323 	int		op;
   1324 	ib_request	*argp;
   1325 	nis_name	princp;
   1326 {
   1327 	nis_result 	*res;
   1328 #define	buf		__nis_get_tsd()->__ibops_buf
   1329 	nis_object	*t_obj, *n_obj, *d_obj;
   1330 	nis_attr	*za;
   1331 	int		zn;
   1332 	table_col	*tc;
   1333 	ulong_t		ttime;
   1334 	int		i, mc, xid;
   1335 	entry_col	*ec;
   1336 	nis_db_result	*table;
   1337 	struct ticks	t;
   1338 	char		optxt[32];
   1339 	nis_error	xx;
   1340 
   1341 	res = (nis_result *)XCALLOC(1, sizeof (nis_result));
   1342 	add_xdr_cleanup(xdr_nis_result, (char *)res, "ibops result");
   1343 
   1344 	if (readonly) {
   1345 		res->status = NIS_TRYAGAIN;
   1346 		return (res);
   1347 	}
   1348 
   1349 	if (auth_verbose) {
   1350 		switch (op) {
   1351 			case ADD_OP :
   1352 				strcpy(optxt, "ADD");
   1353 				break;
   1354 			case REM_OP :
   1355 				strcpy(optxt, "REMOVE");
   1356 				break;
   1357 			case MOD_OP :
   1358 				strcpy(optxt, "MODIFY");
   1359 				break;
   1360 		}
   1361 		syslog(LOG_INFO, "Entry operation '%s' for principal %s",
   1362 				optxt, princp);
   1363 	}
   1364 
   1365 	/*
   1366 	 * Check to see if we serve the directory this table is in.
   1367 	 * NOTE: this could do a NIS+ lookup to the parent.
   1368 	 */
   1369 	xx = __directory_object(nis_domain_of(argp->ibr_name), &t, TRUE,
   1370 								&d_obj);
   1371 	res->aticks = t.aticks;
   1372 	res->zticks = t.zticks;
   1373 	res->cticks = t.cticks;
   1374 	res->dticks = t.dticks;
   1375 	if (d_obj == NULL) {
   1376 		syslog(LOG_ERR, "Couldn't locate directory object for %s",
   1377 				argp->ibr_name);
   1378 		res->status = NIS_NOT_ME;  /* XXX should we use 'xx'? */
   1379 		return (res);
   1380 	}
   1381 
   1382 	/*
   1383 	 * Get the last update time. If the current time is earlier
   1384 	 * someone has set the clock back which is very bad as far
   1385 	 * as NIS+ is concerned. This will catch almost all possible
   1386 	 * situations except the single second when time has finally
   1387 	 * caught up with the last entry in the log. Fixing this
   1388 	 * would require more radical changes to the whole update
   1389 	 * mechanism which is potentially risky and may even require
   1390 	 * protocol changes.
   1391 	 */
   1392 	if (last_update(d_obj->DI_data.do_name) > time(0)) {
   1393 		syslog(LOG_ERR, "Update rejected because system time is "
   1394 		    "earlier than most recent log entry");
   1395 		res->status = NIS_SYSTEMERROR;
   1396 		return (res);
   1397 	}
   1398 
   1399 	/*
   1400 	 * POLICY : Should we have to read the directory to
   1401 	 *	    read the table ?
   1402 	 * ANSWER : No, if we _know_ the name of the table we
   1403 	 *	    should be able to access it.
   1404 	 */
   1405 
   1406 	/* quick pointer to the "new" object */
   1407 	if (argp->ibr_obj.ibr_obj_len)
   1408 		n_obj = argp->ibr_obj.ibr_obj_val;
   1409 	else
   1410 		n_obj = NULL;
   1411 
   1412 	/* Get the table that we will be manipulating. */
   1413 	table = db_lookup(argp->ibr_name);
   1414 	res->dticks += table->ticks;
   1415 	if (table->status != NIS_SUCCESS) {
   1416 		res->status = table->status;
   1417 		return (res);
   1418 	}
   1419 
   1420 	t_obj = table->obj;
   1421 	if (__type_of(t_obj) != NIS_TABLE_OBJ) {
   1422 		res->status = NIS_INVALIDOBJ;
   1423 		return (res);
   1424 	}
   1425 
   1426 	if (! __search_ok(argp->ibr_srch.ibr_srch_len,
   1427 				argp->ibr_srch.ibr_srch_val, t_obj)) {
   1428 		res->status = NIS_BADATTRIBUTE;
   1429 		return (res);
   1430 	}
   1431 
   1432 	/* For ADD/MODify Make sure the types are the same */
   1433 	if ((op == MOD_OP) || (op == ADD_OP)) {
   1434 		if (! n_obj) {
   1435 			res->status = NIS_BADOBJECT;
   1436 			return (res);
   1437 		}
   1438 	}
   1439 
   1440 	/*
   1441 	 *  For all operations, if there is an object, check that
   1442 	 *  it matches the table.
   1443 	 */
   1444 	if (n_obj) {
   1445 		if (strcasecmp(n_obj->EN_data.en_type,
   1446 					t_obj->TA_data.ta_type) != 0) {
   1447 			res->status = NIS_TYPEMISMATCH;
   1448 			return (res);
   1449 		}
   1450 
   1451 		/* Make sure the number of columns are the same */
   1452 		if (n_obj->EN_data.en_cols.en_cols_len !=
   1453 		    t_obj->TA_data.ta_maxcol) {
   1454 			res->status = NIS_TYPEMISMATCH;
   1455 			return (res);
   1456 		}
   1457 	}
   1458 
   1459 	/*
   1460 	 * Now we check the attribute list, doing some sanity checking
   1461 	 * and then go to the database.
   1462 	 */
   1463 
   1464 	tc = t_obj->TA_data.ta_cols.ta_cols_val; /* The Table columns */
   1465 	mc = t_obj->TA_data.ta_maxcol;		/* The total columns */
   1466 	if (n_obj)				/* The Entry columns */
   1467 		ec = n_obj->EN_data.en_cols.en_cols_val;
   1468 	else
   1469 		ec = NULL;
   1470 
   1471 	if (argp->ibr_srch.ibr_srch_len) {
   1472 		za = argp->ibr_srch.ibr_srch_val;	/* The AVA list */
   1473 		zn = argp->ibr_srch.ibr_srch_len;	/* The current AVA */
   1474 	} else if (ec) {
   1475 		/* build a search criteria from the passed entry */
   1476 		za = (nis_attr *)nis_get_static_storage(&buf, sizeof (nis_attr),
   1477 									    mc);
   1478 		for (i = 0, zn = 0; i < mc; i++) {
   1479 			if ((tc[i].tc_flags & TA_SEARCHABLE) &&
   1480 			    (ec[i].ec_value.ec_value_val)) {
   1481 				za[zn].zattr_ndx = tc[i].tc_name;
   1482 				za[zn].ZAVAL = ec[i].ENVAL;
   1483 				za[zn].ZALEN = ec[i].ENLEN;
   1484 				zn++;
   1485 			}
   1486 		}
   1487 	} else {
   1488 		za = NULL;
   1489 		zn = 0;
   1490 	}
   1491 
   1492 	xid = 0;
   1493 
   1494 	switch (op) {
   1495 
   1496 		case ADD_OP :
   1497 			add_entry(argp->ibr_name, zn, za, n_obj, t_obj,
   1498 						argp->ibr_flags, princp, &xid,
   1499 						res);
   1500 			break;
   1501 		case REM_OP :
   1502 			remove_entry(argp->ibr_name, zn, za, n_obj, t_obj,
   1503 						argp->ibr_flags, princp, &xid,
   1504 						res);
   1505 			break;
   1506 		case MOD_OP :
   1507 			modify_entry(argp->ibr_name, zn, za, n_obj, t_obj,
   1508 						argp->ibr_flags, princp, &xid,
   1509 						res);
   1510 			break;
   1511 	}
   1512 
   1513 	if (verbose)
   1514 		syslog(LOG_INFO, "ibops: operation completed.");
   1515 	if (res->status == NIS_SUCCESS) {
   1516 		end_transaction(xid);
   1517 		ttime = last_update(d_obj->DI_data.do_name);
   1518 		if (d_obj->DI_data.do_servers.do_servers_len > 1) {
   1519 			RLOCK(ping_list);
   1520 			add_pingitem(d_obj, ttime, &ping_list);
   1521 			RULOCK(ping_list);
   1522 		}
   1523 	} else if (xid != 0)
   1524 		abort_transaction(xid);
   1525 
   1526 	if (verbose)
   1527 		syslog(LOG_INFO, "ibops: returning...");
   1528 	return (res);
   1529 }
   1530 
   1531 #undef	buf
   1532 
   1533 /*
   1534  * nis_fnops()
   1535  * This is routine is common to the first and next functions below. Quite
   1536  * a bit of code is shared so this cuts down on the chance for errors.
   1537  * The first part is identical to the ibops function above but this
   1538  * routine needs to return different data so I didn't want to overload
   1539  * the return value of ibobs() above.
   1540  */
   1541 static nis_result *
   1542 __nis_fnops(op, argp, princp)
   1543 	int		op;
   1544 	ib_request	*argp;
   1545 	nis_name	princp;
   1546 {
   1547 	nis_object		*r_obj, *t_obj, *ib_obj = NULL;
   1548 	nis_fn_result		*fnr;
   1549 	table_col		*tc;
   1550 	int			all_read = 0;
   1551 	nis_result		*res;
   1552 	int			zn;
   1553 	nis_attr		*za;
   1554 
   1555 	res = (nis_result *)XCALLOC(1, sizeof (nis_result));
   1556 	if (! res) {
   1557 		syslog(LOG_ERR, "rpc.nisd: Out of memory");
   1558 		return (NULL);
   1559 	}
   1560 	add_cleanup((void (*)())XFREE, (char *)res, "fnops result");
   1561 
   1562 	ib_obj = __nis_listinit(res, argp, princp);
   1563 
   1564 	if (! ib_obj) {
   1565 		res->zticks = __stop_clock(0);
   1566 		return (res);
   1567 	}
   1568 
   1569 	all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, princp);
   1570 	if (op == FIRST_OP)
   1571 		fnr = db_firstib(argp->ibr_name, argp->ibr_srch.ibr_srch_len,
   1572 			argp->ibr_srch.ibr_srch_val, FN_MANGLE+FN_NORAGS, NULL);
   1573 	else if (op == NEXT_OP) {
   1574 		res->cookie.n_len   = argp->ibr_cookie.n_len;
   1575 		res->cookie.n_bytes = (char *)malloc(res->cookie.n_len);
   1576 		memcpy(res->cookie.n_bytes,
   1577 			argp->ibr_cookie.n_bytes, res->cookie.n_len);
   1578 		fnr = db_nextib(argp->ibr_name, &(res->cookie),
   1579 						FN_MANGLE+FN_NORAGS, NULL);
   1580 	}
   1581 
   1582 	res->dticks += fnr->ticks;
   1583 	if (fnr->status != NIS_SUCCESS) {
   1584 		res->status = fnr->status;
   1585 		XFREE(fnr);
   1586 		return (res);
   1587 	}
   1588 
   1589 	res->cookie = fnr->cookie;
   1590 	res->status = fnr->status;
   1591 	/*
   1592 	 * If we have read access to everything or the object then we're done.
   1593 	 */
   1594 	if (all_read ||
   1595 	    __can_do(NIS_READ_ACC, fnr->obj->zo_access, fnr->obj, princp)) {
   1596 		res->objects.objects_val = fnr->obj;
   1597 		res->objects.objects_len = 1;
   1598 		add_cleanup(nis_destroy_object, (char *)fnr->obj, "fnops obj");
   1599 		add_cleanup((void (*)())XFREE, (char *)fnr->cookie.n_bytes,
   1600 							"fnops descript");
   1601 		XFREE(fnr);
   1602 		return (res);
   1603 	}
   1604 
   1605 	r_obj = fnr->obj;
   1606 	XFREE(fnr); /* free the result structure */
   1607 	if (__type_of(ib_obj) == NIS_TABLE_OBJ)
   1608 		tc = ib_obj->TA_data.ta_cols.ta_cols_val;
   1609 	else
   1610 		tc = tbl_prototype.ta_cols.ta_cols_val;
   1611 	do {
   1612 		if (__can_do(NIS_READ_ACC, r_obj->zo_access, r_obj, princp))
   1613 			t_obj = r_obj;
   1614 		else {
   1615 			zn = argp->ibr_srch.ibr_srch_len;
   1616 			za = argp->ibr_srch.ibr_srch_val;
   1617 			t_obj = nis_censor_object_attr(r_obj, tc, princp,
   1618 							zn, za);
   1619 			nis_destroy_object(r_obj);
   1620 		}
   1621 		if (t_obj) {
   1622 			res->objects.objects_val = t_obj;
   1623 			res->objects.objects_len = 1;
   1624 			add_cleanup(nis_destroy_object, (char *)t_obj,
   1625 								"fnops obj");
   1626 			add_cleanup((void (*)())XFREE,
   1627 						(char *)res->cookie.n_bytes,
   1628 							"fnops descript");
   1629 			return (res);
   1630 		}
   1631 
   1632 		fnr = db_nextib(argp->ibr_name, &(res->cookie),
   1633 						FN_MANGLE+FN_NORAGS, NULL);
   1634 		res->dticks += fnr->ticks;
   1635 		res->status = fnr->status;
   1636 		if (res->status == NIS_SUCCESS)
   1637 			res->cookie = fnr->cookie;
   1638 		r_obj = fnr->obj;
   1639 		XFREE(fnr);
   1640 	} while (res->status == NIS_SUCCESS);
   1641 
   1642 	return (res);
   1643 }
   1644 
   1645 /*
   1646  * nis_addib, this function adds an entry into an Information Base.
   1647  * the entry is not "visible" in the namespace, only as a component
   1648  * of the information base that contains it.
   1649  */
   1650 nis_result *
   1651 nis_ibadd_svc(argp, reqstp)
   1652 	ib_request *argp;
   1653 	struct svc_req *reqstp;
   1654 {
   1655 	nis_result 	*res;
   1656 	char		pname[1024];
   1657 
   1658 	__start_clock(0);
   1659 	if (verbose)
   1660 		syslog(LOG_INFO, "Entry ADD_SVC: to table %s", argp->ibr_name);
   1661 	/* A quick and easy check... */
   1662 	if ((argp->ibr_obj.ibr_obj_len == 1) &&
   1663 	    (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) {
   1664 		return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0,
   1665 							__stop_clock(0)));
   1666 	}
   1667 	nis_getprincipal(pname, reqstp);
   1668 	res = __nis_ibops(ADD_OP, argp, pname);
   1669 	if (verbose)
   1670 		syslog(LOG_INFO, "Done, exit status %s",
   1671 			nis_sperrno(res->status));
   1672 	res->zticks += __stop_clock(0);
   1673 	return (res);
   1674 }
   1675 
   1676 /*
   1677  * nis_ibmodify, this function modifys an entry in the information
   1678  * base by changing the columns that are marked as modified in the
   1679  * passed entry. Permission checking is done here as well.
   1680  */
   1681 nis_result *
   1682 nis_ibmodify_svc(argp, reqstp)
   1683 	ib_request *argp;
   1684 	struct svc_req *reqstp;
   1685 {
   1686 	char		pname[1024];
   1687 	nis_result 	*res;
   1688 
   1689 	__start_clock(0);
   1690 	if (verbose)
   1691 		syslog(LOG_INFO, "Entry MODIFY_SVC: to table %s",
   1692 							argp->ibr_name);
   1693 	/* A quick and easy check... */
   1694 	if ((argp->ibr_obj.ibr_obj_len == 1) &&
   1695 	    (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) {
   1696 		return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0,
   1697 							__stop_clock(0)));
   1698 	}
   1699 	nis_getprincipal(pname, reqstp);
   1700 	res = __nis_ibops(MOD_OP, argp, pname);
   1701 	if (verbose)
   1702 		syslog(LOG_INFO, "Done, exit status %s",
   1703 			nis_sperrno(res->status));
   1704 	res->zticks += __stop_clock(0);
   1705 	return (res);
   1706 }
   1707 
   1708 /*
   1709  * nis_remove, delete an entry from the indicated information base.
   1710  */
   1711 nis_result *
   1712 nis_ibremove_svc(argp, reqstp)
   1713 	ib_request *argp;
   1714 	struct svc_req *reqstp;
   1715 {
   1716 	char		pname[1024];
   1717 	nis_result 	*res;
   1718 
   1719 	__start_clock(0);
   1720 	if (verbose)
   1721 		syslog(LOG_INFO, "Entry REMOVE_SVC: to table %s",
   1722 							argp->ibr_name);
   1723 	/* A quick and easy check... */
   1724 	if ((argp->ibr_obj.ibr_obj_len == 1) &&
   1725 	    (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) {
   1726 		return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0,
   1727 							__stop_clock(0)));
   1728 	}
   1729 	nis_getprincipal(pname, reqstp);
   1730 	res = __nis_ibops(REM_OP, argp, pname);
   1731 	if (verbose)
   1732 		syslog(LOG_INFO, "Done, exit status %s",
   1733 			nis_sperrno(res->status));
   1734 	res->zticks += __stop_clock(0);
   1735 	return (res);
   1736 }
   1737 
   1738 /*
   1739  * nis_ibfirst()
   1740  * This function will return the first entry in an information base.
   1741  * It is provided primarily for backward compatibility with YP and its
   1742  * use is not encouraged.
   1743  */
   1744 nis_result *
   1745 nis_ibfirst_svc(argp, reqstp)
   1746 	ib_request *argp;
   1747 	struct svc_req *reqstp;
   1748 {
   1749 	char		pname[1024];
   1750 	nis_result 	*res;
   1751 
   1752 
   1753 	__start_clock(0);
   1754 	if (verbose)
   1755 		syslog(LOG_INFO, "Entry FIRST_SVC : Fetch from table %s",
   1756 			argp->ibr_name);
   1757 	nis_getprincipal(pname, reqstp);
   1758 	res = __nis_fnops(FIRST_OP, argp, pname);
   1759 	if (verbose)
   1760 		syslog(LOG_INFO, "Done, exit status %s",
   1761 			nis_sperrno(res->status));
   1762 	res->zticks += __stop_clock(0);
   1763 	return (res);
   1764 }
   1765 
   1766 /*
   1767  * nis_ibnext, return a subsequent entry in the information base.
   1768  */
   1769 nis_result *
   1770 nis_ibnext_svc(argp, reqstp)
   1771 	ib_request *argp;
   1772 	struct svc_req *reqstp;
   1773 {
   1774 	char		pname[1024];
   1775 	nis_result 	*res;
   1776 
   1777 	__start_clock(0);
   1778 	if (verbose)
   1779 		syslog(LOG_INFO, "Entry NEXT_SVC : Fetch from table %s",
   1780 			argp->ibr_name);
   1781 	nis_getprincipal(pname, reqstp);
   1782 	res = __nis_fnops(NEXT_OP, argp, pname);
   1783 	if (verbose)
   1784 		syslog(LOG_INFO, "Done, exit status %s",
   1785 			nis_sperrno(res->status));
   1786 	res->zticks += __stop_clock(0);
   1787 	return (res);
   1788 }
   1789