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 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
  29  */
  30 
  31 #include "lint.h"
  32 #include <mtlib.h>
  33 #include <dlfcn.h>
  34 #include <atomic.h>
  35 
  36 #define __NSS_PRIVATE_INTERFACE
  37 #include "nsswitch_priv.h"
  38 #undef  __NSS_PRIVATE_INTERFACE
  39 
  40 #include <nss_common.h>
  41 #include <nss_dbdefs.h>
  42 #include <unistd.h>
  43 #include <stdlib.h>
  44 #include <stdio.h>
  45 #include <string.h>
  46 #include <thread.h>
  47 #include <synch.h>
  48 #include <pthread.h>
  49 #include <sys/types.h>
  50 #include <sys/mman.h>
  51 #include <errno.h>
  52 #include "libc.h"
  53 #include "tsd.h"
  54 
  55 #include <getxby_door.h>
  56 
  57 /*
  58  * configurable values for default buffer sizes
  59  */
  60 
  61 /*
  62  * PSARC/2005/133 updated the buffering mechanisms to handle
  63  * up to 2^64 buffering.  But sets a practical limit of 512*1024.
  64  * The expectation is the practical limit will be dynamic from
  65  * nscd.  For now, set the group limit to this value.
  66  */
  67 
  68 #define NSS_BUFLEN_PRACTICAL    (512*1024)
  69 
  70 static size_t __nss_buflen_group = NSS_BUFLEN_PRACTICAL;
  71 static size_t __nss_buflen_default = NSS_BUFLEN_DOOR;
  72 
  73 /*
  74  * policy component function interposing definitions:
  75  * nscd if so desired can interpose it's own switch functions over
  76  * the internal unlocked counterparts.  This will allow nscd to replace
  77  * the switch policy state engine with one that uses it's internal
  78  * components.
  79  * Only nscd can change this through it's use of nss_config.
  80  * The golden rule is: ptr == NULL checking is used in the switch to
  81  * see if a function was interposed.  But nscd is responsible for seeing
  82  * that mutex locking to change the values are observed when the data is
  83  * changed.  Especially if it happens > once.  The switch does not lock
  84  * the pointer with mutexs.
  85  */
  86 
  87 typedef struct {
  88         void    *p;
  89 #if 0
  90         void            (*nss_delete_fp)(nss_db_root_t *rootp);
  91         nss_status_t    (*nss_search_fp)(nss_db_root_t *rootp,
  92                                 nss_db_initf_t initf, int search_fnum,
  93                                 void *search_args);
  94         void            (*nss_setent_u_fp)(nss_db_root_t *,
  95                                 nss_db_initf_t, nss_getent_t *);
  96         nss_status_t    (*nss_getent_u_fp)(nss_db_root_t *,
  97                                 nss_db_initf_t, nss_getent_t *, void *);
  98         void            (*nss_endent_u_fp)(nss_db_root_t *,
  99                                 nss_db_initf_t, nss_getent_t *);
 100         void            (*end_iter_u_fp)(nss_db_root_t *rootp,
 101                                 struct nss_getent_context *contextp);
 102 #endif
 103 } nss_policyf_t;
 104 
 105 static mutex_t nss_policyf_lock = DEFAULTMUTEX;
 106 static nss_policyf_t nss_policyf_ptrs =
 107         { (void *)NULL };
 108 
 109 /*
 110  * nsswitch db_root state machine definitions:
 111  * The golden rule is:  if you hold a pointer to an nss_db_state struct and
 112  * you don't hold the lock, you'd better have incremented the refcount
 113  * while you held the lock;  otherwise, it may vanish or change
 114  * significantly when you least expect it.
 115  *
 116  * The pointer in nss_db_root_t is one such, so the reference count >= 1.
 117  * Ditto the pointer in struct nss_getent_context.
 118  */
 119 
 120 /*
 121  * State for one nsswitch database (e.g. "passwd", "hosts")
 122  */
 123 struct nss_db_state {
 124         nss_db_root_t           orphan_root;    /* XXX explain */
 125         unsigned                refcount;       /* One for the pointer in    */
 126                                                 /*   nss_db_root_t, plus one */
 127                                                 /*   for each active thread. */
 128         nss_db_params_t         p;
 129         struct __nsw_switchconfig_v1 *config;
 130         int                     max_src;        /* is == config->num_lookups */
 131         struct nss_src_state    *src;           /* Pointer to array[max_src] */
 132 };
 133 
 134 /*
 135  * State for one of the sources (e.g. "nis", "compat") for a database
 136  */
 137 struct nss_src_state {
 138         struct __nsw_lookup_v1  *lkp;
 139         int                     n_active;
 140         int                     n_dormant;
 141         int                     n_waiting;      /* ... on wanna_be */
 142         cond_t                  wanna_be;
 143         union {
 144                 nss_backend_t   *single; /* Efficiency hack for common case */
 145                                             /* when limit_dead_backends == 1 */
 146                 nss_backend_t   **multi; /* array[limit_dead_backends] of */
 147         } dormant;                          /* pointers to dormant backends */
 148         nss_backend_constr_t    be_constr;
 149         nss_backend_finder_t    *finder;
 150         void                    *finder_priv;
 151 };
 152 
 153 static struct nss_db_state      *_nss_db_state_constr(nss_db_initf_t);
 154 void                            _nss_db_state_destr(struct nss_db_state *);
 155 
 156 /* ==== null definitions if !MTSAFE?  Ditto lock field in nss_db_root_t */
 157 
 158 #define NSS_ROOTLOCK(r, sp)     (cancel_safe_mutex_lock(&(r)->lock), \
 159                                 *(sp) = (r)->s)
 160 
 161 #define NSS_UNLOCK(r)           (cancel_safe_mutex_unlock(&(r)->lock))
 162 
 163 #define NSS_CHECKROOT(rp, s)    ((s) != (*(rp))->s &&                        \
 164                         (cancel_safe_mutex_unlock(&(*(rp))->lock),       \
 165                         cancel_safe_mutex_lock(&(s)->orphan_root.lock), \
 166                         *(rp) = &(s)->orphan_root))
 167 
 168 #define NSS_RELOCK(rp, s)       (cancel_safe_mutex_lock(&(*(rp))->lock), \
 169                         NSS_CHECKROOT(rp, s))
 170 
 171 #define NSS_STATE_REF_u(s)      (++(s)->refcount)
 172 
 173 #define NSS_UNREF_UNLOCK(r, s)  (--(s)->refcount != 0                        \
 174                         ? ((void)NSS_UNLOCK(r))                         \
 175                         : ((void)NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
 176 
 177 #define NSS_LOCK_CHECK(r, f, sp)    (NSS_ROOTLOCK((r), (sp)),   \
 178                                     *(sp) == 0 &&               \
 179                                     (r->s = *(sp) = _nss_db_state_constr(f)))
 180 /* === In the future, NSS_LOCK_CHECK() may also have to check that   */
 181 /* === the config info hasn't changed (by comparing version numbers) */
 182 
 183 
 184 /*
 185  * NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions:
 186  * This remains for backwards compatibility.  But generally nscd will
 187  * decide if/how this gets used.
 188  */
 189 static int checked_env = 0;             /* protected by "rootlock" */
 190 
 191         /* allowing __nss_debug_file to be set could be a security hole. */
 192 FILE *__nss_debug_file = stdout;
 193 int __nss_debug_eng_loop;
 194 
 195 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
 196         /* allowing __nis_debug_file to be set could be a security hole. */
 197 FILE *__nis_debug_file = stdout;
 198 int __nis_debug_bind;
 199 int __nis_debug_rpc;
 200 int __nis_debug_calls;
 201 char *__nis_prefsrv;
 202 char *__nis_preftype;
 203 char *__nis_server;   /* if set, use only this server for binding */
 204 
 205 #define OPT_INT 1
 206 #define OPT_STRING 2
 207 #ifdef DEBUG
 208 #define OPT_FILE 3
 209 #endif
 210 
 211 struct option {
 212         char *name;
 213         int type;
 214         void *address;
 215 };
 216 
 217 static struct option nss_options[] = {
 218 #ifdef DEBUG
 219         /* allowing __nss_debug_file to be set could be a security hole. */
 220         { "debug_file", OPT_FILE, &__nss_debug_file },
 221 #endif
 222         { "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop },
 223         { 0, 0, 0 },
 224 };
 225 
 226 static struct option nis_options[] = {
 227 #ifdef DEBUG
 228         /* allowing __nis_debug_file to be set could be a security hole. */
 229         { "debug_file", OPT_FILE, &__nis_debug_file },
 230 #endif
 231         { "debug_bind", OPT_INT, &__nis_debug_bind },
 232         { "debug_rpc", OPT_INT, &__nis_debug_rpc },
 233         { "debug_calls", OPT_INT, &__nis_debug_calls },
 234         { "server", OPT_STRING, &__nis_server },
 235         { "pref_srvr", OPT_STRING, &__nis_prefsrv },
 236         { "pref_type", OPT_STRING, &__nis_preftype },
 237         { 0, 0, 0 },
 238 };
 239 
 240 /*
 241  * switch configuration parameter "database" definitions:
 242  * The switch maintains a simmple read/write parameter database
 243  * that nscd and the switch components can use to communicate
 244  * nscd data to other components for configuration or out of band
 245  * [IE no in the context of a getXbyY or putXbyY operation] data.
 246  * The data passed are pointers to a lock  data buffer and a length.
 247  * Use of this is treated as SunwPrivate between nscd and the switch
 248  * unless other wise stated.
 249  */
 250 
 251 typedef struct nss_cfgparam {
 252         char            *name;
 253         mutex_t         *lock;
 254         void            *buffer;
 255         size_t          length;
 256 } nss_cfgparam_t;
 257 
 258 typedef struct nss_cfglist {
 259         char            *name;
 260         nss_cfgparam_t  *list;
 261         int             count;
 262         int             max;
 263 } nss_cfglist_t;
 264 
 265 #define NSS_CFG_INCR    16
 266 
 267 static nss_cfglist_t *nss_cfg = NULL;
 268 static int nss_cfgcount = 0;
 269 static int nss_cfgmax = 0;
 270 static mutex_t nss_cfglock = DEFAULTMUTEX;
 271 
 272 static int nss_cfg_policy_init();
 273 
 274 /*
 275  * A config parameters are in the form component:parameter
 276  * as in: nss:parameter - switch (internal FE/policy/BE) parameter
 277  *        nscd:param - nscd application parameter
 278  *        ldap:param - nss_ldap BE parameter
 279  *        passwd:param - get/put passwd FE parameter
 280  */
 281 
 282 #define NSS_CONFIG_BRK  ':'
 283 
 284 /*
 285  * The policy components initial parameter list
 286  */
 287 static nss_config_t     nss_policy_params[] = {
 288         { "nss:policyfunc", NSS_CONFIG_ADD, &nss_policyf_lock,
 289                 (void *)&nss_policyf_ptrs, (size_t)sizeof (nss_policyf_t) },
 290         { NULL, NSS_CONFIG_ADD, (mutex_t *)NULL, (void *)NULL, (size_t)0 },
 291 };
 292 
 293 /*
 294  * NSS parameter configuration routines
 295  */
 296 
 297 /* compare config name (component:parameter) to a component name */
 298 static int
 299 nss_cfgcn_cmp(const char *cfgname, const char *compname)
 300 {
 301         char *c;
 302         size_t len, len2;
 303 
 304         /* this code assumes valid pointers */
 305         if ((c = strchr(cfgname, NSS_CONFIG_BRK)) == NULL)
 306                 return (-1);
 307         len = (size_t)(c - cfgname);
 308         len2 = strlen(compname);
 309         if (len2 != len)
 310                 return (-1);
 311         return (strncmp(cfgname, compname, len));
 312 }
 313 
 314 /* init configuration arena */
 315 static int
 316 nss_cfg_init()
 317 {
 318         nss_cfglist_t *cfg;
 319         int i;
 320 
 321         /* First time caller? */
 322         if (nss_cfg != NULL) {
 323                 membar_consumer();
 324                 return (0);
 325         }
 326 
 327         /* Initialize internal tables */
 328         lmutex_lock(&nss_cfglock);
 329         if (nss_cfg != NULL) {
 330                 lmutex_unlock(&nss_cfglock);
 331                 membar_consumer();
 332                 return (0);
 333         }
 334         cfg = libc_malloc(NSS_CFG_INCR * sizeof (nss_cfglist_t));
 335         if (cfg == NULL) {
 336                 errno = ENOMEM;
 337                 lmutex_unlock(&nss_cfglock);
 338                 return (-1);
 339         }
 340         for (i = 0; i < NSS_CFG_INCR; i++) {
 341                 cfg[i].list = libc_malloc(
 342                     NSS_CFG_INCR * sizeof (nss_cfgparam_t));
 343                 if (cfg[i].list == NULL) {
 344                         while (--i >= 0)
 345                                 libc_free(cfg[i].list);
 346                         libc_free(cfg);
 347                         errno = ENOMEM;
 348                         lmutex_unlock(&nss_cfglock);
 349                         return (-1);
 350                 }
 351                 cfg[i].max = NSS_CFG_INCR;
 352         }
 353         nss_cfgmax = NSS_CFG_INCR;
 354         membar_producer();
 355         nss_cfg = cfg;
 356         lmutex_unlock(&nss_cfglock);
 357 
 358         /* Initialize Policy Engine values */
 359         if (nss_cfg_policy_init() < 0) {
 360                 return (-1);
 361         }
 362         return (0);
 363 }
 364 
 365 /* find the name'd component list - create it if non-existent */
 366 static nss_cfglist_t *
 367 nss_cfgcomp_get(char *name, int add)
 368 {
 369         nss_cfglist_t   *next;
 370         char    *c;
 371         int     i, len;
 372         size_t  nsize;
 373 
 374         /* Make sure system is init'd */
 375         if (nss_cfg_init() < 0)
 376                 return ((nss_cfglist_t *)NULL);
 377 
 378         /* and check component:name validity */
 379         if (name == NULL || (c = strchr(name, NSS_CONFIG_BRK)) == NULL)
 380                 return ((nss_cfglist_t *)NULL);
 381 
 382         lmutex_lock(&nss_cfglock);
 383         next = nss_cfg;
 384         for (i = 0; i < nss_cfgcount; i++) {
 385                 if (next->name && nss_cfgcn_cmp(name, next->name) == 0) {
 386                         lmutex_unlock(&nss_cfglock);
 387                         return (next);
 388                 }
 389                 next++;
 390         }
 391         if (!add) {
 392                 lmutex_unlock(&nss_cfglock);
 393                 return (NULL);
 394         }
 395 
 396         /* not found, create a fresh one */
 397         if (nss_cfgcount >= nss_cfgmax) {
 398                 /* realloc first */
 399                 nsize = (nss_cfgmax + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
 400                 next = (nss_cfglist_t *)libc_realloc(nss_cfg, nsize);
 401                 if (next == NULL) {
 402                         errno = ENOMEM;
 403                         lmutex_unlock(&nss_cfglock);
 404                         return ((nss_cfglist_t *)NULL);
 405                 }
 406                 (void) memset((void *)(next + nss_cfgcount), '\0',
 407                     NSS_CFG_INCR * sizeof (nss_cfglist_t));
 408                 nss_cfgmax += NSS_CFG_INCR;
 409                 nss_cfg = next;
 410         }
 411         next = nss_cfg + nss_cfgcount;
 412         len = (size_t)(c - name) + 1;
 413         if ((next->name = libc_malloc(len)) == NULL) {
 414                 errno = ENOMEM;
 415                 lmutex_unlock(&nss_cfglock);
 416                 return ((nss_cfglist_t *)NULL);
 417         }
 418         nss_cfgcount++;
 419         (void) strlcpy(next->name, name, len);
 420         lmutex_unlock(&nss_cfglock);
 421         return (next);
 422 }
 423 
 424 /* find the name'd parameter - create it if non-existent */
 425 static nss_cfgparam_t *
 426 nss_cfgparam_get(char *name, int add)
 427 {
 428         nss_cfglist_t   *comp;
 429         nss_cfgparam_t  *next;
 430         int     count, i;
 431         size_t  nsize;
 432 
 433         if ((comp = nss_cfgcomp_get(name, add)) == NULL)
 434                 return ((nss_cfgparam_t *)NULL);
 435         lmutex_lock(&nss_cfglock);
 436         count = comp->count;
 437         next = comp->list;
 438         for (i = 0; i < count; i++) {
 439                 if (next->name && strcmp(name, next->name) == 0) {
 440                         lmutex_unlock(&nss_cfglock);
 441                         return (next);
 442                 }
 443                 next++;
 444         }
 445         if (!add) {
 446                 lmutex_unlock(&nss_cfglock);
 447                 return (NULL);
 448         }
 449 
 450         /* not found, create a fresh one */
 451         if (count >= comp->max) {
 452                 /* realloc first */
 453                 nsize = (comp->max + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
 454                 next = (nss_cfgparam_t *)libc_realloc(comp->list, nsize);
 455                 if (next == NULL) {
 456                         errno = ENOMEM;
 457                         lmutex_unlock(&nss_cfglock);
 458                         return ((nss_cfgparam_t *)NULL);
 459                 }
 460                 comp->max += NSS_CFG_INCR;
 461                 comp->list = next;
 462         }
 463         next = comp->list + comp->count;
 464         if ((next->name = libc_strdup(name)) == NULL) {
 465                 errno = ENOMEM;
 466                 lmutex_unlock(&nss_cfglock);
 467                 return ((nss_cfgparam_t *)NULL);
 468         }
 469         comp->count++;
 470         lmutex_unlock(&nss_cfglock);
 471         return (next);
 472 }
 473 
 474 /* find the name'd parameter - delete it if it exists */
 475 static void
 476 nss_cfg_del(nss_config_t *cfgp)
 477 {
 478         char            *name;
 479         nss_cfglist_t   *comp;
 480         nss_cfgparam_t  *next, *cur;
 481         int     count, i, j;
 482 
 483         /* exit if component name does not already exist */
 484         if ((name = cfgp->name) == NULL ||
 485             (comp = nss_cfgcomp_get(name, 0)) == NULL)
 486                 return;
 487 
 488         /* find it */
 489         lmutex_lock(&nss_cfglock);
 490         count = comp->count;
 491         next = comp->list;
 492         for (i = 0; i < count; i++) {
 493                 if (next->name && strcmp(name, next->name) == 0) {
 494                         break;  /* found it... */
 495                 }
 496                 next++;
 497         }
 498         if (i >= count) {
 499                 /* not found, already deleted */
 500                 lmutex_unlock(&nss_cfglock);
 501                 return;
 502         }
 503 
 504         /* copy down the remaining parameters, and clean up */
 505         /* don't try to clean up component tables */
 506         cur = next;
 507         next++;
 508         for (j = i+1; j < count; j++) {
 509                 *cur = *next;
 510                 cur++;
 511                 next++;
 512         }
 513         /* erase the last one */
 514         if (cur->name) {
 515                 libc_free(cur->name);
 516                 cur->name = (char *)NULL;
 517         }
 518         cur->lock = (mutex_t *)NULL;
 519         cur->buffer = (void *)NULL;
 520         cur->length = 0;
 521         comp->count--;
 522         lmutex_unlock(&nss_cfglock);
 523 }
 524 
 525 static int
 526 nss_cfg_get(nss_config_t *next)
 527 {
 528         nss_cfgparam_t  *param;
 529 
 530         errno = 0;
 531         if ((param = nss_cfgparam_get(next->name, 0)) == NULL)
 532                 return (-1);
 533         next->lock = param->lock;
 534         next->buffer = param->buffer;
 535         next->length = param->length;
 536         return (0);
 537 }
 538 
 539 static int
 540 nss_cfg_put(nss_config_t *next, int add)
 541 {
 542         nss_cfgparam_t  *param;
 543 
 544         errno = 0;
 545         if ((param = nss_cfgparam_get(next->name, add)) == NULL)
 546                 return (-1);
 547         param->lock = next->lock;
 548         param->buffer = next->buffer;
 549         param->length = next->length;
 550         return (0);
 551 }
 552 
 553 /*
 554  * Policy engine configurator - set and get interface
 555  * argument is a NULL terminated list of set/get requests
 556  * with input/result buffers and lengths.  nss_cname is the
 557  * specifier of a set or get operation and the property being
 558  * managed.  The intent is limited functions and expandability.
 559  */
 560 
 561 nss_status_t
 562 nss_config(nss_config_t **plist, int cnt)
 563 {
 564         nss_config_t    *next;
 565         int     i;
 566 
 567         /* interface is only available to nscd */
 568         if (_nsc_proc_is_cache() <= 0) {
 569                 return (NSS_UNAVAIL);
 570         }
 571         if (plist == NULL || cnt <= 0)
 572                 return (NSS_SUCCESS);
 573         for (i = 0; i < cnt; i++) {
 574                 next = plist[i];
 575                 if (next == NULL)
 576                         break;
 577                 if (next->name == NULL) {
 578                         errno = EFAULT;
 579                         return (NSS_ERROR);
 580                 }
 581                 switch (next->cop) {
 582                 case NSS_CONFIG_GET:
 583                         /* get current lock/buffer/length fields */
 584                         if (nss_cfg_get(next) < 0) {
 585                                 return (NSS_ERROR);
 586                         }
 587                         break;
 588                 case NSS_CONFIG_PUT:
 589                         /* set new lock/buffer/length fields */
 590                         if (nss_cfg_put(next, 0) < 0) {
 591                                 return (NSS_ERROR);
 592                         }
 593                         break;
 594                 case NSS_CONFIG_ADD:
 595                         /* add parameter & set new lock/buffer/length fields */
 596                         if (nss_cfg_put(next, 1) < 0) {
 597                                 return (NSS_ERROR);
 598                         }
 599                         break;
 600                 case NSS_CONFIG_DELETE:
 601                         /* delete parameter - should always work... */
 602                         nss_cfg_del(next);
 603                         break;
 604                 case NSS_CONFIG_LIST:
 605                         break;
 606                 default:
 607                         continue;
 608                 }
 609         }
 610         return (NSS_SUCCESS);
 611 }
 612 
 613 /*
 614  * This routine is called immediately after nss_cfg_init but prior to
 615  * any commands from nscd being processed.  The intent here is to
 616  * initialize the nss:* parameters allowed by the policy component
 617  * so that nscd can then proceed and modify them if so desired.
 618  *
 619  * We know we can only get here if we are nscd so we can skip the
 620  * preliminaries.
 621  */
 622 
 623 static int
 624 nss_cfg_policy_init()
 625 {
 626         nss_config_t    *next = &nss_policy_params[0];
 627 
 628         for (; next && next->name != NULL; next++) {
 629                 if (nss_cfg_put(next, 1) < 0)
 630                         return (-1);
 631         }
 632         return (0);
 633 }
 634 
 635 /*
 636  * NSS_OPTION & NIS_OPTION environment variable functions
 637  */
 638 
 639 static
 640 void
 641 set_option(struct option *opt, char *name, char *val)
 642 {
 643         int n;
 644         char *p;
 645 #ifdef DEBUG
 646         FILE *fp;
 647 #endif
 648 
 649         for (; opt->name; opt++) {
 650                 if (strcmp(name, opt->name) == 0) {
 651                         switch (opt->type) {
 652                         case OPT_STRING:
 653                                 p = libc_strdup(val);
 654                                 *((char **)opt->address) = p;
 655                                 break;
 656 
 657                         case OPT_INT:
 658                                 if (strcmp(val, "") == 0)
 659                                         n = 1;
 660                                 else
 661                                         n = atoi(val);
 662                                 *((int *)opt->address) = n;
 663                                 break;
 664 #ifdef DEBUG
 665                         case OPT_FILE:
 666                                 fp = fopen(val, "wF");
 667                                 *((FILE **)opt->address) = fp;
 668                                 break;
 669 #endif
 670                         }
 671                         break;
 672                 }
 673         }
 674 }
 675 
 676 static
 677 void
 678 __parse_environment(struct option *opt, char *p)
 679 {
 680         char *base;
 681         char optname[100];
 682         char optval[100];
 683 
 684         while (*p) {
 685                 while (isspace(*p))
 686                         p++;
 687                 if (*p == '\0')
 688                         break;
 689 
 690                 base = p;
 691                 while (*p && *p != '=' && !isspace(*p))
 692                         p++;
 693                 /*
 694                  * play it safe and keep it simple, bail if an opt name
 695                  * is too long.
 696                  */
 697                 if ((p-base) >= sizeof (optname))
 698                         return;
 699 
 700                 (void) strncpy(optname, base, p-base);
 701                 optname[p-base] = '\0';
 702 
 703                 if (*p == '=') {
 704                         p++;
 705                         base = p;
 706                         while (*p && !isspace(*p))
 707                                 p++;
 708                         /*
 709                          * play it safe and keep it simple, bail if an opt
 710                          * value is too long.
 711                          */
 712                         if ((p-base) >= sizeof (optval))
 713                                 return;
 714 
 715                         (void) strncpy(optval, base, p-base);
 716                         optval[p-base] = '\0';
 717                 } else {
 718                         optval[0] = '\0';
 719                 }
 720 
 721                 set_option(opt, optname, optval);
 722         }
 723 }
 724 
 725 static
 726 void
 727 nss_get_environment()
 728 {
 729         char *p;
 730 
 731 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
 732         p = getenv("NSS_OPTIONS");
 733         if (p == NULL)
 734                 return;
 735         __parse_environment(nss_options, p);
 736 }
 737 
 738 /*
 739  * sole external routine called from libnsl/nis/cache/cache_api.cc in the
 740  * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
 741  * Only after checking "checked_env" (which must be done with mutex
 742  * "cur_cache_lock" held) and is done once, (then "checked_env" is set)
 743  */
 744 void
 745 __nis_get_environment()
 746 {
 747         char *p;
 748 
 749         p = getenv("NIS_OPTIONS");
 750         if (p == NULL)
 751                 return;
 752         __parse_environment(nis_options, p);
 753 }
 754 
 755 
 756 /*
 757  * Switch policy component backend state machine functions
 758  */
 759 
 760 static nss_backend_t *
 761 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src)
 762 {
 763         struct nss_src_state    *src = &s->src[n_src];
 764         nss_backend_t           *be;
 765         int cancel_state;
 766 
 767         for (;;) {
 768                 if (src->n_dormant > 0) {
 769                         src->n_dormant--;
 770                         src->n_active++;
 771                         if (s->p.max_dormant_per_src == 1) {
 772                                 be = src->dormant.single;
 773                         } else {
 774                                 be = src->dormant.multi[src->n_dormant];
 775                         }
 776                         break;
 777                 }
 778 
 779                 if (src->be_constr == 0) {
 780                         nss_backend_finder_t    *bf;
 781 
 782                         for (bf = s->p.finders;  bf != 0;  bf = bf->next) {
 783                                 nss_backend_constr_t c;
 784 
 785                                 c = (*bf->lookup) (bf->lookup_priv, s->p.name,
 786                                     src->lkp->service_name, &src->finder_priv);
 787                                 if (c != 0) {
 788                                         src->be_constr = c;
 789                                         src->finder = bf;
 790                                         break;
 791                                 }
 792                         }
 793                         if (src->be_constr == 0) {
 794                                 /* Couldn't find the backend anywhere */
 795                                 be = 0;
 796                                 break;
 797                         }
 798                 }
 799 
 800                 if (src->n_active < s->p.max_active_per_src) {
 801                         be = (*src->be_constr)(s->p.name,
 802                             src->lkp->service_name, 0 /* === unimplemented */);
 803                         if (be != 0) {
 804                                 src->n_active++;
 805                                 break;
 806                         } else if (src->n_active == 0) {
 807                                 /* Something's wrong;  we should be */
 808                                 /*   able to create at least one    */
 809                                 /*   instance of the backend        */
 810                                 break;
 811                         }
 812                         /*
 813                          * Else it's odd that we can't create another backend
 814                          *   instance, but don't sweat it;  instead, queue for
 815                          *   an existing backend instance.
 816                          */
 817                 }
 818 
 819                 src->n_waiting++;
 820                 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
 821                     &cancel_state);
 822                 (void) cond_wait(&src->wanna_be, &(*rootpp)->lock);
 823                 (void) pthread_setcancelstate(cancel_state, NULL);
 824                 NSS_CHECKROOT(rootpp, s);
 825                 src->n_waiting--;
 826 
 827                 /*
 828                  * Loop and see whether things got better for us, or whether
 829                  *   someone else got scheduled first and we have to try
 830                  *   this again.
 831                  *
 832                  * === ?? Should count iterations, assume bug if many ??
 833                  */
 834         }
 835         return (be);
 836 }
 837 
 838 static void
 839 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be)
 840 {
 841         struct nss_src_state    *src = &s->src[n_src];
 842 
 843         if (be == 0) {
 844                 return;
 845         }
 846 
 847         src->n_active--;
 848 
 849         if (src->n_dormant < s->p.max_dormant_per_src) {
 850                 if (s->p.max_dormant_per_src == 1) {
 851                         src->dormant.single = be;
 852                         src->n_dormant++;
 853                 } else if (src->dormant.multi != 0 ||
 854                     (src->dormant.multi =
 855                     libc_malloc(s->p.max_dormant_per_src *
 856                     sizeof (nss_backend_t *))) != NULL) {
 857                         src->dormant.multi[src->n_dormant] = be;
 858                         src->n_dormant++;
 859                 } else {
 860                         /* Can't store it, so toss it */
 861                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
 862                 }
 863         } else {
 864                 /* We've stored as many as we want, so toss it */
 865                 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
 866         }
 867         if (src->n_waiting > 0) {
 868                 (void) cond_signal(&src->wanna_be);
 869         }
 870 }
 871 
 872 static struct nss_db_state *
 873 _nss_db_state_constr(nss_db_initf_t initf)
 874 {
 875         struct nss_db_state     *s;
 876         struct __nsw_switchconfig_v1 *config = 0;
 877         struct __nsw_lookup_v1  *lkp;
 878         enum __nsw_parse_err    err;
 879         const char              *config_name;
 880         int                     n_src;
 881 
 882         if ((s = libc_malloc(sizeof (*s))) == 0) {
 883                 return (0);
 884         }
 885         (void) mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0);
 886 
 887         s->p.max_active_per_src      = 10;
 888         s->p.max_dormant_per_src = 1;
 889         s->p.finders = nss_default_finders;
 890         (*initf)(&s->p);
 891         if (s->p.name == 0) {
 892                 _nss_db_state_destr(s);
 893                 return (0);
 894         }
 895 
 896         if (!checked_env) {
 897 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
 898                 nss_get_environment();
 899                 checked_env = 1;
 900         }
 901 
 902         config_name = s->p.config_name ? s->p.config_name : s->p.name;
 903         if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
 904                 config = __nsw_getconfig_v1(config_name, &err);
 905                 /* === ? test err ? */
 906         }
 907         if (config == 0) {
 908                 /* getconfig failed, or frontend demanded default config */
 909 
 910                 char    *str;   /* _nsw_getoneconfig() clobbers its argument */
 911 
 912                 if ((str = libc_strdup(s->p.default_config)) != 0) {
 913                         config = _nsw_getoneconfig_v1(config_name, str, &err);
 914                         libc_free(str);
 915                 }
 916                 if (config == 0) {
 917                         _nss_db_state_destr(s);
 918                         return (0);
 919                 }
 920         }
 921         s->config = config;
 922         if ((s->max_src = config->num_lookups) <= 0 ||
 923             (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) {
 924                 _nss_db_state_destr(s);
 925                 return (0);
 926         }
 927         for (n_src = 0, lkp = config->lookups;
 928             n_src < s->max_src; n_src++, lkp = lkp->next) {
 929                 s->src[n_src].lkp = lkp;
 930                 (void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0);
 931         }
 932         s->refcount = 1;
 933         return (s);
 934 }
 935 
 936 void
 937 _nss_src_state_destr(struct nss_src_state *src, int max_dormant)
 938 {
 939         if (max_dormant == 1) {
 940                 if (src->n_dormant != 0) {
 941                         (void) NSS_INVOKE_DBOP(src->dormant.single,
 942                             NSS_DBOP_DESTRUCTOR, 0);
 943                 };
 944         } else if (src->dormant.multi != 0) {
 945                 int     n;
 946 
 947                 for (n = 0;  n < src->n_dormant;  n++) {
 948                         (void) NSS_INVOKE_DBOP(src->dormant.multi[n],
 949                             NSS_DBOP_DESTRUCTOR, 0);
 950                 }
 951                 libc_free(src->dormant.multi);
 952         }
 953 
 954         /* cond_destroy(&src->wanna_be); */
 955 
 956         if (src->finder != 0) {
 957                 (*src->finder->delete)(src->finder_priv, src->be_constr);
 958         }
 959 }
 960 
 961 /*
 962  * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
 963  *      nss_db_state structure.
 964  * Assumes that s has been ref-counted down to zero (in particular,
 965  *      rootp->s has already been dealt with).
 966  *
 967  * Nobody else holds a pointer to *s (if they did, refcount != 0),
 968  *   so we can clean up state *after* we drop the lock (also, by the
 969  *   time we finish freeing the state structures, the lock may have
 970  *   ceased to exist -- if we were using the orphan_root).
 971  */
 972 
 973 void
 974 _nss_db_state_destr(struct nss_db_state *s)
 975 {
 976 
 977         if (s == NULL)
 978                 return;
 979 
 980         /* === mutex_destroy(&s->orphan_root.lock); */
 981         if (s->p.cleanup != 0) {
 982                 (*s->p.cleanup)(&s->p);
 983         }
 984         if (s->config != 0) {
 985                 (void) __nsw_freeconfig_v1(s->config);
 986         }
 987         if (s->src != 0) {
 988                 int     n_src;
 989 
 990                 for (n_src = 0;  n_src < s->max_src;  n_src++) {
 991                         _nss_src_state_destr(&s->src[n_src],
 992                             s->p.max_dormant_per_src);
 993                 }
 994                 libc_free(s->src);
 995         }
 996         libc_free(s);
 997 }
 998 
 999 
