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