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] == NULL) || 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 }