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  * Copyright 2012 Milan Jurik. All rights reserved.
  26  */
  27 
  28 #include <stdlib.h>       /* getenv() */
  29 #include <assert.h>
  30 #include <unistd.h>
  31 #include <string.h>
  32 #include <pthread.h>
  33 #include <dlfcn.h>
  34 #include <nss_dbdefs.h>
  35 #include <exec_attr.h>
  36 #include <gssapi/gssapi.h>
  37 #include "nscd_door.h"
  38 #include "nscd_switch.h"
  39 #include "nscd_log.h"
  40 #include "nscd_frontend.h"
  41 
  42 #pragma weak nss_search = _nss_search
  43 #define nss_search      _nss_search
  44 
  45 extern rwlock_t nscd_smf_service_state_lock;
  46 
  47 /* nscd id: main, forker, or child */
  48 extern int _whoami;
  49 
  50 static int
  51 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
  52 {
  53         if (res != NSS_TRYAGAIN && res !=  NSS_NISSERVDNS_TRYAGAIN) {
  54                 if (res == NSS_SUCCESS) {
  55                         __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
  56                         __NSW_UNPAUSE_ACTION(
  57                             lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
  58                 }
  59                 return (0);
  60         }
  61 
  62         if ((res == NSS_TRYAGAIN &&
  63             lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
  64             (res == NSS_NISSERVDNS_TRYAGAIN &&
  65             lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
  66                 return (1);
  67 
  68         if (res == NSS_TRYAGAIN &&
  69             lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
  70                 if (n <= lkp->max_retries)
  71                         return (1);
  72                 else {
  73                         lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
  74                         return (0);
  75                 }
  76 
  77         if (res == NSS_NISSERVDNS_TRYAGAIN &&
  78             lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
  79                 if (n <= lkp->max_retries)
  80                         return (1);
  81                 else {
  82                         lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
  83                             __NSW_TRYAGAIN_PAUSED;
  84                         return (0);
  85                 }
  86 
  87         return (0);
  88 }
  89 
  90 static thread_key_t loopback_key = THR_ONCE_KEY;
  91 typedef struct lb_key {
  92         int             srci;
  93         int             dbi;
  94         int             fnum;
  95         int             *lb_flagp;
  96 } lb_key_t;
  97 
  98 static int
  99 set_loopback_key(lb_key_t *key) {
 100 
 101         int             rc;
 102 
 103         rc = thr_keycreate_once(&loopback_key, NULL);
 104         /* set key if not already set */
 105         if (rc == 0 && pthread_getspecific(loopback_key) == NULL)
 106                 rc = thr_setspecific(loopback_key, key);
 107 
 108         return (rc);
 109 }
 110 
 111 static lb_key_t *
 112 get_loopback_key(void) {
 113 
 114         char            *me = "get_loopback_key";
 115         lb_key_t        *k = NULL;
 116 
 117         k = pthread_getspecific(loopback_key);
 118 
 119         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 120         (me, "get loopback key, key = %p\n", k);
 121 
 122         return (k);
 123 }
 124 
 125 static void
 126 clear_loopback_key(lb_key_t *key) {
 127 
 128         char            *me = "clear_loopback_key";
 129 
 130         if (loopback_key != THR_ONCE_KEY && key != NULL) {
 131                 /*
 132                  * key->lb_flagp points to the location of the
 133                  * flag, check_flag, in the stack where it was
 134                  * first set; clearing the flag tells that
 135                  * stack the loopback error has been resolved
 136                  */
 137                 *key->lb_flagp = 0;
 138                 (void) thr_setspecific(loopback_key, NULL);
 139         }
 140 
 141         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 142         (me, "key %p cleared\n", key);
 143 }
 144 
 145 static thread_key_t initf_key = THR_ONCE_KEY;
 146 
 147 static int
 148 set_initf_key(void *pbuf) {
 149 
 150         int             rc;
 151 
 152         rc = thr_keycreate_once(&initf_key, NULL);
 153         if (rc == 0)
 154                 rc = thr_setspecific(initf_key, pbuf);
 155 
 156         return (rc);
 157 }
 158 
 159 static void *
 160 get_initf_key(void) {
 161 
 162         char            *me = "get_initf_key";
 163         void            *pbuf;
 164 
 165         pbuf = pthread_getspecific(initf_key);
 166 
 167         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 168         (me, "got initf pbuf, key = %p\n", pbuf);
 169 
 170         return (pbuf);
 171 }
 172 
 173 static void
 174 clear_initf_key(void) {
 175 
 176         char            *me = "clear_initf_key";
 177 
 178         (void) thr_setspecific(initf_key, NULL);
 179 
 180         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 181         (me, "initf pbuf cleared\n");
 182 }
 183 
 184 /*
 185  * Call the input initf function to extract the
 186  * NSS front end parameters and examine them to
 187  * determine if an NSS lookup is to be performed
 188  * on a regular or a pseudo (called from compat
 189  * backend) database. Then set the necessary
 190  * parameters for later data structures creation
 191  * and processing.
 192  */
 193 static nscd_rc_t
 194 getparams(
 195         int                     search_fnum,
 196         nss_db_initf_t          initf,
 197         nscd_nsw_params_t       *params)
 198 {
 199 
 200         nscd_rc_t       rc = NSCD_SUCCESS;
 201         nss_db_params_t *p;
 202         int             j;
 203         char            *dbn;
 204         const char      *n;
 205         char            *me = "getparams";
 206 
 207         p = &params->p;
 208         (void) memset(params, 0, sizeof (nscd_nsw_params_t));
 209         (*initf)(p);
 210         params->dbi = -1;
 211         params->cfgdbi = -1;
 212         params->compati = -1;
 213         params->dnsi = -1;
 214 
 215         /* map database name to index */
 216         n = p->name;
 217         for (j = 0; j < NSCD_NUM_DB; j++) {
 218                 dbn = NSCD_NSW_DB_NAME(j);
 219                 if (*n != *dbn)
 220                         continue;
 221                 if (strcmp(n, dbn) == 0) {
 222                         params->dbi = j;
 223                         if (*n != 'h' && *n != 'i' && *n != 's' && *n != 'a')
 224                                 break;
 225                         if (strcmp(n, NSS_DBNAM_HOSTS) == 0 &&
 226                             search_fnum == NSS_DBOP_HOSTS_BYNAME)
 227                                 params->dnsi = 0;
 228                         else if (strcmp(n, NSS_DBNAM_IPNODES) == 0 &&
 229                             search_fnum == NSS_DBOP_IPNODES_BYNAME)
 230                                 params->dnsi = 1;
 231                         else if (strcmp(n, NSS_DBNAM_SHADOW) == 0)
 232                                 params->privdb = 1;
 233                         break;
 234                 }
 235         }
 236 
 237         /*
 238          * use the switch policy for passwd_compat or
 239          * group_compat?
 240          */
 241         if (p->config_name != NULL) {
 242                 n = p->config_name;
 243                 for (j = 0; j < NSCD_NUM_DB; j++) {
 244                         dbn = NSCD_NSW_DB_NAME(j);
 245                         if (*n == *dbn) {
 246                                 if (strcmp(n, dbn) == 0) {
 247                                         params->cfgdbi = j;
 248                                         break;
 249                                 }
 250                         }
 251                 }
 252         }
 253 
 254         /* map the database name to the pseudo database index */
 255         if (params->cfgdbi != -1) {
 256                 if (strstr(p->config_name, "_compat") != NULL) {
 257                         n = p->name;
 258                         for (j = params->cfgdbi; j < NSCD_NUM_DB; j++) {
 259                                 dbn = NSCD_NSW_DB_NAME(j);
 260                                 if (*n == *dbn) {
 261                                         if (strcmp(n, dbn) == 0) {
 262                                                 params->compati = j;
 263                                                 break;
 264                                         }
 265                                 }
 266                         }
 267                 }
 268         }
 269 
 270         /*
 271          * if unsupported database, let caller determine what to do next
 272          */
 273         if (params->dbi == -1) {
 274                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 275                 (me, "unsupported database: %s\n", p->name);
 276                 return (NSCD_CFG_UNSUPPORTED_SWITCH_DB);
 277         }
 278 
 279         return (rc);
 280 }
 281 
 282 static void
 283 nscd_initf(nss_db_params_t      *p)
 284 {
 285         nss_pheader_t           *pbuf;
 286         nssuint_t               off;
 287         nss_dbd_t               *pdbd;
 288         char                    *me = "nscd_initf";
 289 
 290         pbuf = (nss_pheader_t *)get_initf_key();
 291         if (pbuf == NULL) {
 292                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 293                 (me, "ERROR: initf key not set\n");
 294                 return;
 295         }
 296 
 297         if (pbuf->dbd_len <= sizeof (nss_dbd_t)) {
 298                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 299                 (me, "invalid db front params data ? dbd_len = %d\n",
 300                     pbuf->dbd_len);
 301                 return;
 302         }
 303 
 304         off = pbuf->dbd_off;
 305         pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
 306 
 307         p->name = (char *)pdbd + pdbd->o_name;
 308         p->config_name = (char *)pdbd + pdbd->o_config_name;
 309         p->default_config = (char *)pdbd + pdbd->o_default_config;
 310         p->flags = (enum nss_dbp_flags)pdbd->flags;
 311         (void) memcpy(&p->private, &pbuf->nscdpriv, sizeof (p->private));
 312 
 313         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 314         (me, "db frontend params: name =%s, config_name = %s, "
 315         "default_config = %s, flags = %x\n", p->name,
 316             (p->config_name && *p->config_name != '\0' ?
 317             p->config_name : "<NOT SPECIFIED>"),
 318             (p->default_config && *p->default_config != '\0' ?
 319             p->default_config : "<NOT SPECIFIED>"),
 320             p->flags);
 321 }
 322 
 323 
 324 static void
 325 trace_result(
 326         int             dbi,
 327         int             srci,
 328         int             op,
 329         nss_status_t    res,
 330         nss_XbyY_args_t *arg)
 331 {
 332         char    *res_str;
 333         char    *src = "?";
 334         char    *db = "?";
 335         char    *data_str = "<NOT STRING FORMAT>";
 336         int     data_len = 0;
 337         char    *me = "trace_result";
 338 
 339         switch (res) {
 340         case NSS_SUCCESS:
 341                 res_str = "NSS_SUCCESS";
 342                 break;
 343         case NSS_NOTFOUND:
 344                 res_str = "NSS_NOTFOUND";
 345                 break;
 346         case NSS_UNAVAIL:
 347                 res_str = "NSS_UNAVAIL";
 348                 break;
 349         case NSS_TRYAGAIN:
 350                 res_str = "NSS_TRYAGAIN";
 351                 break;
 352         case NSS_NISSERVDNS_TRYAGAIN:
 353                 res_str = "NSS_NISSERVDNS_TRYAGAIN";
 354                 break;
 355         default:
 356                 res_str = "UNKNOWN STATUS";
 357                 break;
 358         }
 359 
 360         if (dbi != -1)
 361                 db = NSCD_NSW_DB_NAME(dbi);
 362         if (srci != -1)
 363                 src = NSCD_NSW_SRC_NAME(srci);
 364 
 365         if (arg->buf.result == NULL) {
 366                 data_str = arg->buf.buffer;
 367                 data_len = arg->returnlen;
 368         }
 369 
 370         if (res == NSS_SUCCESS) {
 371                 _nscd_logit(me, "%s: database: %s, operation: %d, "
 372                     "source: %s returned >>%s<<, length = %d\n",
 373                     res_str, db, op, src, data_str, data_len);
 374                 return;
 375         }
 376 
 377         _nscd_logit(me, "%s: database: %s, operation: %d, source: %s, "
 378             "erange= %d, herrno: %s (%d)\n",
 379             res_str, db, op, src, arg->erange, hstrerror(arg->h_errno),
 380             arg->h_errno);
 381 }
 382 
 383 /*
 384  * Determine if a request should be done locally in the getXbyY caller's
 385  * process. Return none zero if yes, 0 otherwise. This should be called
 386  * before the switch engine steps through the backends/sources.
 387  * This function returns 1 if:
 388  *   -- the database is exec_attr and the search_flag is GET_ALL
 389  */
 390 static int
 391 try_local(
 392         int                     dbi,
 393         void                    *arg)
 394 {
 395         struct nss_XbyY_args    *ap = (struct nss_XbyY_args *)arg;
 396         _priv_execattr          *ep;
 397         int                     rc = 0;
 398         char                    *me = "try_local";
 399 
 400         if (strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_EXECATTR) == 0) {
 401                 if ((ep = ap->key.attrp) != NULL && IS_GET_ALL(ep->search_flag))
 402                         rc = 1;
 403         }
 404 
 405         if (rc != 0) {
 406 
 407                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 408                 (me, "TRYLOCAL: exec_attr:GET_ALL\n");
 409         }
 410 
 411         return (rc);
 412 }
 413 
 414 /*
 415  * Determine if a request should be done locally in the getXbyY caller's
 416  * process. Return none zero if yes, 0 otherwise. This should be called
 417  * before the switch engine invokes any backend.
 418  * This function returns 1 if:
 419  *   -- the database is shadow and the source is compat
 420  */
 421 static int
 422 try_local2(
 423         int     dbi,
 424         int     srci)
 425 {
 426         int     rc = 0;
 427         char    *me = "try_local2";
 428 
 429         if (*NSCD_NSW_DB_NAME(dbi) == 's' &&
 430             strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_SHADOW) == 0) {
 431                 if (strcmp(NSCD_NSW_SRC_NAME(srci), "compat") == 0)
 432                         rc = 1;
 433         }
 434 
 435         if (rc != 0) {
 436                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 437                 (me, "TRYLOCAL: database: shadow, source: %s\n",
 438                     NSCD_NSW_SRC_NAME(srci));
 439         }
 440 
 441         return (rc);
 442 }
 443 
 444 static nscd_rc_t
 445 get_lib_func(void **handle, void **func, mutex_t *lock,
 446         char *lib, char *name, void **func_p)
 447 {
 448         char    *me = "get_lib_func";
 449         void    *sym;
 450 
 451         if (func_p != NULL && *handle != NULL && *func != NULL) {
 452                 *func_p = *func;
 453                 return (NSCD_SUCCESS);
 454         }
 455 
 456         (void) mutex_lock(lock);
 457 
 458         /* close the handle if requested */
 459         if (func_p == NULL) {
 460                 if (*handle != NULL) {
 461                         (void) dlclose(*handle);
 462                         *handle = NULL;
 463                         *func = NULL;
 464                 }
 465                 (void) mutex_unlock(lock);
 466                 return (NSCD_SUCCESS);
 467         }
 468 
 469         if (*handle != NULL && *func != NULL) {
 470                 *func_p = *func;
 471                 (void) mutex_unlock(lock);
 472                 return (NSCD_SUCCESS);
 473         }
 474 
 475         if (*handle == NULL) {
 476                 *handle = dlopen(lib, RTLD_LAZY);
 477                 if (*handle == NULL) {
 478                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
 479                         (me, "unable to dlopen %s\n", lib);
 480                         (void) mutex_unlock(lock);
 481                         return (NSCD_CFG_DLOPEN_ERROR);
 482                 }
 483         }
 484 
 485         if ((sym = dlsym(*handle, name)) == NULL) {
 486 
 487                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
 488                 (me, "unable to find symbol %s:%s\n", lib, name);
 489                 (void) mutex_unlock(lock);
 490                 return (NSCD_CFG_DLSYM_ERROR);
 491         } else {
 492                 *func_p = sym;
 493                 *func = sym;
 494         }
 495 
 496         (void) mutex_unlock(lock);
 497         return (NSCD_SUCCESS);
 498 }
 499 
 500 static nscd_rc_t
 501 get_libc_nss_search(void **func_p)
 502 {
 503         static void     *handle = NULL;
 504         static void     *func = NULL;
 505         static mutex_t  lock = DEFAULTMUTEX;
 506 
 507         return (get_lib_func(&handle, &func, &lock,
 508             "libc.so", "nss_search", func_p));
 509 }
 510 
 511 static nscd_rc_t
 512 get_gss_func(void **func_p)
 513 {
 514         static void     *handle = NULL;
 515         static void     *func = NULL;
 516         static mutex_t  lock = DEFAULTMUTEX;
 517 
 518         return (get_lib_func(&handle, &func, &lock,
 519             "libgss.so", "gss_inquire_cred", func_p));
 520 }
 521 
 522 static nscd_rc_t
 523 get_sldap_shadow_func(void **func_p)
 524 {
 525         static void     *handle = NULL;
 526         static void     *func = NULL;
 527         static mutex_t  lock = DEFAULTMUTEX;
 528 
 529         return (get_lib_func(&handle, &func, &lock,
 530             "libsldap.so", "__ns_ldap_is_shadow_update_enabled",
 531             func_p));
 532 }
 533 
 534 /*
 535  * get_dns_funcs returns pointers to gethostbyname functions in the
 536  * dynamically loaded nss_dns & nss_mdns modules that return host
 537  * lookup results along with the TTL value in the DNS resource
 538  * records. The dnsi parameter indicates whether the lookup database
 539  * is hosts(0) or ipnodes(1). The srcname parameter identifies the DNS
 540  * module: dns/mdns and the function returns the address of the specific
 541  * gethostbyname function in func_p variable.
 542  */
 543 static nscd_rc_t
 544 get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname)
 545 {
 546         int             si;
 547         void            **funcpp;
 548         static void     *handle[2] = { NULL, NULL };
 549         static mutex_t  func_lock[2] = { DEFAULTMUTEX, DEFAULTMUTEX };
 550         static void     *func[2][2] = {{NULL, NULL}, {NULL, NULL}};
 551         static const char       *lib[2] = { "nss_dns.so.1", "nss_mdns.so.1" };
 552         static const char       *func_name[2][2] =
 553                 {{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" },
 554                 { "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }};
 555 
 556         /* source index: 0 = dns, 1 = mdns */
 557         if (strcmp(srcname, "dns") == 0)
 558                 si = 0;
 559         else
 560                 si = 1;
 561 
 562         /*
 563          * function index (func[si][dnsi]):
 564          * [0,0] = dns/hosts, [0,1] = dns/ipnodes,
 565          * [1,0] = mdns/hosts, [1,1] = mdns/ipnodes
 566          */
 567 
 568         if (dnsi < 0) { /* close handle */
 569                 funcpp = NULL;
 570                 (void) mutex_lock(&func_lock[si]);
 571                 func[si][0] = NULL;
 572                 func[si][1] = NULL;
 573                 (void) mutex_unlock(&func_lock[si]);
 574         } else
 575                 funcpp = (void **)func_p;
 576 
 577         return (get_lib_func(&handle[si], &func[si][dnsi], &func_lock[si],
 578             (char *)lib[si], (char *)func_name[si][dnsi], funcpp));
 579 }
 580 
 581 static nss_status_t
 582 search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi)
 583 {
 584         nss_status_t    (*func)();
 585         nss_status_t    res = NSS_UNAVAIL;
 586         nscd_rc_t       rc;
 587 
 588         swret->noarg = 0;
 589         if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0)
 590                 return (NSS_ERROR);
 591 
 592         rc = get_dns_funcs(dnsi, &func, srcname);
 593         if (rc == NSCD_SUCCESS) {
 594                 /*
 595                  * data_len in the packed buf header may be changed
 596                  * by the dns or mdns backend, reset it just in
 597                  * case
 598                  */
 599                 ((nss_pheader_t *)swret->pbuf)->data_len =
 600                     swret->datalen;
 601                 res = (func)(NULL, &swret->pbuf, &swret->pbufsiz);
 602         }
 603         return (res);
 604 }
 605 
 606 /*
 607  * Returns a flag to indicate if needs to fall back to the
 608  * main nscd when a per-user lookup failed with rc NSS_NOTFOUND.
 609  */
 610 static int
 611 set_fallback_flag(char *srcname, nss_status_t rc)
 612 {
 613         char    *me = "set_fallback_flag";
 614         if (strcmp(srcname, "ldap") == 0 && rc == NSS_NOTFOUND) {
 615                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 616                 (me, "NSS_NOTFOUND (ldap): fallback to main nscd "
 617                 "may be needed\n");
 618                 return (1);
 619         }
 620         return (0);
 621 }
 622 
 623 nss_status_t
 624 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
 625         void *search_args)
 626 {
 627         char                    *me = "nss_search";
 628         nss_status_t            res = NSS_UNAVAIL;
 629         nscd_nsw_state_t        *s = NULL;
 630         int                     n_src;
 631         unsigned int            status_vec = 0;
 632         int                     dbi, srci = -1;
 633         int                     check_loopback = 0;
 634         int                     state_thr = 0;
 635         lb_key_t                key, *k = NULL;
 636         nss_db_root_t           root_db;
 637         nscd_nsw_params_t       params;
 638         nscd_sw_return_t        *swret;
 639 
 640         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 641         (me, "rootp = %p, initf = %p, search_fnum = %d, "
 642             "search_args = %p\n", rootp, initf,
 643             search_fnum, search_args);
 644 
 645         NSCD_SW_STATS_G.lookup_request_received_g++;
 646         NSCD_SW_STATS_G.lookup_request_in_progress_g++;
 647         NSCD_SW_STATS_G.lookup_request_queued_g++;
 648 
 649         /* determine db index, cfg db index, etc */
 650         if (getparams(search_fnum, initf, &params) ==
 651             NSCD_CFG_UNSUPPORTED_SWITCH_DB) {
 652                 /*
 653                  * if unsupported database and the request is from the
 654                  * the door, tell the door client to try it locally
 655                  */
 656                 if (initf == nscd_initf) {
 657                         res = NSS_TRYLOCAL;
 658                         goto error_exit;
 659                 } else { /* otherwise, let libc:nss_search() handle it */
 660                         nss_status_t    (*func)();
 661 
 662                         if (get_libc_nss_search((void **)&func) ==
 663                             NSCD_SUCCESS)
 664                                 return ((func)(rootp, initf, search_fnum,
 665                                     search_args));
 666                         else
 667                                 goto error_exit;
 668                 }
 669         }
 670         dbi = params.dbi;
 671 
 672         /* get address of the switch engine return data area */
 673         if (initf == nscd_initf) {
 674                 swret = (nscd_sw_return_t *)params.p.private;
 675                 swret->srci = -1;
 676         } else {
 677                 swret = NULL;
 678                 params.dnsi = -1;
 679         }
 680 
 681         /*
 682          * for door request that should be processed by the client,
 683          * send it back with status NSS_TRYLOCAL
 684          */
 685         if (initf == nscd_initf && try_local(dbi, search_args) == 1) {
 686                 res = NSS_TRYLOCAL;
 687                 goto error_exit;
 688         }
 689 
 690         NSCD_SW_STATS(dbi).lookup_request_received++;
 691         NSCD_SW_STATS(dbi).lookup_request_in_progress++;
 692         NSCD_SW_STATS(dbi).lookup_request_queued++;
 693 
 694         /* if lookup not enabled, return NSS_UNAVAIL  */
 695         if (!(NSCD_SW_CFG_G.enable_lookup_g == nscd_true &&
 696             NSCD_SW_CFG(dbi).enable_lookup == nscd_true)) {
 697 
 698                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 699                 (me, "lookup not enabled for %s\n", NSCD_NSW_DB_NAME(dbi));
 700 
 701                 goto error_exit;
 702         }
 703 
 704         /* determine if loopback checking is configured */
 705         if (NSCD_SW_CFG_G.enable_loopback_checking_g == nscd_true &&
 706             NSCD_SW_CFG(dbi).enable_loopback_checking == nscd_true) {
 707                 check_loopback = 1;
 708 
 709                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 710                 (me, "loopback checking enabled for %s\n",
 711                     NSCD_NSW_DB_NAME(dbi));
 712         }
 713 
 714         if (check_loopback) {
 715                 k = get_loopback_key();
 716                 if (k != NULL) {
 717                         if (k->dbi != dbi || k->fnum != search_fnum) {
 718                                 clear_loopback_key(k);
 719                                 k = NULL;
 720                         }
 721                 }
 722         }
 723 
 724         if (s == 0) {
 725                 nscd_rc_t       rc;
 726 
 727                 if (check_loopback) {
 728                         rc = _nscd_get_nsw_state_thread(&root_db, &params);
 729                         state_thr = 1;
 730                 } else
 731                         rc = _nscd_get_nsw_state(&root_db, &params);
 732 
 733                 NSCD_SW_STATS_G.lookup_request_queued_g--;
 734                 NSCD_SW_STATS(dbi).lookup_request_queued--;
 735 
 736                 if (rc != NSCD_SUCCESS)
 737                                 goto error_exit;
 738 
 739                 s = (nscd_nsw_state_t *)root_db.s;
 740         }
 741 
 742         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 743         (me, "database = %s, config = >>%s<<\n", NSCD_NSW_DB_NAME(dbi),
 744             (*s->nsw_cfg_p)->nsw_cfg_str);
 745 
 746         for (n_src = 0;  n_src < s->max_src;  n_src++) {
 747                 nss_backend_t           *be = NULL;
 748                 nss_backend_op_t        funcp = NULL;
 749                 struct __nsw_lookup_v1  *lkp;
 750                 int                     smf_state;
 751                 int                     n_loop = 0;
 752                 int                     max_retry = 10;
 753 
 754                 res = NSS_UNAVAIL;
 755 
 756                 if (n_src == 0)
 757                         lkp = s->config->lookups;
 758                 else
 759                         lkp = lkp->next;
 760 
 761                 /* set the number of max. retries */
 762                 if (lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
 763                         max_retry = lkp->max_retries;
 764 
 765                 srci = (*s->nsw_cfg_p)->src_idx[n_src];
 766                 if (swret != NULL)
 767                         swret->srci = srci;
 768 
 769                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 770                 (me, "nsw source = %s\n", NSCD_NSW_SRC_NAME(srci));
 771 
 772                 /*
 773                  * If no privilege to look up, skip.
 774                  * 'files' requires PRIV_FILE_DAC_READ to read shadow(4) data,
 775                  * 'ldap' requires all zones privilege.
 776                  */
 777                 if (params.privdb == 1 && swret != NULL) {
 778                         boolean_t       (*is_shadow_update_enabled)();
 779                         boolean_t       check_ldap_priv = B_FALSE;
 780 
 781                         if (strcmp(NSCD_NSW_SRC_NAME(srci), "ldap") == 0) {
 782                                 if (get_sldap_shadow_func(
 783                                     (void **)&is_shadow_update_enabled) ==
 784                                     NSCD_SUCCESS &&
 785                                     is_shadow_update_enabled()) {
 786                                         check_ldap_priv = B_TRUE;
 787 
 788                                         /*
 789                                          * A peruser nscd doesn't have
 790                                          * the privileges to lookup a
 791                                          * private database, such as shadow,
 792                                          * returns NSS_ALTRETRY to have the
 793                                          * main nscd do the job.
 794                                          */
 795                                         if (_whoami == NSCD_CHILD) {
 796                                                 res = NSS_ALTRETRY;
 797                                                 goto free_nsw_state;
 798                                         }
 799                                 }
 800                         }
 801 
 802                         if ((strcmp(NSCD_NSW_SRC_NAME(srci), "files") == 0 &&
 803                             _nscd_check_client_priv(NSCD_READ_PRIV) != 0) ||
 804                             (check_ldap_priv &&
 805                             _nscd_check_client_priv(NSCD_ALL_PRIV) != 0)) {
 806                                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
 807                                     NSCD_LOG_LEVEL_DEBUG)
 808                                 (me, "no privilege to look up, skip source\n");
 809 
 810                                 goto next_src;
 811                         }
 812                 }
 813 
 814                 /* get state of the (backend) client service */
 815                 smf_state = _nscd_get_smf_state(srci, dbi, 0);
 816 
 817                 /* stop if the source is one that should be TRYLOCAL */
 818                 if (initf == nscd_initf &&      /* request is from the door */
 819                     (smf_state == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
 820                     (smf_state == NSCD_SVC_STATE_FOREIGN_SRC &&
 821                     s->be_version_p[n_src] == NULL) ||
 822                     (params.privdb && try_local2(dbi, srci) == 1))) {
 823                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
 824                         (me, "returning TRYLOCAL ... \n");
 825                         res = NSS_TRYLOCAL;
 826                         goto free_nsw_state;
 827                 }
 828 
 829                 if (check_loopback && k != NULL) {
 830 
 831                         if (k->srci == srci && k->dbi == dbi)
 832                                 if (k->fnum == search_fnum) {
 833 
 834                                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
 835                                             NSCD_LOG_LEVEL_DEBUG)
 836                                         (me, "loopback detected: "
 837                                             "source = %s, database = %s "
 838                                             "search fnum = %d\n",
 839                                             NSCD_NSW_SRC_NAME(srci),
 840                                             NSCD_NSW_DB_NAME(dbi), search_fnum);
 841 
 842                                 NSCD_SW_STATS_G.loopback_nsw_db_skipped_g++;
 843                                 NSCD_SW_STATS(dbi).loopback_nsw_db_skipped++;
 844                                         continue;
 845                                 }
 846                 }
 847 
 848                 be = s->be[n_src];
 849                 if (be != NULL)
 850                         funcp = NSS_LOOKUP_DBOP(be, search_fnum);
 851 
 852                 /* request could be from within nscd so check states again */
 853                 if (be == NULL || (params.dnsi < 0 && (funcp == NULL ||
 854                     (smf_state != NSCD_SVC_STATE_UNINITED &&
 855                     smf_state != NSCD_SVC_STATE_UNSUPPORTED_SRC &&
 856                     smf_state != NSCD_SVC_STATE_FOREIGN_SRC &&
 857                     smf_state < SCF_STATE_ONLINE)))) {
 858 
 859                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
 860                             NSCD_LOG_LEVEL_DEBUG)
 861                         (me, "unable to look up source %s: be = %p, "
 862                         "smf state = %d, funcp = %p\n",
 863                             NSCD_NSW_SRC_NAME(srci), be, smf_state, funcp);
 864 
 865                         goto next_src;
 866                 }
 867 
 868                 do {
 869                         /*
 870                          * we can only retry max_retry times,
 871                          * otherwise threads may get stuck in this
 872                          * do-while loop forever
 873                          */
 874                         if (n_loop > max_retry) {
 875                                 if (swret != NULL)
 876                                         res = NSS_TRYLOCAL;
 877                                 goto free_nsw_state;
 878                         }
 879 
 880                         /*
 881                          * set up to prevent loopback
 882                          */
 883                         if (check_loopback && k == NULL) {
 884                                 key.srci = srci;
 885                                 key.dbi = dbi;
 886                                 key.fnum = search_fnum;
 887                                 key.lb_flagp = &check_loopback;
 888                                 (void) set_loopback_key(&key);
 889                                 k = &key;
 890                         }
 891 
 892                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
 893                             NSCD_LOG_LEVEL_DEBUG)
 894                         (me, "looking up source = %s, loop# = %d \n",
 895                             NSCD_NSW_SRC_NAME(srci), n_loop);
 896 
 897                         /*
 898                          * search the backend, if hosts lookups,
 899                          * try to get the hosts data with ttl first
 900                          */
 901                         if (params.dnsi >= 0) {
 902                                 res = search_dns_withttl(swret,
 903                                     NSCD_NSW_SRC_NAME(srci), params.dnsi);
 904                                 /*
 905                                  * if not able to get ttl, fall back
 906                                  * to the regular backend call
 907                                  */
 908                                 if (res == NSS_ERROR)
 909                                         res = (*funcp)(be, search_args);
 910                                 else {
 911                                         /*
 912                                          * status/result are in the
 913                                          * packed buffer, not
 914                                          * search_args
 915                                          */
 916                                         swret->noarg = 1;
 917                                 }
 918                         } else
 919                                 res = (*funcp)(be, search_args);
 920                         if (swret != NULL)
 921                                 swret->errnum = errno;
 922 
 923                         /*
 924                          * backend is not up, check and update the
 925                          * smf state table
 926                          */
 927                         if (res == NSS_UNAVAIL)
 928                                 (void) _nscd_get_smf_state(srci, dbi, 1);
 929 
 930                         /*
 931                          * may need to fall back to use the main nscd
 932                          * if per-user lookup
 933                          */
 934                         if (_whoami == NSCD_CHILD && swret != NULL)
 935                                 swret->fallback = set_fallback_flag(
 936                                     NSCD_NSW_SRC_NAME(srci), res);
 937 
 938                         _NSCD_LOG_IF(NSCD_LOG_SWITCH_ENGINE,
 939                             NSCD_LOG_LEVEL_DEBUG) {
 940 
 941                                 /*
 942                                  * set up to trace the result/status
 943                                  * of the dns/ttl lookup
 944                                  */
 945                                 if (swret != NULL && swret->noarg == 1) {
 946                                         nss_pheader_t *phdr;
 947                                         struct nss_XbyY_args *arg;
 948                                         arg = (struct nss_XbyY_args *)
 949                                             search_args;
 950                                         phdr = (nss_pheader_t *)swret->pbuf;
 951                                         arg->buf.buffer = (char *)phdr +
 952                                             phdr->data_off;
 953                                         arg->returnlen = phdr->data_len;
 954                                         if (phdr->p_errno == ERANGE)
 955                                                 arg->erange = 1;
 956                                         arg->h_errno = phdr->p_herrno;
 957                                 }
 958 
 959                                 trace_result(dbi, srci, search_fnum, res,
 960                                     (nss_XbyY_args_t *)search_args);
 961                         }
 962 
 963                         n_loop++;
 964                 } while (retry_test(res, n_loop, lkp));
 965 
 966                 next_src:
 967 
 968                 status_vec |= (1 << res);
 969 
 970                 if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
 971                         break;
 972                 }
 973         }
 974 
 975         free_nsw_state:
 976 
 977         if (state_thr == 1)
 978                 _nscd_put_nsw_state_thread(s);
 979         else
 980                 _nscd_put_nsw_state(s);
 981         if (check_loopback && k != NULL)
 982                 clear_loopback_key(k);
 983 
 984         if (res != NSS_SUCCESS)
 985                 goto error_exit;
 986 
 987         NSCD_SW_STATS_G.lookup_request_succeeded_g++;
 988         NSCD_SW_STATS(dbi).lookup_request_succeeded++;
 989         NSCD_SW_STATS_G.lookup_request_in_progress_g--;
 990         NSCD_SW_STATS(dbi).lookup_request_in_progress--;
 991 
 992         return (NSS_SUCCESS);
 993 
 994         error_exit:
 995 
 996         NSCD_SW_STATS_G.lookup_request_failed_g++;
 997         NSCD_SW_STATS_G.lookup_request_in_progress_g--;
 998         NSCD_SW_STATS(dbi).lookup_request_failed++;
 999         NSCD_SW_STATS(dbi).lookup_request_in_progress--;
