xref: /illumos-gate/usr/src/lib/libnisdb/ldap_xdr.c (revision 8d94f651a44d41a7147253bb5dad1a53941e8f50)
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 2015 Gary Mills
24  * Copyright (c) 2001 by Sun Microsystems, Inc.
25  * All rights reserved.
26  */
27 
28 #include <string.h>
29 #include <sys/syslog.h>
30 #include <sys/types.h>
31 #include <rpc/types.h>
32 #include <rpc/xdr.h>
33 #include <rpcsvc/nis.h>
34 
35 #include "db_mindex_c.h"
36 
37 #include "ldap_xdr.h"
38 #include "ldap_util.h"
39 
40 #include "nis_clnt.h"
41 
42 /*
43  * In order not to change the on-disk NIS+ DB format, we need make sure
44  * that XDR does nothing for the new structures added to various C++
45  * classes.
46  */
47 
48 bool_t
49 xdr___nis_table_mapping_t(XDR *xdrs, void *t) {
50 	return (TRUE);
51 }
52 
53 bool_t
54 xdr___nisdb_ptr_t(XDR *xdrs, void *ptr) {
55 	return (TRUE);
56 }
57 
58 bool_t
59 xdr___nisdb_dictionary_defer_t(XDR *xdrs, void *defer) {
60 	return (TRUE);
61 }
62 
63 bool_t
64 xdr___nisdb_rwlock_t(XDR *xdrs, void *rw) {
65 	return (TRUE);
66 }
67 
68 bool_t
69 xdr___nisdb_flag_t(XDR *xdrs, void *flag) {
70 	return (TRUE);
71 }
72 
73 /*
74  * Imported from rpc.nisd/nis_db.c
75  *
76  * Special abbreviated XDR string which knows that the namep parameter (mainly
77  * owner and group) has a trailing end which matches the last 'n' characters
78  * in the domainname part.  It makes use of those common characters to
79  * encode/decode this information.  We append an integer string to the
80  * name to be encoded which denotes the place in the domainname from where the
81  * common string starts.  For example, if the name was "foo.my.domain." and the
82  * domainname was "my.domain.", the name would be encoded as "foo.10" because
83  * the length of the common part "my.domain." is 10.
84  */
85 bool_t
86 xdr_nis_name_abbrev(
87 	XDR		*xdrs,
88 	nis_name	*namep,
89 	nis_name	domainname)	/* domainname field from the table */
90 {
91 	size_t	name_len, dom_len, min_len;
92 	char 	buf[NIS_MAXNAMELEN];
93 	char 	*name;
94 	char	*lenstr, *tmp;
95 	int	i;
96 
97 	switch (xdrs->x_op) {
98 	case XDR_ENCODE:
99 		/* Get the start of the common part */
100 		name = *namep;
101 		name_len = strlen(name);
102 		if (name_len == 0)
103 			return (xdr_nis_name(xdrs, namep));
104 		dom_len = strlen(domainname);
105 		min_len = (name_len < dom_len) ? name_len : dom_len;
106 		for (i = 1; i <= min_len; i++) {
107 			if (name[name_len - i] != domainname[dom_len - i])
108 				break;
109 		}
110 		i--;
111 		memcpy(buf, name, name_len - i);
112 		sprintf(buf + name_len - i, ".%d", dom_len - i);
113 		tmp = buf;
114 		return (xdr_nis_name(xdrs, &tmp));
115 
116 	case XDR_DECODE:
117 		tmp = buf;
118 		if (!xdr_nis_name(xdrs, &tmp))
119 		    return (FALSE);
120 		if ((buf[0] == '\0') || buf[strlen(buf) - 1] == '.') {
121 			/* It is either a FQN or a NULL string */
122 			if (*namep) {
123 				strcpy(*namep, buf);
124 				return (TRUE);
125 			} else {
126 				if ((*namep = strdup(buf)) == NULL)
127 					return (FALSE);
128 				else
129 					return (TRUE);
130 			}
131 		}
132 		/* Now concoct the new name */
133 		if ((lenstr = strrchr(buf, '.')) == NULL) {
134 			/* something went wrong here */
135 			syslog(LOG_ERR,
136 				"xdr_nis_name_abbrev: no dot found in %s", buf);
137 			return (FALSE);
138 		}
139 		i = atoi(lenstr + 1);
140 		strcpy(lenstr, domainname + i);
141 		if (*namep) {
142 			strcpy(*namep, buf);
143 		} else {
144 			if ((*namep = strdup(buf)) == NULL)
145 				return (FALSE);
146 		}
147 		return (TRUE);
148 
149 	default:
150 		return (xdr_nis_name(xdrs, namep));
151 	}
152 }
153 
154 /*
155  * Imported from rpc.nisd/nis_db.c
156  *
157  * special XDR for fetus object.  We create the actual object from the
158  * "forming" object plus the table object.  We create this special object to
159  * save the following components of the nis_object:
160  *	zo_name and zo_domain: replaced by just the length field of 0.  We had
161  *		to keep the length field for backward compatibility.  If we
162  *		ever change the object format, we should fix this.
163  *	zo_owner and zo_group: we condensed it by abbreviating the common part
164  *		shared between the table object and the entry object
165  *	en_type: Avoided altogether
166  *	zo_type and other en_data: Avoided altogether.
167  *
168  * XXX: If the definition of nis_object ever changes, this should be changed.
169  */
170 bool_t
171 xdr_nis_fetus_object(
172 	XDR		*xdrs,
173 	nis_object	*objp,	/* Entry object */
174 	nis_object	*tobj)	/* Table object */
175 {
176 	uint_t	size;
177 
178 	if (xdrs->x_op == XDR_FREE)
179 		return (xdr_nis_object(xdrs, objp));
180 	if (!xdr_nis_oid(xdrs, &objp->zo_oid))
181 		return (FALSE);
182 
183 	/*
184 	 * While encoding of zo_name, we put 0 in the length field, while for
185 	 * decoding, we get the name from the table object.
186 	 */
187 	if (xdrs->x_op == XDR_ENCODE) {
188 		size = 0;
189 		if (!xdr_u_int(xdrs, &size))
190 			return (FALSE);
191 	} else {
192 		if (!xdr_u_int(xdrs, &size))
193 			return (FALSE);
194 		if (size == 0) {	/* shrinked format */
195 			/* get the name from the table object */
196 			if ((objp->zo_name = strdup(tobj->zo_name)) == NULL)
197 				return (FALSE);
198 		} else {
199 			/*
200 			 * We are opening up the xdr_string implementation here
201 			 * because we called xdr_u_int() earlier.
202 			 */
203 			if ((objp->zo_name = (char *)malloc(size + 1)) == NULL)
204 				return (FALSE);
205 			if (!xdr_opaque(xdrs, objp->zo_name, size))
206 				return (FALSE);
207 		}
208 	}
209 
210 	/*
211 	 * We use the xdr_nis_name_abbrev() function for both owner
212 	 * and group which constructs the name from the domain name.
213 	 */
214 	if (!xdr_nis_name_abbrev(xdrs, &objp->zo_owner, tobj->zo_domain))
215 		return (FALSE);
216 	if (!xdr_nis_name_abbrev(xdrs, &objp->zo_group, tobj->zo_domain))
217 		return (FALSE);
218 
219 	/*
220 	 * While encoding of zo_domain, we put 0 in the length field, while for
221 	 * decoding, we get the name from the table object.  Same as above for
222 	 * the name.  Could have used a function instead.
223 	 */
224 	if (xdrs->x_op == XDR_ENCODE) {
225 		size = 0;
226 		if (!xdr_u_int(xdrs, &size))
227 			return (FALSE);
228 	} else {
229 		if (!xdr_u_int(xdrs, &size))
230 			return (FALSE);
231 		if (size == 0) {	/* shrinked format */
232 			/* get the name from the table object */
233 			if ((objp->zo_domain = strdup(tobj->zo_domain)) == NULL)
234 				return (FALSE);
235 		} else {
236 			/*
237 			 * We are opening up the xdr_string implementation here
238 			 * because we called xdr_u_int() earlier.
239 			 */
240 			if ((objp->zo_domain = (char *)malloc(size + 1))
241 				== NULL)
242 				return (FALSE);
243 			if (!xdr_opaque(xdrs, objp->zo_domain, size))
244 				return (FALSE);
245 		}
246 	}
247 
248 	if (!xdr_u_int(xdrs, &objp->zo_access))
249 		return (FALSE);
250 	if (!xdr_u_int(xdrs, &objp->zo_ttl))
251 		return (FALSE);
252 
253 	/*
254 	 * We know that this is an entry object, so we'll save all the entry_obj
255 	 * space because we can recreate it later.
256 	 */
257 	if (xdrs->x_op == XDR_ENCODE)
258 		return (TRUE);
259 	/* Now for the DECODE case, just handcraft the entries and ignore XDR */
260 	objp->zo_data.zo_type = NIS_ENTRY_OBJ;
261 	if ((objp->zo_data.objdata_u.en_data.en_type =
262 		strdup(tobj->zo_data.objdata_u.ta_data.ta_type)) == NULL)
263 		return (FALSE);
264 	objp->zo_data.objdata_u.en_data.en_cols.en_cols_val = NULL;
265 	objp->zo_data.objdata_u.en_data.en_cols.en_cols_len = 0;
266 	return (TRUE);
267 }
268 
269 static const char	*in_directory = "IN_DIRECTORY";
270 
271 /*
272  * Given an input NIS+ object, create the kind
273  * of pseudo-entry_obj (with an XDR-encoded nis_object in the
274  * first column) that's stored in the DB. Note that:
275  *
276  *	If the input object is an entry, it's assumed to have the
277  *	columns moved up one step (col 0 in en_cols.en_cols_val[1],
278  *	etc.). en_cols.en_cols_val[0] will be overwritten. The
279  *	input object will be changed (some pointers set to zero,
280  *	etc.) on exit.
281  *
282  *	'eo' is assumed to be a pointer to an empty entry_obj (or,
283  *	at least, one that can be overwritten). It must not be a
284  *	pointer to the entry_obj in 'obj'. If the input object is
285  *	of a type other than entry, the 'eo' pointer must have
286  *	en_cols.en_cols_val appropriately initialized to an array of
287  *	(at least) length one.
288  *
289  *	'tobj' is a pointer to the table object for the table for
290  *	which the entry_obj is destined. It's needed for entry objects,
291  *	but unused for other object types.
292  */
293 entry_obj *
294 makePseudoEntryObj(nis_object *obj, entry_obj *eo, nis_object *tobj) {
295 	int		bufsize;
296 	char		*buf;
297 	XDR		xdrs;
298 	bool_t		xret;
299 	uint_t		ecl;
300 	entry_col	*ecv;
301 	char		*myself = "makePseudoEntryObj";
302 
303 	if (obj == 0 || eo == 0)
304 		return (0);
305 
306 	if (obj->zo_data.zo_type == NIS_ENTRY_OBJ) {
307 		*eo = obj->zo_data.objdata_u.en_data;
308 		eo->en_type = 0;
309 
310 		/*
311 		 * To prevent the XDR function from making a copy of
312 		 * the entry columns, we set the columns structure to
313 		 * 0 (ie no column data)
314 		 */
315 		ecl = obj->EN_data.en_cols.en_cols_len;
316 		ecv = obj->EN_data.en_cols.en_cols_val;
317 		obj->EN_data.en_cols.en_cols_len  = 0;
318 		obj->EN_data.en_cols.en_cols_val  = 0;
319 	} else {
320 		eo->en_type = (char *)in_directory;
321 	}
322 
323 	bufsize = xdr_sizeof(xdr_nis_object, obj);
324 	buf = am(myself, bufsize);
325 	if (buf == 0) {
326 		if (obj->zo_data.zo_type == NIS_ENTRY_OBJ) {
327 			obj->EN_data.en_cols.en_cols_len = ecl;
328 			obj->EN_data.en_cols.en_cols_val = ecv;
329 		}
330 		return (0);
331 	}
332 
333 	xdrmem_create(&xdrs, (char *)buf, bufsize, XDR_ENCODE);
334 
335 	if (obj->zo_data.zo_type == NIS_ENTRY_OBJ) {
336 		xret = xdr_nis_fetus_object(&xdrs, obj, tobj);
337 	} else {
338 		xret = xdr_nis_object(&xdrs, obj);
339 	}
340 
341 	/* Restore the 'obj' */
342 	if (obj->zo_data.zo_type == NIS_ENTRY_OBJ) {
343 		obj->EN_data.en_cols.en_cols_len = ecl;
344 		obj->EN_data.en_cols.en_cols_val = ecv;
345 	}
346 
347 	if (!xret) {
348 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
349 			"%s: XDR encode failure", myself);
350 		sfree(buf);
351 		return (0);
352 	}
353 
354 	eo->en_cols.en_cols_val[0].ec_value.ec_value_val = buf;
355 	eo->en_cols.en_cols_val[0].ec_value.ec_value_len = xdr_getpos(&xdrs);
356 	eo->en_cols.en_cols_val[0].ec_flags = EN_BINARY+EN_XDR;
357 
358 	return (eo);
359 }
360 
361 nis_object *
362 unmakePseudoEntryObj(entry_obj *e, nis_object *tobj) {
363 	nis_object	*o;
364 	XDR		xdrs;
365 	bool_t		stat;
366 	char		*myself = "unmakePseudoEntryObj";
367 
368 	if (e == 0 || e->en_cols.en_cols_val == 0 ||
369 			e->en_cols.en_cols_len == 0)
370 		return (0);
371 
372 	o = am(myself, sizeof (*o));
373 	if (o == 0)
374 		return (0);
375 
376 	xdrmem_create(&xdrs, e->en_cols.en_cols_val[0].ec_value.ec_value_val,
377 			e->en_cols.en_cols_val[0].ec_value.ec_value_len,
378 			XDR_DECODE);
379 
380 	if (tobj != 0 && (e->en_type == 0 || e->en_type[0] == '\0')) {
381 		stat = xdr_nis_fetus_object(&xdrs, o, tobj);
382 	} else {
383 		stat = xdr_nis_object(&xdrs, o);
384 	}
385 
386 	if (!stat) {
387 		sfree(o);
388 		o = 0;
389 	}
390 
391 	/*
392 	 * If it's an entry object, construct the column information.
393 	 * We make this a copy, so that 'o' can be freed using
394 	 * nis_destroy_object().
395 	 */
396 	if (o != 0 && o->zo_data.zo_type == NIS_ENTRY_OBJ &&
397 			o->zo_data.objdata_u.en_data.en_cols.en_cols_val == 0 &&
398 			e->en_cols.en_cols_len > 1) {
399 		entry_col	*ec, *oec;
400 		uint_t		i, *ocl;
401 
402 		ec = am(myself, (e->en_cols.en_cols_len - 1) * sizeof (ec[0]));
403 		if (ec == 0) {
404 			nis_destroy_object(o);
405 			return (0);
406 		}
407 
408 		o->zo_data.objdata_u.en_data.en_cols.en_cols_val = ec;
409 		o->zo_data.objdata_u.en_data.en_cols.en_cols_len = 0;
410 		ocl = &o->zo_data.objdata_u.en_data.en_cols.en_cols_len;
411 		oec = e->en_cols.en_cols_val;
412 
413 		for (i = 1; i < e->en_cols.en_cols_len; i++) {
414 			uint_t	len;
415 
416 			if (oec[i].ec_value.ec_value_val != 0) {
417 				len = oec[i].ec_value.ec_value_len;
418 				if (len == 0)
419 					len++;
420 				ec[i-1].ec_value.ec_value_val = am(myself, len);
421 				if (ec[i-1].ec_value.ec_value_val == 0) {
422 					nis_destroy_object(o);
423 					return (0);
424 				}
425 				(void) memcpy(ec[i-1].ec_value.ec_value_val,
426 						oec[i].ec_value.ec_value_val,
427 						oec[i].ec_value.ec_value_len);
428 				ec[i-1].ec_value.ec_value_len =
429 						oec[i].ec_value.ec_value_len;
430 			} else {
431 				ec[i-1].ec_value.ec_value_val = 0;
432 				ec[i-1].ec_value.ec_value_len = 0;
433 			}
434 			*ocl += 1;
435 		}
436 	}
437 
438 	/*
439 	 * If it's an entry, and we have the table object, make sure
440 	 * zo_name and en_type either already are set, or get them
441 	 * from the table.
442 	 */
443 	if (o != 0 && o->zo_data.zo_type == NIS_ENTRY_OBJ && tobj != 0) {
444 		if (o->zo_name == 0)
445 			o->zo_name = sdup(myself, T, tobj->zo_name);
446 		if (o->zo_data.objdata_u.en_data.en_type == 0)
447 			o->zo_data.objdata_u.en_data.en_type = sdup(myself, T,
448 				tobj->zo_data.objdata_u.ta_data.ta_type);
449 	}
450 
451 	return (o);
452 }
453 
454 /*
455  * Input:  A (nis_object *), and (optionally) an (entry_obj *) array.
456  * Output: Pointer to an XDR:ed version of an (xdr_nis_object_t).
457  */
458 void *
459 xdrNisObject(nis_object *obj, entry_obj **ea, int numEa, int *xdrLenP) {
460 	xdr_nis_object_t	xno;
461 	void			*buf;
462 	int			xdrLen;
463 	XDR			xdrs;
464 	bool_t			xret;
465 	char			*myself = "xdrNisObject";
466 
467 	if (obj == 0)
468 		return (0);
469 
470 	/*
471 	 * The version tells us what the XDR:ed buffer contains.
472 	 * Should be incremented whenever xdr_nis_object_t changes
473 	 * incompatibly.
474 	 */
475 	xno.xversion = 1;
476 
477 	xno.obj = obj;
478 
479 	if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ &&
480 			ea != 0 && numEa > 0) {
481 		int	i;
482 
483 		/*
484 		 * The ea[] array is expected to contain the kind of
485 		 * pseudo-entry object stored in the nisdb incarnation
486 		 * of a NIS+ directory. Column zero contains the XDR:ed
487 		 * directory entry object (which we ignore), while column
488 		 * one contains the name of said entry. It's the latter
489 		 * that we borrow for use in the dirEntry[] list of the
490 		 * xdr_nis_object_t.
491 		 */
492 
493 		xno.dirEntry.dirEntry_len = 0;
494 		xno.dirEntry.dirEntry_val = am(myself, numEa *
495 			sizeof (xno.dirEntry.dirEntry_val[0]));
496 		if (xno.dirEntry.dirEntry_val == 0)
497 			return (0);
498 
499 		for (i = 0; i < numEa; i++) {
500 			if (ea[i] == 0 || ea[i]->en_cols.en_cols_val == 0 ||
501 					ea[i]->en_cols.en_cols_len != 2 ||
502 					ea[i]->en_cols.en_cols_val[1].
503 						ec_value.ec_value_len == 0)
504 				continue;
505 			/*
506 			 * Yes, there's a NUL at the end of the dir entry
507 			 * name.
508 			 */
509 			xno.dirEntry.dirEntry_val[xno.dirEntry.dirEntry_len] =
510 				ea[i]->en_cols.en_cols_val[1].
511 					ec_value.ec_value_val;
512 			xno.dirEntry.dirEntry_len++;
513 		}
514 	} else {
515 		/* No directory entries */
516 		xno.dirEntry.dirEntry_len = 0;
517 		xno.dirEntry.dirEntry_val = 0;
518 	}
519 
520 	xdrLen = xdr_sizeof(xdr_xdr_nis_object_t, &xno);
521 	buf = am(myself, xdrLen);
522 	if (buf == 0)
523 		return (0);
524 
525 	xdrmem_create(&xdrs, (char *)buf, xdrLen, XDR_ENCODE);
526 
527 	xret = xdr_xdr_nis_object_t(&xdrs, &xno);
528 
529 	sfree(xno.dirEntry.dirEntry_val);
530 
531 	if (!xret) {
532 		sfree(buf);
533 		return (0);
534 	}
535 
536 	if (xdrLenP != 0)
537 		*xdrLenP = xdrLen;
538 
539 	return (buf);
540 }
541 
542 /*
543  * Input:  Pointer to an XDR:ed version of an (xdr_nis_object_t).
544  * Output: Pointer to a (nis_object *) and (if the object is a
545  *         directory) a pointer to an array of (entry_obj *).
546  */
547 nis_object *
548 unXdrNisObject(void *buf, int bufLen, entry_obj ***eaP, int *numEaP) {
549 	xdr_nis_object_t	*xno;
550 	XDR			xdrs;
551 	bool_t			xret;
552 	entry_obj		**ea;
553 	int			numEa;
554 	nis_object		*o;
555 	char			*myself = "unXdrNisObject";
556 
557 	if (buf == 0 || bufLen <= 0)
558 		return (0);
559 
560 	xno = am(myself, sizeof (*xno));
561 	if (xno == 0)
562 		return (0);
563 
564 	xdrmem_create(&xdrs, buf, bufLen, XDR_DECODE);
565 	xret = xdr_xdr_nis_object_t(&xdrs, xno);
566 
567 	if (!xret) {
568 		sfree(xno);
569 		return (0);
570 	}
571 
572 	switch (xno->xversion) {
573 	case 1:
574 		break;
575 	default:
576 		xdr_free(xdr_xdr_nis_object_t, (char *)xno);
577 		sfree(xno);
578 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
579 			"%s: Unknown xdr_nis_object_t version %d",
580 			myself, xno->xversion);
581 		return (0);
582 	}
583 
584 	if (eaP != 0 && numEaP != 0 && xno->dirEntry.dirEntry_len > 0 &&
585 			xno->dirEntry.dirEntry_val != 0) {
586 		ea = am(myself, xno->dirEntry.dirEntry_len * sizeof (ea[0]));
587 		if (ea == 0) {
588 			xdr_free(xdr_xdr_nis_object_t, (char *)xno);
589 			sfree(xno);
590 			return (0);
591 		}
592 		for (numEa = 0; numEa < xno->dirEntry.dirEntry_len; numEa++) {
593 			ea[numEa] = am(myself, sizeof (*ea[numEa]));
594 			if (ea[numEa] != 0) {
595 				ea[numEa]->en_cols.en_cols_len = 2;
596 				ea[numEa]->en_cols.en_cols_val = am(myself,
597 					ea[numEa]->en_cols.en_cols_len *
598 				sizeof (ea[numEa]->en_cols.en_cols_val[0]));
599 			}
600 			if (ea[numEa] == 0 ||
601 					ea[numEa]->en_cols.en_cols_val == 0) {
602 				int	i;
603 				for (i = 0; i < numEa; i++) {
604 					sfree(ea[i]->en_cols.en_cols_val);
605 					sfree(ea[i]);
606 				}
607 				sfree(ea);
608 				xdr_free(xdr_xdr_nis_object_t, (char *)xno);
609 				sfree(xno);
610 				return (0);
611 			}
612 			/* Leave column 0 (XDR:ed object) empty */
613 			ea[numEa]->en_cols.en_cols_val[0].
614 				ec_value.ec_value_len = 0;
615 			ea[numEa]->en_cols.en_cols_val[0].
616 				ec_value.ec_value_val = 0;
617 			/*
618 			 * Fill in name of dir entry. The DB counts the NUL
619 			 * as part of the dir entry name; hence, add one
620 			 * to the string length.
621 			 */
622 			ea[numEa]->en_cols.en_cols_val[1].
623 				ec_value.ec_value_len = slen(xno->dirEntry.
624 					dirEntry_val[numEa]) + 1;
625 			ea[numEa]->en_cols.en_cols_val[1].
626 				ec_value.ec_value_val =
627 					xno->dirEntry.dirEntry_val[numEa];
628 		}
629 		*eaP = ea;
630 		*numEaP = numEa;
631 		/*
632 		 * The xno->dirEntry.dirEntry_val[] pointers are duplicated
633 		 * in 'ea'. Set the xno pointers to zero, so that the xdr_free
634 		 * doesn't free the 'ea' data.
635 		 */
636 		if (numEa > 0) {
637 			int	i;
638 			for (i = 0; i < numEa; i++) {
639 				xno->dirEntry.dirEntry_val[i] = 0;
640 			}
641 		}
642 	} else {
643 		if (eaP != 0)
644 			*eaP = 0;
645 		if (numEaP != 0)
646 			*numEaP = 0;
647 	}
648 
649 	o = xno->obj;
650 	xno->obj = 0;
651 	xdr_free(xdr_xdr_nis_object_t, (char *)xno);
652 	sfree(xno);
653 
654 	return (o);
655 }
656 
657 void
658 freeEntryObjArray(entry_obj **ea, int numEa) {
659 	int	i;
660 
661 	if (ea == 0)
662 		return;
663 
664 	for (i = 0; i < numEa; i++) {
665 		int	j;
666 
667 		for (j = 0; j < ea[i]->en_cols.en_cols_len; j++) {
668 			sfree(ea[i]->en_cols.en_cols_val[j].
669 				ec_value.ec_value_val);
670 		}
671 
672 		sfree(ea[i]->en_cols.en_cols_val);
673 	}
674 
675 	sfree(ea);
676 }
677 
678 /*
679  * Return TRUE if 'o1' and 'o2' are the same, FALSE otherwise.
680  * We perform the comparison by XDR encoding the objects, and then
681  * checking the XDR buffers for equality. However, we don't want to
682  * include the zo_oid (i.e., ctime and mtime) in the comparison.
683  */
684 bool_t
685 sameNisPlusObj(nis_object *o1, nis_object *o2) {
686 	XDR		x1, x2;
687 	void		*b1, *b2;
688 	int		l1, l2;
689 	bool_t		ret;
690 	nis_object	obj1, obj2;
691 	char		*myself = "sameNisPlusObj";
692 
693 	if (o1 == o2)
694 		return (TRUE);
695 	else if (o1 == 0 || o2 == 0)
696 		return (FALSE);
697 
698 	/*
699 	 * We want to exclude the zo_oid from the comparison. In order
700 	 * not to modify the objects (even very briefly), we do this by
701 	 * making copies (nis_object itself only, not the underlying
702 	 * structures accessed through pointers), and setting the zo_oid
703 	 * to zero in the copies.
704 	 */
705 	obj1 = *o1;
706 	obj2 = *o2;
707 	obj1.zo_oid.ctime = obj1.zo_oid.mtime = 0;
708 	obj2.zo_oid.ctime = obj2.zo_oid.mtime = 0;
709 
710 	l1 = xdr_sizeof(xdr_nis_object, &obj1);
711 	l2 = xdr_sizeof(xdr_nis_object, &obj2);
712 	if (l1 != l2)
713 		return (FALSE);
714 
715 	b1 = am(myself, l1);
716 	b2 = am(myself, l2);
717 	if (b1 == 0 || b2 == 0) {
718 		sfree(b1);
719 		sfree(b2);
720 		return (FALSE);
721 	}
722 
723 	xdrmem_create(&x1, (char *)b1, l1, XDR_ENCODE);
724 	xdrmem_create(&x2, (char *)b2, l2, XDR_ENCODE);
725 
726 	if (xdr_nis_object(&x1, &obj1) && xdr_nis_object(&x2, &obj2)) {
727 		ret = (memcmp(b1, b2, l1) == 0);
728 	} else {
729 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
730 			"%s: xdr_nis_object() error",
731 			myself);
732 		ret = FALSE;
733 	}
734 
735 	sfree(b1);
736 	sfree(b2);
737 
738 	return (ret);
739 }
740 
741 /*
742  * A wrapper/convenience function for sameNisPlusObj() that extracts
743  * the object in column zero of 'e2'.
744  */
745 bool_t
746 sameNisPlusPseudoObj(nis_object *o1, entry_obj *e2) {
747 	nis_object	*o2;
748 	bool_t		res;
749 
750 	if (o1 == 0 && e2 == 0)
751 		return (TRUE);
752 	else if (e2 == 0)
753 		return (FALSE);
754 
755 	o2 = unmakePseudoEntryObj(e2, 0);
756 	if (o2 == 0)
757 		return ((o1 == 0) ? TRUE : FALSE);
758 
759 	res = sameNisPlusObj(o1, o2);
760 
761 	nis_destroy_object(o2);
762 
763 	return (res);
764 }
765