1000 /*
1001  * _nss_status_vec() returns a bit vector of all status codes returned during
1002  * the most recent call to nss_search().
1003  * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
1004  * failure.
1005  * These functions are private.  Don't use them externally without discussing
1006  * it with the switch maintainers.
1007  */
1008 static uint_t *
1009 _nss_status_vec_p()
1010 {
1011         return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
1012 }
1013 
1014 unsigned int
1015 _nss_status_vec(void)
1016 {
1017         unsigned int *status_vec_p = _nss_status_vec_p();
1018 
1019         return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL));
1020 }
1021 
1022 static void
1023 output_loop_diag_a(
1024         int n,
1025         char *dbase,
1026         struct __nsw_lookup_v1 *lkp)
1027 {
1028         (void) fprintf(__nss_debug_file,
1029             "NSS_retry(%d): '%s': trying '%s' ... ",
1030             n, dbase, lkp->service_name);
1031         (void) fflush(__nss_debug_file);
1032 
1033 }
1034 
1035 static void
1036 output_loop_diag_b(
1037         nss_status_t res,
1038         struct __nsw_lookup_v1 *lkp)
1039 {
1040         (void) fprintf(__nss_debug_file, "result=");
1041         switch (res) {
1042         case NSS_SUCCESS:
1043                 (void) fprintf(__nss_debug_file, "SUCCESS");
1044                 break;
1045         case NSS_NOTFOUND:
1046                 (void) fprintf(__nss_debug_file, "NOTFOUND");
1047                 break;
1048         case NSS_UNAVAIL:
1049                 (void) fprintf(__nss_debug_file, "UNAVAIL");
1050                 break;
1051         case NSS_TRYAGAIN:
1052                 (void) fprintf(__nss_debug_file, "TRYAGAIN");
1053                 break;
1054         case NSS_NISSERVDNS_TRYAGAIN:
1055                 (void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN");
1056                 break;
1057         default:
1058                 (void) fprintf(__nss_debug_file, "undefined");
1059         }
1060         (void) fprintf(__nss_debug_file, ", action=");
1061         switch (lkp->actions[res]) {
1062         case __NSW_CONTINUE:
1063                 (void) fprintf(__nss_debug_file, "CONTINUE");
1064                 break;
1065         case  __NSW_RETURN:
1066                 (void) fprintf(__nss_debug_file, "RETURN");
1067                 break;
1068         case __NSW_TRYAGAIN_FOREVER:
1069                 (void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER");
1070                 break;
1071         case __NSW_TRYAGAIN_NTIMES:
1072                 (void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)",
1073                     lkp->max_retries);
1074                 break;
1075         case __NSW_TRYAGAIN_PAUSED:
1076                 (void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED");
1077                 break;
1078         default:
1079                 (void) fprintf(__nss_debug_file, "undefined");
1080         }
1081         (void) fprintf(__nss_debug_file, "\n");
1082 }
1083 
1084 #define NSS_BACKOFF(n, b, t) \
1085                         ((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
1086 
1087 static int
1088 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
1089 {
1090         if (res != NSS_TRYAGAIN && res !=  NSS_NISSERVDNS_TRYAGAIN) {
1091                 if (res == NSS_SUCCESS) {
1092                         __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
1093                         __NSW_UNPAUSE_ACTION(
1094                             lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
1095                 }
1096                 return (0);
1097         }
1098 
1099         if ((res == NSS_TRYAGAIN &&
1100             lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
1101             (res == NSS_NISSERVDNS_TRYAGAIN &&
1102             lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
1103                 return (1);
1104 
1105         if (res == NSS_TRYAGAIN &&
1106             lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1107                 if (n <= lkp->max_retries)
1108                         return (1);
1109                 else {
1110                         lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
1111                         return (0);
1112                 }
1113 
1114         if (res == NSS_NISSERVDNS_TRYAGAIN &&
1115             lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1116                 if (n <= lkp->max_retries)
1117                         return (1);
1118                 else {
1119                         lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
1120                             __NSW_TRYAGAIN_PAUSED;
1121                         return (0);
1122                 }
1123 
1124         return (0);
1125 }
1126 
1127 /*
1128  * Switch policy component functional interfaces
1129  */
1130 
1131 void
1132 nss_delete(nss_db_root_t *rootp)
1133 {
1134         struct nss_db_state     *s;
1135 
1136         /* no name service cache daemon divert here */
1137         /* local nss_delete decrements state reference counts */
1138         /* and may free up opened switch resources. */
1139 
1140         NSS_ROOTLOCK(rootp, &s);
1141         if (s == 0) {
1142                 NSS_UNLOCK(rootp);
1143         } else {
1144                 rootp->s = 0;
1145                 NSS_UNREF_UNLOCK(rootp, s);
1146         }
1147 }
1148 
1149 nss_status_t
1150 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1151     void *search_args)
1152 {
1153         nss_status_t            res = NSS_UNAVAIL;
1154         struct nss_db_state     *s;
1155         int                     n_src;
1156         unsigned int            *status_vec_p;
1157 
1158         /* name service cache daemon divert */
1159         res = _nsc_search(rootp, initf, search_fnum, search_args);
1160         if (res != NSS_TRYLOCAL)
1161                 return (res);
1162 
1163         /* fall through - process locally */
1164         errno = 0;                      /* just in case ... */
1165         res = NSS_UNAVAIL;
1166         status_vec_p = _nss_status_vec_p();
1167 
1168         if (status_vec_p == NULL) {
1169                 return (NSS_UNAVAIL);
1170         }
1171         *status_vec_p = 0;
1172 
1173         NSS_LOCK_CHECK(rootp, initf, &s);
1174         if (s == 0) {
1175                 NSS_UNLOCK(rootp);
1176                 return (res);
1177         }
1178         NSS_STATE_REF_u(s);
1179 
1180         for (n_src = 0;  n_src < s->max_src;  n_src++) {
1181                 nss_backend_t           *be;
1182                 nss_backend_op_t        funcp;
1183 
1184                 res = NSS_UNAVAIL;
1185                 if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) {
1186                         if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) {
1187                                 int n_loop = 0;
1188                                 int no_backoff = 19;
1189                                 int max_backoff = 5;    /* seconds */
1190 
1191                                 do {
1192                                         /*
1193                                          * Backend operation may take a while;
1194                                          * drop the lock so we don't serialize
1195                                          * more than necessary.
1196                                          */
1197                                         NSS_UNLOCK(rootp);
1198 
1199                                         /* After several tries, backoff... */
1200                                         if (n_loop > no_backoff) {
1201                                                 if (__nss_debug_eng_loop > 1)
1202                                                         (void) fprintf(
1203                                                             __nss_debug_file,
1204                                                             "NSS: loop: "
1205                                                             "sleeping %d ...\n",
1206                                                             NSS_BACKOFF(n_loop,
1207                                                             no_backoff,
1208                                                             max_backoff));
1209 
1210                                                 (void) sleep(NSS_BACKOFF(n_loop,
1211                                                     no_backoff, max_backoff));
1212                                         }
1213 
1214                                         if (__nss_debug_eng_loop)
1215                                                 output_loop_diag_a(n_loop,
1216                                                     s->config->dbase,
1217                                                     s->src[n_src].lkp);
1218 
1219 
1220                                         res = (*funcp)(be, search_args);
1221                                         NSS_RELOCK(&rootp, s);
1222                                         n_loop++;
1223                                         if (__nss_debug_eng_loop)
1224                                                 output_loop_diag_b(res,
1225                                                     s->src[n_src].lkp);
1226                                 } while (retry_test(res, n_loop,
1227                                     s->src[n_src].lkp));
1228                         }
1229                         nss_put_backend_u(s, n_src, be);
1230                 }
1231                 *status_vec_p |= (1 << res);
1232                 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1233                         if (__nss_debug_eng_loop)
1234                                 (void) fprintf(__nss_debug_file,
1235                                     "NSS: '%s': return.\n",
1236                                     s->config->dbase);
1237                         break;
1238                 } else
1239                         if (__nss_debug_eng_loop)
1240                                 (void) fprintf(__nss_debug_file,
1241                                     "NSS: '%s': continue ...\n",
1242                                     s->config->dbase);
1243         }
1244         NSS_UNREF_UNLOCK(rootp, s);
1245         return (res);
1246 }
1247 
1248 
1249 /*
1250  * Start of nss_{setent|getent|endent}
1251  */
1252 
1253 /*
1254  * State (here called "context") for one setent/getent.../endent sequence.
1255  *   In principle there could be multiple contexts active for a single
1256  *   database;  in practice, since Posix and UI have helpfully said that
1257  *   getent() state is global rather than, say, per-thread or user-supplied,
1258  *   we have at most one of these per nss_db_state.
1259  *   XXX ? Is this statement still true?
1260  *
1261  * NSS2 - a client's context is maintained as a cookie delivered by and
1262  * passed to nscd.  The cookie is a 64 bit (nssuint_t) unique opaque value
1263  * created by nscd.
1264  * cookie states:
1265  *      NSCD_NEW_COOKIE         - cookie value uninitialized
1266  *      NSCD_LOCAL_COOKIE       - setent is a local setent
1267  *      all other               - NSCD unique opaque id for this setent
1268  * A client's context is also associated with a seq_num.  This is a nscd
1269  * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
1270  * to validate the sequencing of the context.  The client treats this as
1271  * a pass through value.
1272  *
1273  * XXX ??  Use Cookie as cross-check info so that we can detect an
1274  * nss_context that missed an nss_delete() or similar.
1275  */
1276 
1277 struct nss_getent_context {
1278         int                     n_src;  /* >= max_src ==> end of sequence */
1279         nss_backend_t           *be;
1280         struct nss_db_state     *s;
1281         nssuint_t               cookie;
1282         nssuint_t               seq_num;
1283         nssuint_t               cookie_setent;
1284         nss_db_params_t         param;
1285 };
1286 
1287 static void             nss_setent_u(nss_db_root_t *,
1288                                     nss_db_initf_t,
1289                                     nss_getent_t *);
1290 static nss_status_t     nss_getent_u(nss_db_root_t *,
1291                                     nss_db_initf_t,
1292                                     nss_getent_t *,
1293                                     void *);
1294 static void             nss_endent_u(nss_db_root_t *,
1295                                     nss_db_initf_t,
1296                                     nss_getent_t *);
1297 
1298 void
1299 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1300 {
1301         if (contextpp == 0) {
1302                 return;
1303         }
1304         cancel_safe_mutex_lock(&contextpp->lock);
1305         nss_setent_u(rootp, initf, contextpp);
1306         cancel_safe_mutex_unlock(&contextpp->lock);
1307 }
1308 
1309 nss_status_t
1310 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1311     void *args)
1312 {
1313         nss_status_t            status;
1314 
1315         if (contextpp == 0) {
1316                 return (NSS_UNAVAIL);
1317         }
1318         cancel_safe_mutex_lock(&contextpp->lock);
1319         status = nss_getent_u(rootp, initf, contextpp, args);
1320         cancel_safe_mutex_unlock(&contextpp->lock);
1321         return (status);
1322 }
1323 
1324 void
1325 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1326 {
1327         if (contextpp == 0) {
1328                 return;
1329         }
1330         cancel_safe_mutex_lock(&contextpp->lock);
1331         nss_endent_u(rootp, initf, contextpp);
1332         cancel_safe_mutex_unlock(&contextpp->lock);
1333 }
1334 
1335 /*
1336  * Each of the _u versions of the nss interfaces assume that the context
1337  * lock is held.  No need to divert to nscd.  Private to local sequencing.
1338  */
1339 
1340 static void
1341 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1342 {
1343         struct nss_db_state     *s;
1344         nss_backend_t           *be;
1345         int                     n_src;
1346 
1347         s = contextp->s;
1348         n_src = contextp->n_src;
1349         be = contextp->be;
1350 
1351         if (s != 0) {
1352                 if (n_src < s->max_src && be != 0) {
1353                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1354                         NSS_RELOCK(&rootp, s);
1355                         nss_put_backend_u(s, n_src, be);
1356                         contextp->be = 0;  /* Should be unnecessary, but hey */
1357                         NSS_UNREF_UNLOCK(rootp, s);
1358                 }
1359                 contextp->s = 0;
1360         }
1361 }
1362 
1363 static void
1364 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1365     nss_getent_t *contextpp)
1366 {
1367         nss_status_t            status;
1368         struct nss_db_state     *s;
1369         struct nss_getent_context *contextp;
1370         nss_backend_t           *be;
1371         int                     n_src;
1372 
1373         /* setup process wide context while locked */
1374         if ((contextp = contextpp->ctx) == 0) {
1375                 if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
1376                         return;
1377                 }
1378                 contextpp->ctx = contextp;
1379                 contextp->cookie = NSCD_NEW_COOKIE;  /* cookie init */
1380                 contextp->seq_num = 0;                       /* seq_num init */
1381                 s = 0;
1382         } else {
1383                 s = contextp->s;
1384                 if (contextp->cookie != NSCD_LOCAL_COOKIE)
1385                         contextp->cookie = NSCD_NEW_COOKIE;
1386         }
1387 
1388         /* name service cache daemon divert */
1389         if (contextp->cookie == NSCD_NEW_COOKIE) {
1390                 status = _nsc_setent_u(rootp, initf, contextpp);
1391                 if (status != NSS_TRYLOCAL)
1392                         return;
1393         }
1394 
1395         /* fall through - process locally */
1396         if (s == 0) {
1397                 NSS_LOCK_CHECK(rootp, initf, &s);
1398                 if (s == 0) {
1399                         /* Couldn't set up state, so quit */
1400                         NSS_UNLOCK(rootp);
1401                         /* ==== is there any danger of not having done an */
1402                         /* end_iter() here, and hence of losing backends? */
1403                         contextpp->ctx = 0;
1404                         libc_free(contextp);
1405                         return;
1406                 }
1407                 NSS_STATE_REF_u(s);
1408                 contextp->s = s;
1409         } else {
1410                 s       = contextp->s;
1411                 n_src   = contextp->n_src;
1412                 be      = contextp->be;
1413                 if (n_src == 0 && be != 0) {
1414                         /*
1415                          * Optimization:  don't do endent, don't change
1416                          *   backends, just do the setent.  Look Ma, no locks
1417                          *   (nor any context that needs updating).
1418                          */
1419                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1420                         return;
1421                 }
1422                 if (n_src < s->max_src && be != 0) {
1423                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1424                         NSS_RELOCK(&rootp, s);
1425                         nss_put_backend_u(s, n_src, be);
1426                         contextp->be = 0;    /* Play it safe */
1427                 } else {
1428                         NSS_RELOCK(&rootp, s);
1429                 }
1430         }
1431         for (n_src = 0, be = 0; n_src < s->max_src &&
1432             (be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
1433                 ;
1434         }
1435         NSS_UNLOCK(rootp);
1436 
1437         contextp->n_src      = n_src;
1438         contextp->be = be;
1439 
1440         if (be == 0) {
1441                 /* Things are broken enough that we can't do setent/getent */
1442                 nss_endent_u(rootp, initf, contextpp);
1443                 return;
1444         }
1445         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1446 }
1447 
1448 static nss_status_t
1449 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1450     nss_getent_t *contextpp, void *args)
1451 {
1452         nss_status_t            status;
1453         struct nss_db_state     *s;
1454         struct nss_getent_context *contextp;
1455         int                     n_src;
1456         nss_backend_t           *be;
1457 
1458         if ((contextp = contextpp->ctx) == 0) {
1459                 nss_setent_u(rootp, initf, contextpp);
1460                 if ((contextp = contextpp->ctx) == 0) {
1461                         /* Give up */
1462                         return (NSS_UNAVAIL);
1463                 }
1464         }
1465         /* name service cache daemon divert */
1466         status = _nsc_getent_u(rootp, initf, contextpp, args);
1467         if (status != NSS_TRYLOCAL)
1468                 return (status);
1469 
1470         /* fall through - process locally */
1471         s       = contextp->s;
1472         n_src   = contextp->n_src;
1473         be      = contextp->be;
1474 
1475         if (s == 0) {
1476                 /*
1477                  * We've done an end_iter() and haven't done nss_setent()
1478                  * or nss_endent() since;  we should stick in this state
1479                  * until the caller invokes one of those two routines.
1480                  */
1481                 return (NSS_SUCCESS);
1482         }
1483 
1484         while (n_src < s->max_src) {
1485                 nss_status_t res;
1486 
1487                 if (be == 0) {
1488                         /* If it's null it's a bug, but let's play safe */
1489                         res = NSS_UNAVAIL;
1490                 } else {
1491                         res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1492                 }
1493 
1494                 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1495                         if (res != __NSW_SUCCESS) {
1496                                 end_iter_u(rootp, contextp);
1497                         }
1498                         return (res);
1499                 }
1500                 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1501                 NSS_RELOCK(&rootp, s);
1502                 nss_put_backend_u(s, n_src, be);
1503                 do {
1504                         n_src++;
1505                 } while (n_src < s->max_src &&
1506                     (be = nss_get_backend_u(&rootp, s, n_src)) == 0);
1507                 contextp->be = be;
1508                 if (be == 0) {
1509                         /*
1510                          * This is the case where we failed to get the backend
1511                          * for the last source. We exhausted all sources.
1512                          *
1513                          * We need to do cleanup ourselves because end_iter_u()
1514                          * does not do it for be == 0.
1515                          */
1516                         NSS_UNREF_UNLOCK(rootp, s);
1517                         contextp->s = 0;
1518                         break;
1519                 } else {
1520                         NSS_UNLOCK(rootp);
1521                         contextp->n_src = n_src;
1522                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1523                 }
1524         }
1525         /* Got to the end of the sources without finding another entry */
1526         end_iter_u(rootp, contextp);
1527         return (NSS_SUCCESS);
1528         /* success is either a successful entry or end of the sources */
1529 }
1530 
1531 /*ARGSUSED*/
1532 static void
1533 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1534     nss_getent_t *contextpp)
1535 {
1536         nss_status_t            status;
1537         struct nss_getent_context *contextp;
1538 
1539         if ((contextp = contextpp->ctx) == 0) {
1540                 /* nss_endent() on an unused context is a no-op */
1541                 return;
1542         }
1543 
1544         /* notify name service cache daemon */
1545         status = _nsc_endent_u(rootp, initf, contextpp);
1546         if (status != NSS_TRYLOCAL) {
1547                 /* clean up */
1548                 libc_free(contextp);
1549                 contextpp->ctx = 0;
1550                 return;
1551         }
1552 
1553         /* fall through - process locally */
1554 
1555         /*
1556          * Existing code (BSD, SunOS) works in such a way that getXXXent()
1557          *   following an endXXXent() behaves as though the user had invoked
1558          *   setXXXent(), i.e. it iterates properly from the beginning.
1559          * We'd better not break this, so our choices are
1560          *      (1) leave the context structure around, and do nss_setent or
1561          *          something equivalent,
1562          *   or (2) free the context completely, and rely on the code in
1563          *          nss_getent() that makes getXXXent() do the right thing
1564          *          even without a preceding setXXXent().
1565          * The code below does (2), which frees up resources nicely but will
1566          * cost more if the user then does more getXXXent() operations.
1567          * Moral:  for efficiency, don't call endXXXent() prematurely.
1568          */
1569         end_iter_u(rootp, contextp);
1570         libc_free(contextp);
1571         contextpp->ctx = 0;
1572 }
1573 
1574 /*
1575  * pack dbd data into header
1576  * Argment pointers assumed valid.
1577  * poff offset position pointer
1578  *   IN = starting offset for dbd header
1579  *   OUT = starting offset for next section
1580  */
1581 
1582 static nss_status_t
1583 nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff)
1584 {
1585         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1586         nss_dbd_t               *pdbd;
1587         size_t                  off = *poff;
1588         size_t                  len, blen;
1589         size_t                  n, nc, dc;
1590         char                    *bptr;
1591 
1592         pbuf->dbd_off = (nssuint_t)off;
1593         bptr = (char *)buffer + off;
1594         blen = bufsize - off;
1595         len = sizeof (nss_dbd_t);
1596 
1597         n = nc = dc = 0;
1598         if (p->name == NULL) {
1599                 errno = ERANGE;                 /* actually EINVAL */
1600                 return (NSS_ERROR);
1601         }
1602 
1603         /* if default config not specified, the flag should be reset */
1604         if (p->default_config == NULL) {
1605                 p->default_config = "<NULL>";
1606                 p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG;
1607         }
1608 
1609         n = strlen(p->name) + 1;
1610         dc = strlen(p->default_config) + 1;
1611         if (n < 2 || dc < 2) {                    /* What no DB? */
1612                 errno = ERANGE;                 /* actually EINVAL */
1613                 return (NSS_ERROR);
1614         }
1615         if (p->config_name != NULL) {
1616                 nc = strlen(p->config_name) + 1;
1617         }
1618         if ((len + n + nc + dc) >= blen) {
1619                 errno = ERANGE;                 /* actually EINVAL */
1620                 return (NSS_ERROR);
1621         }
1622 
1623         pdbd = (nss_dbd_t *)((void *)bptr);
1624         bptr += len;
1625         pdbd->flags = p->flags;
1626         pdbd->o_name = len;
1627         (void) strlcpy(bptr, p->name, n);
1628         len += n;
1629         bptr += n;
1630         if (nc == 0) {
1631                 pdbd->o_config_name = 0;
1632         } else {
1633                 pdbd->o_config_name = len;
1634                 (void) strlcpy(bptr, p->config_name, nc);
1635                 bptr += nc;
1636                 len += nc;
1637         }
1638         pdbd->o_default_config = len;
1639         (void) strlcpy(bptr, p->default_config, dc);
1640         len += dc;
1641         pbuf->dbd_len = (nssuint_t)len;
1642         off += ROUND_UP(len, sizeof (nssuint_t));
1643         *poff = off;
1644         return (NSS_SUCCESS);
1645 }
1646 
1647 /*
1648  * Switch packed and _nsc (switch->nscd) interfaces
1649  * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1650  */
1651 
1652 /*ARGSUSED*/
1653 nss_status_t
1654 nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1655     nss_db_initf_t initf, int search_fnum, void *search_args)
1656 {
1657         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1658         nss_XbyY_args_t         *in = (nss_XbyY_args_t *)search_args;
1659         nss_db_params_t         tparam = { 0 };
1660         nss_status_t            ret = NSS_ERROR;
1661         const char              *dbn;
1662         size_t                  blen, len, off = 0;
1663         char                    *bptr;
1664         struct nss_groupsbymem  *gbm;
1665 
1666         if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) {
1667                 errno = ERANGE;                 /* actually EINVAL */
1668                 return (ret);
1669         }
1670         tparam.cleanup = NULL;
1671         (*initf)(&tparam);
1672         if ((dbn = tparam.name) == 0) {
1673                 if (tparam.cleanup != 0)
1674                         (tparam.cleanup)(&tparam);
1675                 errno = ERANGE;                 /* actually EINVAL */
1676                 return (ret);
1677         }
1678 
1679         /* init buffer header */
1680         pbuf->pbufsiz = (nssuint_t)bufsize;
1681         pbuf->p_ruid = (uint32_t)getuid();
1682         pbuf->p_euid = (uint32_t)geteuid();
1683         pbuf->p_version = NSCD_HEADER_REV;
1684         pbuf->p_status = 0;
1685         pbuf->p_errno = 0;
1686         pbuf->p_herrno = 0;
1687 
1688         /* possible audituser init */
1689         if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0)
1690                 pbuf->p_herrno = (uint32_t)in->h_errno;
1691 
1692         pbuf->libpriv = 0;
1693 
1694         off = sizeof (nss_pheader_t);
1695 
1696         /* setup getXbyY operation - database and sub function */
1697         pbuf->nss_dbop = (uint32_t)search_fnum;
1698         ret = nss_pack_dbd(buffer, bufsize, &tparam, &off);
1699         if (ret != NSS_SUCCESS) {
1700                 errno = ERANGE;                 /* actually EINVAL */
1701                 return (ret);
1702         }
1703         ret = NSS_ERROR;
1704         /* setup request key */
1705         pbuf->key_off = (nssuint_t)off;
1706         bptr = (char *)buffer + off;
1707         blen = bufsize - off;
1708         /* use key2str if provided, else call default getXbyY packer */
1709         if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1710                 /* This has to run locally due to backend knowledge */
1711                 if (search_fnum == NSS_DBOP_NETGROUP_SET) {
1712                         errno = 0;
1713                         return (NSS_TRYLOCAL);
1714                 }
1715                 /* use default packer for known getXbyY ops */
1716                 ret = nss_default_key2str(bptr, blen, in, dbn,
1717                     search_fnum, &len);
1718         } else if (in->key2str == NULL ||
1719             (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1720             strcmp(dbn, NSS_DBNAM_GROUP) == 0)) {
1721                 /* use default packer for known getXbyY ops */
1722                 ret = nss_default_key2str(bptr, blen, in, dbn,
1723                     search_fnum, &len);
1724         } else {
1725                 ret = (*in->key2str)(bptr, blen, &in->key, &len);
1726         }
1727         if (tparam.cleanup != 0)
1728                 (tparam.cleanup)(&tparam);
1729         if (ret != NSS_SUCCESS) {
1730                 errno = ERANGE;                 /* actually ENOMEM */
1731                 return (ret);
1732         }
1733         pbuf->key_len = (nssuint_t)len;
1734         off += ROUND_UP(len, sizeof (nssuint_t));
1735 
1736         pbuf->data_off = (nssuint_t)off;
1737         pbuf->data_len = (nssuint_t)(bufsize - off);
1738         /*
1739          * Prime data return with first result if
1740          * the first result is passed in
1741          * [_getgroupsbymember oddness]
1742          */
1743         gbm = (struct nss_groupsbymem *)search_args;
1744         if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1745             strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) {
1746                 gid_t   *gidp;
1747                 gidp = (gid_t *)((void *)((char *)buffer + off));
1748                 *gidp = gbm->gid_array[0];
1749         }
1750 
1751         errno = 0;                              /* just in case ... */
1752         return (NSS_SUCCESS);
1753 }
1754 
1755 /*
1756  * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1757  * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1758  */
1759 
1760 /*ARGSUSED*/
1761 nss_status_t
1762 nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1763     nss_db_initf_t initf, nss_getent_t *contextpp)
1764 {
1765         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1766         struct nss_getent_context *contextp = contextpp->ctx;
1767         nss_status_t            ret = NSS_ERROR;
1768         size_t                  blen, len = 0, off = 0;
1769         char                    *bptr;
1770         nssuint_t               *nptr;
1771 
1772         if (pbuf == NULL || initf == (nss_db_initf_t)NULL) {
1773                 errno = ERANGE;                 /* actually EINVAL */
1774                 return (ret);
1775         }
1776 
1777         /* init buffer header */
1778         pbuf->pbufsiz = (nssuint_t)bufsize;
1779         pbuf->p_ruid = (uint32_t)getuid();
1780         pbuf->p_euid = (uint32_t)geteuid();
1781         pbuf->p_version = NSCD_HEADER_REV;
1782         pbuf->p_status = 0;
1783         pbuf->p_errno = 0;
1784         pbuf->p_herrno = 0;
1785         pbuf->libpriv = 0;
1786 
1787         off = sizeof (nss_pheader_t);
1788 
1789         /* setup getXXXent operation - database and sub function */
1790         pbuf->nss_dbop = (uint32_t)0;        /* iterators have no dbop */
1791         ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off);
1792         if (ret != NSS_SUCCESS) {
1793                 errno = ERANGE;                 /* actually EINVAL */
1794                 return (ret);
1795         }
1796         ret = NSS_ERROR;
1797         off += ROUND_UP(len, sizeof (nssuint_t));
1798 
1799         pbuf->key_off = (nssuint_t)off;
1800         bptr = (char *)buffer + off;
1801         blen = bufsize - off;
1802         len = (size_t)(sizeof (nssuint_t) * 2);
1803         if (len >= blen) {
1804                 errno = ERANGE;                 /* actually EINVAL */
1805                 return (ret);
1806         }
1807         nptr = (nssuint_t *)((void *)bptr);
1808         *nptr++ = contextp->cookie;
1809         *nptr = contextp->seq_num;
1810         pbuf->key_len = (nssuint_t)len;
1811 
1812         off += len;
1813         pbuf->data_off = (nssuint_t)off;
1814         pbuf->data_len = (nssuint_t)(bufsize - off);
1815         return (NSS_SUCCESS);
1816 }
1817 
1818 /*
1819  * Unpack packed arguments buffer
1820  * Return: status, errnos and results from requested operation.
1821  *
1822  * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1823  * or via the backwards compatibility interfaces then the standard
1824  * str2group API is used in conjunction with process_cstr.  When,
1825  * processing a returned buffer, in NSS2 the return results are the
1826  * already digested groups array.  Therefore, unpack the digested results
1827  * back to the return buffer.
1828  *
1829  * Note: the digested results are nssuint_t quantities.  _getgroupsbymember
1830  * digests int quantities.  Therefore convert.  Assume input is in nssuint_t
1831  * quantities.  Store in an int array... Assume gid's are <= 32 bits...
1832  */
1833 
1834 /*ARGSUSED*/
1835 nss_status_t
1836 nss_unpack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1837     nss_db_initf_t initf, int search_fnum, void *search_args)
1838 {
1839         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1840         nss_XbyY_args_t         *in = (nss_XbyY_args_t *)search_args;
1841         nss_dbd_t               *pdbd;
1842         char                    *dbn;
1843         nss_status_t            status;
1844         char                    *buf;
1845         int                     len;
1846         int                     ret;
1847         int                     i;
1848         int                     fmt_type;
1849         gid_t                   *gidp;
1850         gid_t                   *gptr;
1851         struct nss_groupsbymem  *arg;
1852 
1853 
1854         if (pbuf == NULL || in == NULL)
1855                 return (-1);
1856         status = pbuf->p_status;
1857         /* Identify odd cases */
1858         pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
1859         dbn = (char *)pdbd + pdbd->o_name;
1860         fmt_type = 0; /* nss_XbyY_args_t */
1861         if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1862             strcmp(dbn, NSS_DBNAM_GROUP) == 0)
1863                 fmt_type = 1; /* struct nss_groupsbymem */
1864         else if (search_fnum == NSS_DBOP_NETGROUP_IN &&
1865             strcmp(dbn, NSS_DBNAM_NETGROUP) == 0)
1866                 fmt_type = 2; /* struct nss_innetgr_args */
1867 
1868         /* if error - door's switch error */
1869         /* extended data could contain additional information? */
1870         if (status != NSS_SUCCESS) {
1871                 if (fmt_type == 0) {
1872                         in->h_errno = (int)pbuf->p_herrno;
1873                         if (pbuf->p_errno == ERANGE)
1874                                 in->erange = 1;
1875                 }
1876                 return (status);
1877         }
1878 
1879         if (pbuf->data_off == 0 || pbuf->data_len == 0)
1880                 return (NSS_NOTFOUND);
1881 
1882         buf = (char *)buffer + pbuf->data_off;
1883         len = pbuf->data_len;
1884 
1885         /* sidestep odd cases */
1886         if (fmt_type == 1) {
1887                 arg = (struct nss_groupsbymem *)in;
1888                 /* copy returned gid array from returned nscd buffer */
1889                 i = len / sizeof (gid_t);
1890                 /* not enough buffer */
1891                 if (i > arg->maxgids) {
1892                         i = arg->maxgids;
1893                 }
1894                 arg->numgids = i;
1895                 gidp = arg->gid_array;
1896                 gptr = (gid_t *)((void *)buf);
1897                 (void) memcpy(gidp, gptr, len);
1898                 return (NSS_SUCCESS);
1899         }
1900         if (fmt_type == 2) {
1901                 struct nss_innetgr_args *arg = (struct nss_innetgr_args *)in;
1902 
1903                 if (pbuf->p_status == NSS_SUCCESS) {
1904                         arg->status = NSS_NETGR_FOUND;
1905                         return (NSS_SUCCESS);
1906                 } else {
1907                         arg->status = NSS_NETGR_NO;
1908                         return (NSS_NOTFOUND);
1909                 }
1910         }
1911 
1912         /* process the normal cases */
1913         /* marshall data directly into users buffer */
1914         ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1915             in->buf.buflen);
1916         if (ret == NSS_STR_PARSE_ERANGE) {
1917                 in->returnval = 0;
1918                 in->returnlen = 0;
1919                 in->erange    = 1;
1920                 ret = NSS_NOTFOUND;
1921         } else if (ret == NSS_STR_PARSE_SUCCESS) {
1922                 in->returnval = in->buf.result;
1923                 in->returnlen =  len;
1924                 ret = NSS_SUCCESS;
1925         }
1926         in->h_errno = (int)pbuf->p_herrno;
1927         return ((nss_status_t)ret);
1928 }
1929 
1930 /*
1931  * Unpack a returned packed {set,get,end}ent arguments buffer
1932  * Return: status, errnos, cookie info and results from requested operation.
1933  */
1934 
1935 /*ARGSUSED*/
1936 nss_status_t
1937 nss_unpack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1938     nss_db_initf_t initf, nss_getent_t *contextpp, void *args)
1939 {
1940         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1941         nss_XbyY_args_t         *in = (nss_XbyY_args_t *)args;
1942         struct nss_getent_context *contextp = contextpp->ctx;
1943         nssuint_t               *nptr;
1944         nssuint_t               cookie;
1945         nss_status_t            status;
1946         char                    *buf;
1947         int                     len;
1948         int                     ret;
1949 
1950         if (pbuf == NULL)
1951                 return (-1);
1952         status = pbuf->p_status;
1953         /* if error - door's switch error */
1954         /* extended data could contain additional information? */
1955         if (status != NSS_SUCCESS)
1956                 return (status);
1957 
1958         /* unpack assigned cookie from SET/GET/END request */
1959         if (pbuf->key_off == 0 ||
1960             pbuf->key_len != (sizeof (nssuint_t) * 2))
1961                 return (NSS_NOTFOUND);
1962 
1963         nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off));
1964         cookie = contextp->cookie;
1965         if (cookie != NSCD_NEW_COOKIE && cookie != contextp->cookie_setent &&
1966             cookie != *nptr) {
1967                 /*
1968                  * Should either be new, or the cookie returned by the last
1969                  * setent (i.e., this is the first getent after the setent)
1970                  * or a match, else error
1971                  */
1972                 return (NSS_NOTFOUND);
1973         }
1974         /* save away for the next ent request */
1975         contextp->cookie = *nptr++;
1976         contextp->seq_num = *nptr;
1977 
1978         /* All done if no marshalling is expected {set,end}ent */
1979         if (args == NULL)
1980                 return (NSS_SUCCESS);
1981 
1982         /* unmarshall the data */
1983         if (pbuf->data_off == 0 || pbuf->data_len == 0)
1984                 return (NSS_NOTFOUND);
1985         buf = (char *)buffer + pbuf->data_off;
1986 
1987         len = pbuf->data_len;
1988 
1989         /* marshall data directly into users buffer */
1990         ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1991             in->buf.buflen);
1992         if (ret == NSS_STR_PARSE_ERANGE) {
1993                 in->returnval = 0;
1994                 in->returnlen = 0;
1995                 in->erange    = 1;
1996         } else if (ret == NSS_STR_PARSE_SUCCESS) {
1997                 in->returnval = in->buf.result;
1998                 in->returnlen =  len;
1999         }
2000         in->h_errno = (int)pbuf->p_herrno;
2001         return ((nss_status_t)ret);
2002 }
2003 
2004 /*
2005  * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
2006  */
2007 
2008 nss_status_t
2009 _nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
2010     void *search_args)
2011 {
2012         nss_pheader_t           *pbuf;
2013         void                    *doorptr = NULL;
2014         size_t                  bufsize = 0;
2015         size_t                  datasize = 0;
2016         nss_status_t            status;
2017 
2018         if (_nsc_proc_is_cache() > 0) {
2019                 /* internal nscd call - don't use the door */
2020                 return (NSS_TRYLOCAL);
2021         }
2022 
2023         /* standard client calls nscd code */
2024         if (search_args == NULL)
2025                 return (NSS_NOTFOUND);
2026 
2027         /* get the door buffer  & configured size */
2028         bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen;
2029         if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2030                 return (NSS_TRYLOCAL);
2031         if (doorptr == NULL || bufsize == 0)
2032                 return (NSS_TRYLOCAL);
2033 
2034         pbuf = (nss_pheader_t *)doorptr;
2035         /* pack argument and request into door buffer */
2036         pbuf->nsc_callnumber = NSCD_SEARCH;
2037         /* copy relevant door request info into door buffer */
2038         status = nss_pack((void *)pbuf, bufsize, rootp,
2039             initf, search_fnum, search_args);
2040 
2041         /* Packing error return error results */
2042         if (status != NSS_SUCCESS)
2043                 return (status);
2044 
2045         /* transfer packed switch request to nscd via door */
2046         /* data_off can be used because it is header+dbd_len+key_len */
2047         datasize = pbuf->data_off;
2048         status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2049 
2050         /* If unsuccessful fallback to standard nss logic */
2051         if (status != NSS_SUCCESS) {
2052                 /*
2053                  * check if doors reallocated the memory underneath us
2054                  * if they did munmap it or suffer a memory leak
2055                  */
2056                 if (doorptr != (void *)pbuf) {
2057                         _nsc_resizedoorbuf(bufsize);
2058                         (void) munmap((void *)doorptr, bufsize);
2059                 }
2060                 return (NSS_TRYLOCAL);
2061         }
2062 
2063         /* unpack and marshall data/errors to user structure */
2064         /* set any error conditions */
2065         status = nss_unpack((void *)doorptr, bufsize, rootp, initf,
2066             search_fnum, search_args);
2067         /*
2068          * check if doors reallocated the memory underneath us
2069          * if they did munmap it or suffer a memory leak
2070          */
2071         if (doorptr != (void *)pbuf) {
2072                 _nsc_resizedoorbuf(bufsize);
2073                 (void) munmap((void *)doorptr, bufsize);
2074         }
2075         return (status);
2076 }
2077 
2078 /*
2079  * contact nscd for a cookie or to reset an existing cookie
2080  * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2081  * continue diverting to local
2082  */
2083 nss_status_t
2084 _nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2085     nss_getent_t *contextpp)
2086 {
2087         nss_status_t            status = NSS_TRYLOCAL;
2088         struct nss_getent_context *contextp = contextpp->ctx;
2089         nss_pheader_t           *pbuf;
2090         void                    *doorptr = NULL;
2091         size_t                  bufsize = 0;
2092         size_t                  datasize = 0;
2093 
2094         /* return if already in local mode */
2095         if (contextp->cookie == NSCD_LOCAL_COOKIE)
2096                 return (NSS_TRYLOCAL);
2097 
2098         if (_nsc_proc_is_cache() > 0) {
2099                 /* internal nscd call - don't try to use the door */
2100                 contextp->cookie = NSCD_LOCAL_COOKIE;
2101                 return (NSS_TRYLOCAL);
2102         }
2103 
2104         /* get the door buffer & configured size */
2105         if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) {
2106                 contextp->cookie = NSCD_LOCAL_COOKIE;
2107                 return (NSS_TRYLOCAL);
2108         }
2109         if (doorptr == NULL || bufsize == 0) {
2110                 contextp->cookie = NSCD_LOCAL_COOKIE;
2111                 return (NSS_TRYLOCAL);
2112         }
2113 
2114         pbuf = (nss_pheader_t *)doorptr;
2115         pbuf->nsc_callnumber = NSCD_SETENT;
2116 
2117         contextp->param.cleanup = NULL;
2118         (*initf)(&contextp->param);
2119         if (contextp->param.name == 0) {
2120                 if (contextp->param.cleanup != 0)
2121                         (contextp->param.cleanup)(&contextp->param);
2122                 errno = ERANGE;                 /* actually EINVAL */
2123                 return (NSS_ERROR);
2124         }
2125 
2126         /* pack relevant setent request info into door buffer */
2127         status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2128         if (status != NSS_SUCCESS)
2129                 return (status);
2130 
2131         /* transfer packed switch request to nscd via door */
2132         /* data_off can be used because it is header+dbd_len+key_len */
2133         datasize = pbuf->data_off;
2134         status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2135 
2136         /* If fallback to standard nss logic (door failure) if possible */
2137         if (status != NSS_SUCCESS) {
2138                 if (contextp->cookie == NSCD_NEW_COOKIE) {
2139                         contextp->cookie = NSCD_LOCAL_COOKIE;
2140                         return (NSS_TRYLOCAL);
2141                 }
2142                 return (NSS_UNAVAIL);
2143         }
2144         /* unpack returned cookie stash it away */
2145         status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2146             initf, contextpp, NULL);
2147         /* save the setent cookie for later use */
2148         contextp->cookie_setent = contextp->cookie;
2149         /*
2150          * check if doors reallocated the memory underneath us
2151          * if they did munmap it or suffer a memory leak
2152          */
2153         if (doorptr != (void *)pbuf) {
2154                 _nsc_resizedoorbuf(bufsize);
2155                 (void) munmap((void *)doorptr, bufsize);
2156         }
2157         return (status);
2158 }
2159 
2160 nss_status_t
2161 _nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2162     nss_getent_t *contextpp, void *args)
2163 {
2164         nss_status_t            status = NSS_TRYLOCAL;
2165         struct nss_getent_context *contextp = contextpp->ctx;
2166         nss_pheader_t           *pbuf;
2167         void                    *doorptr = NULL;
2168         size_t                  bufsize = 0;
2169         size_t                  datasize = 0;
2170 
2171         /* return if already in local mode */
2172         if (contextp->cookie == NSCD_LOCAL_COOKIE)
2173                 return (NSS_TRYLOCAL);
2174 
2175         /* _nsc_setent_u already checked for nscd local case ... proceed */
2176         if (args == NULL)
2177                 return (NSS_NOTFOUND);
2178 
2179         /* get the door buffer  & configured size */
2180         bufsize = ((nss_XbyY_args_t *)args)->buf.buflen;
2181         if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2182                 return (NSS_UNAVAIL);
2183         if (doorptr == NULL || bufsize == 0)
2184                 return (NSS_UNAVAIL);
2185 
2186         pbuf = (nss_pheader_t *)doorptr;
2187         pbuf->nsc_callnumber = NSCD_GETENT;
2188 
2189         /* pack relevant setent request info into door buffer */
2190         status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2191         if (status != NSS_SUCCESS)
2192                 return (status);
2193 
2194         /* transfer packed switch request to nscd via door */
2195         /* data_off can be used because it is header+dbd_len+key_len */
2196         datasize = pbuf->data_off;
2197         status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2198 
2199         /* If fallback to standard nss logic (door failure) if possible */
2200         if (status != NSS_SUCCESS) {
2201                 if (status == NSS_TRYLOCAL ||
2202                     contextp->cookie == NSCD_NEW_COOKIE) {
2203                         contextp->cookie = NSCD_LOCAL_COOKIE;
2204 
2205                         /* init the local cookie */
2206                         nss_setent_u(rootp, initf, contextpp);
2207                         if (contextpp->ctx == 0)
2208                                 return (NSS_UNAVAIL);
2209                         return (NSS_TRYLOCAL);
2210                 }
2211                 return (NSS_UNAVAIL);
2212         }
2213         /* check error, unpack and process results */
2214         status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2215             initf, contextpp, args);
2216         /*
2217          * check if doors reallocated the memory underneath us
2218          * if they did munmap it or suffer a memory leak
2219          */
2220         if (doorptr != (void *)pbuf) {
2221                 _nsc_resizedoorbuf(bufsize);
2222                 (void) munmap((void *)doorptr, bufsize);
2223         }
2224         return (status);
2225 }
2226 
2227 nss_status_t
2228 _nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2229     nss_getent_t *contextpp)
2230 {
2231         nss_status_t            status = NSS_TRYLOCAL;
2232         struct nss_getent_context *contextp = contextpp->ctx;
2233         nss_pheader_t           *pbuf;
2234         void                    *doorptr = NULL;
2235         size_t                  bufsize = 0;
2236         size_t                  datasize = 0;
2237 
2238         /* return if already in local mode */
2239         if (contextp->cookie == NSCD_LOCAL_COOKIE)
2240                 return (NSS_TRYLOCAL);
2241 
2242         /* _nsc_setent_u already checked for nscd local case ... proceed */
2243 
2244         /* get the door buffer  & configured size */
2245         if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2246                 return (NSS_UNAVAIL);
2247         if (doorptr == NULL || bufsize == 0)
2248                 return (NSS_UNAVAIL);
2249 
2250         /* pack up a NSCD_ENDGET request passing in the cookie */
2251         pbuf = (nss_pheader_t *)doorptr;
2252         pbuf->nsc_callnumber = NSCD_ENDENT;
2253 
2254         /* pack relevant setent request info into door buffer */
2255         status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2256         if (status != NSS_SUCCESS)
2257                 return (status);
2258 
2259         /* transfer packed switch request to nscd via door */
2260         /* data_off can be used because it is header+dbd_len+key_len */
2261         datasize = pbuf->data_off;
2262         (void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2263 
2264         /* error codes & unpacking ret values don't matter.  We're done */
2265 
2266         /*
2267          * check if doors reallocated the memory underneath us
2268          * if they did munmap it or suffer a memory leak
2269          */
2270         if (doorptr != (void *)pbuf) {
2271                 _nsc_resizedoorbuf(bufsize);
2272                 (void) munmap((void *)doorptr, bufsize);
2273         }
2274 
2275         /* clean up initf setup */
2276         if (contextp->param.cleanup != 0)
2277                 (contextp->param.cleanup)(&contextp->param);
2278         contextp->param.cleanup = NULL;
2279 
2280         /* clear cookie */
2281         contextp->cookie = NSCD_NEW_COOKIE;
2282         return (NSS_SUCCESS);
2283 }
2284 
2285 /*
2286  * Internal private API to return default suggested buffer sizes
2287  * for nsswitch API requests.
2288  */
2289 
2290 size_t
2291 _nss_get_bufsizes(int arg)
2292 {
2293         switch (arg) {
2294         case _SC_GETGR_R_SIZE_MAX:
2295                 return (__nss_buflen_group);
2296         }
2297         return (__nss_buflen_default);
2298 }
2299 
2300 void *
2301 _nss_XbyY_fini(nss_XbyY_args_t *args)
2302 {
2303         if ((args->returnval == NULL) && (args->erange != 0))
2304                 errno = ERANGE;
2305         return (args->returnval);
2306 }