1000 
1001         return (res);
1002 }
1003 
1004 
1005 /* ===> get/set/endent */
1006 
1007 static void             nss_setent_u(nss_db_root_t *,
1008                                     nss_db_initf_t,
1009                                     nss_getent_t *);
1010 static nss_status_t     nss_getent_u(nss_db_root_t *,
1011                                     nss_db_initf_t,
1012                                     nss_getent_t *,
1013                                     void *);
1014 static void             nss_endent_u(nss_db_root_t *,
1015                                     nss_db_initf_t,
1016                                     nss_getent_t *);
1017 
1018 void
1019 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf,
1020         nss_getent_t *contextpp)
1021 {
1022         if (contextpp == 0)
1023                 return;
1024         nss_setent_u(rootp, initf, contextpp);
1025 }
1026 
1027 nss_status_t
1028 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1029         void *args)
1030 {
1031         nss_status_t            status;
1032 
1033         if (contextpp == 0) {
1034                 return (NSS_UNAVAIL);
1035         }
1036         status = nss_getent_u(rootp, initf, contextpp, args);
1037         return (status);
1038 }
1039 
1040 void
1041 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf,
1042         nss_getent_t *contextpp)
1043 {
1044         if (contextpp == 0)
1045                 return;
1046         nss_endent_u(rootp, initf, contextpp);
1047 }
1048 
1049 /*ARGSUSED*/
1050 static void
1051 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1052 {
1053         nscd_getent_context_t   *ctx;
1054         nscd_nsw_state_t        *s;
1055         nss_backend_t           *be;
1056         int                     n_src;
1057 
1058         ctx = (nscd_getent_context_t *)contextp;
1059         s = ctx->nsw_state;
1060         n_src = ctx->n_src;
1061         be = ctx->be;
1062 
1063         if (s != 0) {
1064                 if (n_src < s->max_src && be != 0) {
1065                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1066                         ctx->be = 0;  /* Should be unnecessary, but hey */
1067                 }
1068         }
1069         ctx->n_src = 0;
1070 }
1071 
1072 static void
1073 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1074         nss_getent_t *contextpp)
1075 {
1076         char                    *me = "nss_setent_u";
1077         nscd_nsw_state_t        *s;
1078         nscd_getent_context_t   *contextp;
1079         nscd_nsw_params_t       params;
1080         nss_db_root_t           root;
1081         nss_backend_t           *be;
1082         int                     n_src, i;
1083         nscd_sw_return_t        *swret = NULL;
1084 
1085         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1086         (me, "rootp = %p, initf = %p, contextpp = %p \n",
1087             rootp, initf, contextpp);
1088 
1089         /*
1090          * Get the nsw db index via the initf function. If unsupported
1091          * database, no need to continue
1092          */
1093         if (getparams(-1, initf, &params) == NSCD_CFG_UNSUPPORTED_SWITCH_DB)
1094                 return;
1095 
1096         /* get address of the switch engine return data area */
1097         if (initf == nscd_initf)
1098                 swret = (nscd_sw_return_t *)params.p.private;
1099 
1100         /* if no privilege to look up, return */
1101         if (params.privdb == 1 && swret != NULL &&
1102             _nscd_check_client_priv(NSCD_READ_PRIV) != 0) {
1103 
1104                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1105                 (me, "no privilege \n");
1106                 return;
1107         }
1108 
1109         if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1110                 if ((_nscd_get_getent_ctx(contextpp, &params)) !=
1111                     NSCD_SUCCESS) {
1112                         return;
1113                 }
1114                 contextp = (nscd_getent_context_t *)contextpp->ctx;
1115         }
1116         s = contextp->nsw_state;
1117 
1118         if (s == 0) {
1119                 if (_nscd_get_nsw_state(&root, &params) !=
1120                     NSCD_SUCCESS) {
1121                         return;
1122                 }
1123                 s = (nscd_nsw_state_t *)root.s;
1124                 contextp->nsw_state = s;
1125 
1126         } else {
1127                 s       = contextp->nsw_state;
1128                 n_src   = contextp->n_src;
1129                 be      = contextp->be;
1130                 if (n_src == 0 && be != 0) {
1131                         /*
1132                          * Optimization:  don't do endent, don't change
1133                          *   backends, just do the setent.  Look Ma, no locks
1134                          *   (nor any context that needs updating).
1135                          */
1136                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1137                         return;
1138                 }
1139                 if (n_src < s->max_src && be != 0) {
1140                         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1141                         contextp->be = 0;    /* Play it safe */
1142                 }
1143         }
1144         for (n_src = 0, be = 0; n_src < s->max_src &&
1145             (be = s->be[n_src]) == 0; n_src++) {
1146                 ;
1147         }
1148 
1149         contextp->n_src      = n_src;
1150         contextp->be = be;
1151 
1152         if (be == 0) {
1153                 /* Things are broken enough that we can't do setent/getent */
1154                 nss_endent_u(rootp, initf, contextpp);
1155                 return;
1156         }
1157 
1158         /*
1159          * make sure all the backends are supported
1160          */
1161         for (i = 0; i < s->max_src; i++) {
1162                 int     st, srci;
1163 
1164                 if (s->be[i] == NULL)
1165                         continue;
1166 
1167                 srci = (*s->nsw_cfg_p)->src_idx[i];
1168                 st = _nscd_get_smf_state(srci, params.dbi, 1);
1169                 if (st == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
1170                     (st == NSCD_SVC_STATE_FOREIGN_SRC &&
1171                     s->be_version_p[i] == NULL && initf == nscd_initf) ||
1172                     st == NSCD_SVC_STATE_UNINITED ||
1173                     (params.privdb &&
1174                     try_local2(params.dbi, srci) == 1)) {
1175                         nss_endent_u(rootp, initf, contextpp);
1176 
1177                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1178                             NSCD_LOG_LEVEL_DEBUG)
1179                         (me, "backend (%s) not available (state = %d)\n",
1180                             NSCD_NSW_SRC_NAME(srci), st);
1181 
1182                         return;
1183                 }
1184         }
1185 
1186         (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1187 }
1188 
1189 nss_status_t
1190 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1191         nss_getent_t *contextpp, void *args)
1192 {
1193         char                    *me = "nss_getent_u";
1194         nscd_nsw_state_t        *s;
1195         nscd_getent_context_t   *contextp;
1196         int                     n_src;
1197         nss_backend_t           *be;
1198 
1199         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1200         (me, "rootp = %p, initf = %p, contextpp = %p, args = %p\n",
1201             rootp, initf, contextpp, args);
1202 
1203         if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1204                 nss_setent_u(rootp, initf, contextpp);
1205                 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1206                         /* Give up */
1207                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1208                             NSCD_LOG_LEVEL_ERROR)
1209                         (me, "not able to obtain getent context ... give up\n");
1210 
1211                         return (NSS_UNAVAIL);
1212                 }
1213         }
1214 
1215         s       = contextp->nsw_state;
1216         n_src   = contextp->n_src;
1217         be      = contextp->be;
1218 
1219         if (s == 0) {
1220                 /*
1221                  * We've done an end_iter() and haven't done nss_setent()
1222                  * or nss_endent() since;  we should stick in this state
1223                  * until the caller invokes one of those two routines.
1224                  */
1225                 return (NSS_SUCCESS);
1226         }
1227 
1228         while (n_src < s->max_src) {
1229                 nss_status_t            res;
1230                 struct __nsw_lookup_v1  *lkp = NULL;
1231                 int                     n;
1232 
1233                 /* get the nsw config for the current source */
1234                 lkp = s->config->lookups;
1235                 for (n = 0; n < n_src; n++)
1236                         lkp = lkp->next;
1237 
1238                 if (be == 0) {
1239                         /* If it's null it's a bug, but let's play safe */
1240                         res = NSS_UNAVAIL;
1241                 } else {
1242                         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1243                             NSCD_LOG_LEVEL_DEBUG)
1244                         (me, "database: %s, backend: %s, nsswitch config: %s\n",
1245                             NSCD_NSW_DB_NAME(s->dbi),
1246                             lkp->service_name,
1247                             (*s->nsw_cfg_p)->nsw_cfg_str);
1248 
1249                         res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1250                 }
1251 
1252                 if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
1253                         if (res != __NSW_SUCCESS) {
1254                                 end_iter_u(rootp,
1255                                     (struct nss_getent_context *)contextp);
1256                         }
1257                         return (res);
1258                 }
1259                 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1260                 do {
1261                         n_src++;
1262                 } while (n_src < s->max_src &&
1263                     (be = s->be[n_src]) == 0);
1264                 if (be == 0) {
1265                         /*
1266                          * This is the case where we failed to get the backend
1267                          * for the last source. We exhausted all sources.
1268                          */
1269                         nss_endent_u(rootp, initf, contextpp);
1270                         return (NSS_NOTFOUND);
1271                 }
1272                 contextp->n_src      = n_src;
1273                 contextp->be = be;
1274                 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1275         }
1276         /* Got to the end of the sources without finding another entry */
1277         end_iter_u(rootp, (struct nss_getent_context *)contextp);
1278         return (NSS_SUCCESS);
1279         /* success is either a successful entry or end of the sources */
1280 }
1281 
1282 /*ARGSUSED*/
1283 void
1284 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1285         nss_getent_t *contextpp)
1286 {
1287         char                    *me = "nss_endent_u";
1288         nscd_getent_context_t   *contextp;
1289 
1290         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1291         (me, "rootp = %p, initf = %p, contextpp = %p \n",
1292             rootp, initf, contextpp);
1293 
1294         if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1295                 /* nss_endent() on an unused context is a no-op */
1296                 return;
1297         }
1298 
1299         if (_nscd_is_getent_ctx_in_use(contextp) == 0) {
1300                 end_iter_u(rootp, (struct nss_getent_context *)contextp);
1301                 _nscd_put_getent_ctx(contextp);
1302                 contextpp->ctx = NULL;
1303         }
1304 }
1305 
1306 /*
1307  * _nss_db_state_destr() and nss_delete() do nothing in nscd
1308  * but is needed to make the caller (below nscd) happy
1309  */
1310 /*ARGSUSED*/
1311 void
1312 _nss_db_state_destr(struct nss_db_state *s)
1313 {
1314         /* nsw state in nscd is always reused, so do nothing here */
1315 }
1316 
1317 /*ARGSUSED*/
1318 void
1319 nss_delete(nss_db_root_t *rootp)
1320 {
1321         /*
1322          * the only resource kept tracked by the nss_db_root_t
1323          * is the nsw state which is always reused and no need
1324          * to be freed. So just return.
1325          */
1326 }
1327 
1328 /*
1329  * Start of nss_psearch/nss_psetent()/nss_pgetent()/nss_pendent()
1330  * buffers switch entry points
1331  */
1332 
1333 /*
1334  * nss_psearch opens a packed structure header, assembles a local
1335  * nss_XbyY_args_t structure and calls the local copy of nss_search.
1336  * The return data is assembled in "files native format" in the
1337  * return buffer location.  Status if packed back up with the buffer
1338  * and the whole wad is returned to the cache or the client.
1339  */
1340 
1341 void
1342 nss_psearch(void *buffer, size_t length)
1343 {
1344         /* inputs */
1345         nss_db_initf_t          initf;
1346         int                     dbop;
1347         int                     rc;
1348         nss_XbyY_args_t         arg;
1349         nss_status_t            status;
1350         nscd_sw_return_t        swret = { 0 }, *swrp = &swret;
1351         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1352         char                    *me = "nss_psearch";
1353 
1354         if (buffer == NULL || length == 0) {
1355                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1356                 return;
1357         }
1358 
1359         status = nss_packed_arg_init(buffer, length,
1360             NULL, &initf, &dbop, &arg);
1361         if (status != NSS_SUCCESS) {
1362                 NSCD_SET_STATUS(pbuf, status, -1);
1363                 return;
1364         }
1365 
1366         /*
1367          * pass the address of the return data area
1368          * for the switch engine to return its own data
1369          */
1370         (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1371         swret.pbuf = buffer;
1372         swret.pbufsiz = length;
1373         swret.datalen = pbuf->data_len;
1374 
1375         /*
1376          * use the generic nscd_initf for all database lookups
1377          * (the TSD key is the pointer to the packed header)
1378          */
1379         rc = set_initf_key(pbuf);
1380         if (rc != 0) {
1381                 NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1382                 return;
1383         }
1384         initf = nscd_initf;
1385 
1386         /* Perform local search and pack results into return buffer */
1387         /* nscd's search ignores db_root */
1388         status = nss_search(NULL, initf, dbop, &arg);
1389 
1390         /*
1391          * If status is NSS_NOTFOUND and ldap also returned
1392          * NSS_NOTFOUND, it is possible that the user does
1393          * not have a credential, so check and see if
1394          * needs to return NSS_ALTRETRY to let the main
1395          * nscd get a chance to process the lookup
1396          */
1397         if (swret.fallback == 1 && status == NSS_NOTFOUND) {
1398                 OM_uint32       (*func)();
1399                 OM_uint32       stat;
1400                 nscd_rc_t       rc;
1401 
1402                 rc = get_gss_func((void **)&func);
1403                 if (rc == NSCD_SUCCESS) {
1404                         if (func(&stat, GSS_C_NO_CREDENTIAL,
1405                             NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
1406 
1407                                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1408                                     NSCD_LOG_LEVEL_DEBUG)
1409                         (me, "NSS_ALTRETRY: fallback to main nscd needed\n");
1410 
1411                                 status = NSS_ALTRETRY;
1412                         }
1413                 }
1414         }
1415 
1416         NSCD_SET_STATUS(pbuf, status, -1);
1417         errno = swret.errnum;
1418 
1419         /*
1420          * Move result/status from args to packed buffer only if
1421          * arg was being used and rc from the switch engine is not
1422          * NSS_TRYLOCAL.
1423          */
1424         if (!swret.noarg && status != NSS_TRYLOCAL)
1425                 nss_packed_set_status(buffer, length, status,  &arg);
1426 
1427         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1428         (me, "switch engine result: source is %s, status %d, "
1429         "herrno is %d, errno is %s\n",
1430             (swret.srci != -1) ? NSCD_NSW_SRC_NAME(swret.srci) : "<NOTSET>",
1431             pbuf->p_status, pbuf->p_herrno, strerror(pbuf->p_errno));
1432 
1433         /* clear the TSD key used by the generic initf */
1434         clear_initf_key();
1435         pbuf->nscdpriv = 0;
1436 }
1437 
1438 static void
1439 nscd_map_contextp(void *buffer, nss_getent_t *contextp,
1440     nssuint_t **cookie_num_p, nssuint_t **seqnum_p, int setent)
1441 {
1442         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1443         nssuint_t               off;
1444         nscd_getent_context_t   *ctx;
1445         char                    *me = "nscd_map_contextp";
1446         nscd_getent_p1_cookie_t *cookie;
1447 
1448         if (buffer == NULL) {
1449                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1450                 return;
1451         }
1452 
1453         off = pbuf->key_off;
1454         cookie = (nscd_getent_p1_cookie_t *)((void *)((char *)buffer + off));
1455         if (seqnum_p != NULL)
1456                 *seqnum_p = &cookie->p1_seqnum;
1457 
1458         /*
1459          * if called by nss_psetent, and the passed in cookie number
1460          * is NSCD_NEW_COOKIE, then there is no cookie yet, return a
1461          * pointer pointing to where the cookie number will be stored.
1462          * Also because there is no cookie to validate, just return
1463          * success.
1464          *
1465          * On the other hand, if a cookie number is passed in, we need
1466          * to validate the cookie number before returning.
1467          */
1468         if (cookie_num_p != NULL)
1469                 *cookie_num_p = &cookie->p1_cookie_num;
1470         if (setent == 1 && cookie->p1_cookie_num == NSCD_NEW_COOKIE) {
1471                 NSCD_SET_STATUS_SUCCESS(pbuf);
1472                 return;
1473         }
1474 
1475         /*
1476          * If the sequence number and start time match nscd's p0 cookie,
1477          * then either setent was done twice in a row or this is the
1478          * first getent after the setent, return success as well.
1479          */
1480         if (cookie->p1_seqnum == NSCD_P0_COOKIE_SEQNUM) {
1481                 nscd_getent_p0_cookie_t *p0c =
1482                     (nscd_getent_p0_cookie_t *)cookie;
1483                 if (p0c->p0_time == _nscd_get_start_time()) {
1484                         NSCD_SET_STATUS_SUCCESS(pbuf);
1485                         return;
1486                 }
1487         }
1488 
1489         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1490         (me, "cookie # = %lld,  sequence # = %lld\n",
1491             cookie->p1_cookie_num, cookie->p1_seqnum);
1492 
1493         ctx = _nscd_is_getent_ctx(cookie->p1_cookie_num);
1494 
1495         if (ctx == NULL) {
1496                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1497                 (me, "No matching context found (cookie number: %lld)\n",
1498                     cookie->p1_cookie_num);
1499 
1500                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1501                 return;
1502         }
1503 
1504         /* if not called by nss_psetent, verify sequence number */
1505         if (setent != 1 && ctx->seq_num !=
1506             (nscd_seq_num_t)cookie->p1_seqnum) {
1507                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1508                 (me, "invalid sequence # (%lld)\n", cookie->p1_seqnum);
1509 
1510                 _nscd_free_ctx_if_aborted(ctx);
1511                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1512                 return;
1513         }
1514 
1515         contextp->ctx = (struct nss_getent_context *)ctx;
1516 
1517         NSCD_SET_STATUS_SUCCESS(pbuf);
1518 }
1519 
1520 void
1521 nss_psetent(void *buffer, size_t length, pid_t pid)
1522 {
1523         nss_getent_t            context = { 0 };
1524         nss_getent_t            *contextp = &context;
1525         nssuint_t               *cookie_num_p;
1526         nssuint_t               *seqnum_p;
1527         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1528         nscd_getent_p0_cookie_t *p0c;
1529         char                    *me = "nss_psetent";
1530 
1531         if (buffer == NULL || length == 0) {
1532                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1533                 return;
1534         }
1535 
1536         /*
1537          * If this is a per-user nscd, and the user does not have
1538          * the necessary credential, return NSS_TRYLOCAL, so the
1539          * setent/getent can be done locally in the process of the
1540          * setent call
1541          */
1542         if (_whoami == NSCD_CHILD) {
1543                 OM_uint32       (*func)();
1544                 OM_uint32       stat;
1545                 nscd_rc_t       rc;
1546 
1547                 rc = get_gss_func((void **)&func);
1548                 if (rc == NSCD_SUCCESS) {
1549                         if (func(&stat, GSS_C_NO_CREDENTIAL,
1550                             NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
1551 
1552                                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1553                                     NSCD_LOG_LEVEL_DEBUG)
1554                         (me, "NSS_TRYLOCAL: fallback to caller process\n");
1555                                 NSCD_SET_STATUS(pbuf, NSS_TRYLOCAL, 0);
1556                                 return;
1557                         }
1558                 }
1559         }
1560 
1561         /* check cookie number */
1562         nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 1);
1563         if (NSCD_STATUS_IS_NOT_OK(pbuf))
1564                 return;
1565 
1566         /* set cookie number and sequence number */
1567         p0c = (nscd_getent_p0_cookie_t *)cookie_num_p;
1568         if (contextp->ctx ==  NULL) {
1569                 /*
1570                  * first setent (no existing getent context),
1571                  * return a p0 cookie
1572                  */
1573                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1574                 (me, "first setent, no getent context yet\n");
1575         } else {
1576                 /*
1577                  * doing setent on an existing getent context,
1578                  * release resources allocated and return a
1579                  * p0 cookie
1580                  */
1581                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1582                 (me, "setent resetting sequence number = %lld\n",  *seqnum_p);
1583 
1584                 if (_nscd_is_getent_ctx_in_use((nscd_getent_context_t *)
1585                     contextp->ctx) == 0) {
1586                         /*
1587                          * context not in use, release the backend and
1588                          * return the context to the pool
1589                          */
1590                         end_iter_u(NULL, contextp->ctx);
1591                         _nscd_put_getent_ctx(
1592                             (nscd_getent_context_t *)contextp->ctx);
1593                         contextp->ctx = NULL;
1594                 }
1595         }
1596 
1597         p0c->p0_pid = pid;
1598         p0c->p0_time = _nscd_get_start_time();
1599         p0c->p0_seqnum = NSCD_P0_COOKIE_SEQNUM;
1600         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1601         (me, "returning a p0 cookie: pid = %ld, time = %ld, seq #= %llx\n",
1602             p0c->p0_pid, p0c->p0_time, p0c->p0_seqnum);
1603 
1604         NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1605 }
1606 
1607 static void
1608 delayed_setent(nss_pheader_t *pbuf, nss_db_initf_t initf,
1609         nss_getent_t *contextp, nssuint_t *cookie_num_p,
1610         nssuint_t *seqnum_p, pid_t pid)
1611 {
1612         nscd_getent_context_t   *ctx;
1613         nscd_sw_return_t        swret = { 0 }, *swrp = &swret;
1614         char                    *me = "delayed_setent";
1615 
1616         /*
1617          * check credential
1618          */
1619         _nscd_APP_check_cred(pbuf, &pid, "NSCD_DELAYED_SETENT",
1620             NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR);
1621         if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
1622                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1623                 (me, "invalid credential\n");
1624                 return;
1625         }
1626 
1627         /*
1628          * pass the packed header buffer pointer to nss_setent
1629          */
1630         (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1631         swret.pbuf = pbuf;
1632 
1633         /* Perform local setent and set context */
1634         nss_setent(NULL, initf, contextp);
1635 
1636         /* insert cookie info into packed buffer header */
1637         ctx = (nscd_getent_context_t *)contextp->ctx;
1638         if (ctx != NULL) {
1639                 *cookie_num_p = ctx->cookie_num;
1640                 *seqnum_p = ctx->seq_num;
1641                 ctx->pid = pid;
1642         } else {
1643                 /*
1644                  * not able to allocate a getent context, the
1645                  * client should try the enumeration locally
1646                  */
1647                 *cookie_num_p = NSCD_LOCAL_COOKIE;
1648                 *seqnum_p = 0;
1649 
1650                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1651                 (me, "NSS_TRYLOCAL: cookie # = %lld,  sequence # = %lld\n",
1652                     *cookie_num_p, *seqnum_p);
1653                 NSCD_SET_STATUS(pbuf, NSS_TRYLOCAL, 0);
1654                 return;
1655         }
1656 
1657         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1658         (me, "NSS_SUCCESS: cookie # = %lld,  sequence # = %lld\n",
1659             ctx->cookie_num, ctx->seq_num);
1660 
1661         NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1662 }
1663 
1664 void
1665 nss_pgetent(void *buffer, size_t length)
1666 {
1667         /* inputs */
1668         nss_db_initf_t          initf;
1669         nss_getent_t            context = { 0 };
1670         nss_getent_t            *contextp = &context;
1671         nss_XbyY_args_t         arg = { 0};
1672         nss_status_t            status;
1673         nssuint_t               *cookie_num_p;
1674         nssuint_t               *seqnum_p;
1675         nscd_getent_context_t   *ctx;
1676         int                     rc;
1677         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1678         char                    *me = "nss_pgetent";
1679 
1680         if (buffer == NULL || length == 0) {
1681                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1682                 return;
1683         }
1684 
1685         /* verify the cookie passed in */
1686         nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
1687         if (NSCD_STATUS_IS_NOT_OK(pbuf))
1688                 return;
1689 
1690         /*
1691          * use the generic nscd_initf for all the getent requests
1692          * (the TSD key is the pointer to the packed header)
1693          */
1694         rc = set_initf_key(pbuf);
1695         if (rc != 0) {
1696                 NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1697                 return;
1698         }
1699         initf = nscd_initf;
1700 
1701         /* if no context yet, get one */
1702         if (contextp->ctx ==  NULL) {
1703                 nscd_getent_p0_cookie_t *p0c =
1704                     (nscd_getent_p0_cookie_t *)cookie_num_p;
1705 
1706                 delayed_setent(pbuf, initf, contextp, cookie_num_p,
1707                     seqnum_p, p0c->p0_pid);
1708                 if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
1709                         clear_initf_key();
1710                         return;
1711                 }
1712         }
1713 
1714         status = nss_packed_context_init(buffer, length,
1715             NULL, &initf, &contextp, &arg);
1716         if (status != NSS_SUCCESS) {
1717                 clear_initf_key();
1718                 _nscd_free_ctx_if_aborted(
1719                     (nscd_getent_context_t *)contextp->ctx);
1720                 NSCD_SET_STATUS(pbuf, status, -1);
1721                 return;
1722         }
1723 
1724         /* Perform local search and pack results into return buffer */
1725         status = nss_getent(NULL, initf, contextp, &arg);
1726         NSCD_SET_STATUS(pbuf, status, -1);
1727         nss_packed_set_status(buffer, length, status,  &arg);
1728 
1729         /* increment sequence number in the buffer and nscd context */
1730         if (status == NSS_SUCCESS) {
1731                 ctx = (nscd_getent_context_t *)contextp->ctx;
1732                 ctx->seq_num++;
1733                 *seqnum_p = ctx->seq_num;
1734                 *cookie_num_p = ctx->cookie_num;
1735 
1736                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1737                 (me, "getent OK, new sequence # = %lld, len = %lld,"
1738                     " data = >>%s<<\n", *seqnum_p,
1739                     pbuf->data_len, (char *)buffer + pbuf->data_off);
1740 
1741                 _nscd_free_ctx_if_aborted(ctx);
1742         } else {
1743                 /* release the resources used */
1744                 ctx = (nscd_getent_context_t *)contextp->ctx;
1745                 if (ctx != NULL && _nscd_is_getent_ctx_in_use(ctx) == 0) {
1746                         _nscd_put_getent_ctx(ctx);
1747                         contextp->ctx = NULL;
1748                 }
1749                 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1750                 (me, "getent failed, status = %d, sequence # = %lld\n",
1751                     status, *seqnum_p);
1752         }
1753 
1754         /* clear the TSD key used by the generic initf */
1755         clear_initf_key();
1756 }
1757 
1758 void
1759 nss_pendent(void *buffer, size_t length)
1760 {
1761         nss_getent_t            context = { 0 };
1762         nss_getent_t            *contextp = &context;
1763         nssuint_t               *seqnum_p;
1764         nssuint_t               *cookie_num_p;
1765         nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
1766         char                    *me = "nss_pendent";
1767 
1768         if (buffer == NULL || length == 0) {
1769                 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1770                 return;
1771         }
1772 
1773         /* map the contextp from the cookie information */
1774         nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
1775         if (NSCD_STATUS_IS_NOT_OK(pbuf))
1776                 return;
1777 
1778         if (contextp->ctx == NULL)
1779                 return;
1780 
1781         _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1782         (me, "endent, cookie = %lld, sequence # = %lld\n",
1783             *cookie_num_p, *seqnum_p);
1784 
1785         /* Perform local endent and reset context */
1786         nss_endent(NULL, NULL, contextp);
1787 
1788         NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1789 }
1790 
1791 /*ARGSUSED*/
1792 void
1793 nss_pdelete(void *buffer, size_t length)
1794 {
1795         nss_pheader_t   *pbuf = (nss_pheader_t *)buffer;
1796 
1797         /* unnecessary, kept for completeness */
1798         NSCD_SET_STATUS_SUCCESS(pbuf);
1799 }