4075 nscd spuriously thinks all databases are privileged
[illumos-gate.git] / usr / src / cmd / nscd / nscd_switch.c
blob40f0e5da623b90aa29e2350bf9c9254069f40b12
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2012 Milan Jurik. All rights reserved.
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"
42 #pragma weak nss_search = _nss_search
43 #define nss_search _nss_search
45 extern rwlock_t nscd_smf_service_state_lock;
47 /* nscd id: main, forker, or child */
48 extern int _whoami;
50 static int
51 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
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]);
59 return (0);
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);
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);
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);
87 return (0);
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;
98 static int
99 set_loopback_key(lb_key_t *key) {
101 int rc;
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);
108 return (rc);
111 static lb_key_t *
112 get_loopback_key(void) {
114 char *me = "get_loopback_key";
115 lb_key_t *k = NULL;
117 k = pthread_getspecific(loopback_key);
119 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
120 (me, "get loopback key, key = %p\n", k);
122 return (k);
125 static void
126 clear_loopback_key(lb_key_t *key) {
128 char *me = "clear_loopback_key";
130 if (loopback_key != THR_ONCE_KEY && key != NULL) {
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
137 *key->lb_flagp = 0;
138 (void) thr_setspecific(loopback_key, NULL);
141 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
142 (me, "key %p cleared\n", key);
145 static thread_key_t initf_key = THR_ONCE_KEY;
147 static int
148 set_initf_key(void *pbuf) {
150 int rc;
152 rc = thr_keycreate_once(&initf_key, NULL);
153 if (rc == 0)
154 rc = thr_setspecific(initf_key, pbuf);
156 return (rc);
159 static void *
160 get_initf_key(void) {
162 char *me = "get_initf_key";
163 void *pbuf;
165 pbuf = pthread_getspecific(initf_key);
167 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
168 (me, "got initf pbuf, key = %p\n", pbuf);
170 return (pbuf);
173 static void
174 clear_initf_key(void) {
176 char *me = "clear_initf_key";
178 (void) thr_setspecific(initf_key, NULL);
180 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
181 (me, "initf pbuf cleared\n");
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.
193 static nscd_rc_t
194 getparams(
195 int search_fnum,
196 nss_db_initf_t initf,
197 nscd_nsw_params_t *params)
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";
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;
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;
238 * use the switch policy for passwd_compat or
239 * group_compat?
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;
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;
271 * if unsupported database, let caller determine what to do next
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);
279 return (rc);
282 static void
283 nscd_initf(nss_db_params_t *p)
285 nss_pheader_t *pbuf;
286 nssuint_t off;
287 nss_dbd_t *pdbd;
288 char *me = "nscd_initf";
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;
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;
304 off = pbuf->dbd_off;
305 pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
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));
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);
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)
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";
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;
360 if (dbi != -1)
361 db = NSCD_NSW_DB_NAME(dbi);
362 if (srci != -1)
363 src = NSCD_NSW_SRC_NAME(srci);
365 if (arg->buf.result == NULL) {
366 data_str = arg->buf.buffer;
367 data_len = arg->returnlen;
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;
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);
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
390 static int
391 try_local(
392 int dbi,
393 void *arg)
395 struct nss_XbyY_args *ap = (struct nss_XbyY_args *)arg;
396 _priv_execattr *ep;
397 int rc = 0;
398 char *me = "try_local";
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;
405 if (rc != 0) {
407 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
408 (me, "TRYLOCAL: exec_attr:GET_ALL\n");
411 return (rc);
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
421 static int
422 try_local2(
423 int dbi,
424 int srci)
426 int rc = 0;
427 char *me = "try_local2";
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;
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));
441 return (rc);
444 static nscd_rc_t
445 get_lib_func(void **handle, void **func, mutex_t *lock,
446 char *lib, char *name, void **func_p)
448 char *me = "get_lib_func";
449 void *sym;
451 if (func_p != NULL && *handle != NULL && *func != NULL) {
452 *func_p = *func;
453 return (NSCD_SUCCESS);
456 (void) mutex_lock(lock);
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;
465 (void) mutex_unlock(lock);
466 return (NSCD_SUCCESS);
469 if (*handle != NULL && *func != NULL) {
470 *func_p = *func;
471 (void) mutex_unlock(lock);
472 return (NSCD_SUCCESS);
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);
485 if ((sym = dlsym(*handle, name)) == NULL) {
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;
496 (void) mutex_unlock(lock);
497 return (NSCD_SUCCESS);
500 static nscd_rc_t
501 get_libc_nss_search(void **func_p)
503 static void *handle = NULL;
504 static void *func = NULL;
505 static mutex_t lock = DEFAULTMUTEX;
507 return (get_lib_func(&handle, &func, &lock,
508 "libc.so", "nss_search", func_p));
511 static nscd_rc_t
512 get_gss_func(void **func_p)
514 static void *handle = NULL;
515 static void *func = NULL;
516 static mutex_t lock = DEFAULTMUTEX;
518 return (get_lib_func(&handle, &func, &lock,
519 "libgss.so", "gss_inquire_cred", func_p));
522 static nscd_rc_t
523 get_sldap_shadow_func(void **func_p)
525 static void *handle = NULL;
526 static void *func = NULL;
527 static mutex_t lock = DEFAULTMUTEX;
529 return (get_lib_func(&handle, &func, &lock,
530 "libsldap.so", "__ns_ldap_is_shadow_update_enabled",
531 func_p));
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.
543 static nscd_rc_t
544 get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname)
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" }};
556 /* source index: 0 = dns, 1 = mdns */
557 if (strcmp(srcname, "dns") == 0)
558 si = 0;
559 else
560 si = 1;
563 * function index (func[si][dnsi]):
564 * [0,0] = dns/hosts, [0,1] = dns/ipnodes,
565 * [1,0] = mdns/hosts, [1,1] = mdns/ipnodes
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;
577 return (get_lib_func(&handle[si], &func[si][dnsi], &func_lock[si],
578 (char *)lib[si], (char *)func_name[si][dnsi], funcpp));
581 static nss_status_t
582 search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi)
584 nss_status_t (*func)();
585 nss_status_t res = NSS_UNAVAIL;
586 nscd_rc_t rc;
588 swret->noarg = 0;
589 if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0)
590 return (NSS_ERROR);
592 rc = get_dns_funcs(dnsi, &func, srcname);
593 if (rc == NSCD_SUCCESS) {
595 * data_len in the packed buf header may be changed
596 * by the dns or mdns backend, reset it just in
597 * case
599 ((nss_pheader_t *)swret->pbuf)->data_len =
600 swret->datalen;
601 res = (func)(NULL, &swret->pbuf, &swret->pbufsiz);
603 return (res);
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.
610 static int
611 set_fallback_flag(char *srcname, nss_status_t rc)
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);
620 return (0);
623 nss_status_t
624 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
625 void *search_args)
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;
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);
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++;
649 /* determine db index, cfg db index, etc */
650 if (getparams(search_fnum, initf, &params) ==
651 NSCD_CFG_UNSUPPORTED_SWITCH_DB) {
653 * if unsupported database and the request is from the
654 * the door, tell the door client to try it locally
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)();
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;
670 dbi = params.dbi;
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;
682 * for door request that should be processed by the client,
683 * send it back with status NSS_TRYLOCAL
685 if (initf == nscd_initf && try_local(dbi, search_args) == 1) {
686 res = NSS_TRYLOCAL;
687 goto error_exit;
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++;
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)) {
698 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
699 (me, "lookup not enabled for %s\n", NSCD_NSW_DB_NAME(dbi));
701 goto error_exit;
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;
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));
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;
724 if (s == 0) {
725 nscd_rc_t rc;
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);
733 NSCD_SW_STATS_G.lookup_request_queued_g--;
734 NSCD_SW_STATS(dbi).lookup_request_queued--;
736 if (rc != NSCD_SUCCESS)
737 goto error_exit;
739 s = (nscd_nsw_state_t *)root_db.s;
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);
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;
754 res = NSS_UNAVAIL;
756 if (n_src == 0)
757 lkp = s->config->lookups;
758 else
759 lkp = lkp->next;
761 /* set the number of max. retries */
762 if (lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
763 max_retry = lkp->max_retries;
765 srci = (*s->nsw_cfg_p)->src_idx[n_src];
766 if (swret != NULL)
767 swret->srci = srci;
769 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
770 (me, "nsw source = %s\n", NSCD_NSW_SRC_NAME(srci));
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.
777 if (params.privdb == 1 && swret != NULL) {
778 boolean_t (*is_shadow_update_enabled)();
779 boolean_t check_ldap_priv = B_FALSE;
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;
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.
795 if (_whoami == NSCD_CHILD) {
796 res = NSS_ALTRETRY;
797 goto free_nsw_state;
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");
810 goto next_src;
814 /* get state of the (backend) client service */
815 smf_state = _nscd_get_smf_state(srci, dbi, 0);
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;
829 if (check_loopback && k != NULL) {
831 if (k->srci == srci && k->dbi == dbi)
832 if (k->fnum == search_fnum) {
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);
842 NSCD_SW_STATS_G.loopback_nsw_db_skipped_g++;
843 NSCD_SW_STATS(dbi).loopback_nsw_db_skipped++;
844 continue;
848 be = s->be[n_src];
849 if (be != NULL)
850 funcp = NSS_LOOKUP_DBOP(be, search_fnum);
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)))) {
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);
865 goto next_src;
868 do {
870 * we can only retry max_retry times,
871 * otherwise threads may get stuck in this
872 * do-while loop forever
874 if (n_loop > max_retry) {
875 if (swret != NULL)
876 res = NSS_TRYLOCAL;
877 goto free_nsw_state;
881 * set up to prevent loopback
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;
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);
898 * search the backend, if hosts lookups,
899 * try to get the hosts data with ttl first
901 if (params.dnsi >= 0) {
902 res = search_dns_withttl(swret,
903 NSCD_NSW_SRC_NAME(srci), params.dnsi);
905 * if not able to get ttl, fall back
906 * to the regular backend call
908 if (res == NSS_ERROR)
909 res = (*funcp)(be, search_args);
910 else {
912 * status/result are in the
913 * packed buffer, not
914 * search_args
916 swret->noarg = 1;
918 } else
919 res = (*funcp)(be, search_args);
920 if (swret != NULL)
921 swret->errnum = errno;
924 * backend is not up, check and update the
925 * smf state table
927 if (res == NSS_UNAVAIL)
928 (void) _nscd_get_smf_state(srci, dbi, 1);
931 * may need to fall back to use the main nscd
932 * if per-user lookup
934 if (_whoami == NSCD_CHILD && swret != NULL)
935 swret->fallback = set_fallback_flag(
936 NSCD_NSW_SRC_NAME(srci), res);
938 _NSCD_LOG_IF(NSCD_LOG_SWITCH_ENGINE,
939 NSCD_LOG_LEVEL_DEBUG) {
942 * set up to trace the result/status
943 * of the dns/ttl lookup
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;
959 trace_result(dbi, srci, search_fnum, res,
960 (nss_XbyY_args_t *)search_args);
963 n_loop++;
964 } while (retry_test(res, n_loop, lkp));
966 next_src:
968 status_vec |= (1 << res);
970 if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
971 break;
975 free_nsw_state:
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);
984 if (res != NSS_SUCCESS)
985 goto error_exit;
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--;
992 return (NSS_SUCCESS);
994 error_exit:
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--;
1001 return (res);
1005 /* ===> get/set/endent */
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 *);
1018 void
1019 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf,
1020 nss_getent_t *contextpp)
1022 if (contextpp == 0)
1023 return;
1024 nss_setent_u(rootp, initf, contextpp);
1027 nss_status_t
1028 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1029 void *args)
1031 nss_status_t status;
1033 if (contextpp == 0) {
1034 return (NSS_UNAVAIL);
1036 status = nss_getent_u(rootp, initf, contextpp, args);
1037 return (status);
1040 void
1041 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf,
1042 nss_getent_t *contextpp)
1044 if (contextpp == 0)
1045 return;
1046 nss_endent_u(rootp, initf, contextpp);
1049 /*ARGSUSED*/
1050 static void
1051 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1053 nscd_getent_context_t *ctx;
1054 nscd_nsw_state_t *s;
1055 nss_backend_t *be;
1056 int n_src;
1058 ctx = (nscd_getent_context_t *)contextp;
1059 s = ctx->nsw_state;
1060 n_src = ctx->n_src;
1061 be = ctx->be;
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 */
1069 ctx->n_src = 0;
1072 static void
1073 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1074 nss_getent_t *contextpp)
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;
1085 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1086 (me, "rootp = %p, initf = %p, contextpp = %p \n",
1087 rootp, initf, contextpp);
1090 * Get the nsw db index via the initf function. If unsupported
1091 * database, no need to continue
1093 if (getparams(-1, initf, &params) == NSCD_CFG_UNSUPPORTED_SWITCH_DB)
1094 return;
1096 /* get address of the switch engine return data area */
1097 if (initf == nscd_initf)
1098 swret = (nscd_sw_return_t *)params.p.private;
1100 /* if no privilege to look up, return */
1101 if (params.privdb == 1 && swret != NULL &&
1102 _nscd_check_client_priv(NSCD_READ_PRIV) != 0) {
1104 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1105 (me, "no privilege \n");
1106 return;
1109 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1110 if ((_nscd_get_getent_ctx(contextpp, &params)) !=
1111 NSCD_SUCCESS) {
1112 return;
1114 contextp = (nscd_getent_context_t *)contextpp->ctx;
1116 s = contextp->nsw_state;
1118 if (s == 0) {
1119 if (_nscd_get_nsw_state(&root, &params) !=
1120 NSCD_SUCCESS) {
1121 return;
1123 s = (nscd_nsw_state_t *)root.s;
1124 contextp->nsw_state = s;
1126 } else {
1127 s = contextp->nsw_state;
1128 n_src = contextp->n_src;
1129 be = contextp->be;
1130 if (n_src == 0 && be != 0) {
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).
1136 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1137 return;
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 */
1144 for (n_src = 0, be = 0; n_src < s->max_src &&
1145 (be = s->be[n_src]) == 0; n_src++) {
1149 contextp->n_src = n_src;
1150 contextp->be = be;
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;
1159 * make sure all the backends are supported
1161 for (i = 0; i < s->max_src; i++) {
1162 int st, srci;
1164 if (s->be[i] == NULL)
1165 continue;
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);
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);
1182 return;
1186 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
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)
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;
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);
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");
1211 return (NSS_UNAVAIL);
1215 s = contextp->nsw_state;
1216 n_src = contextp->n_src;
1217 be = contextp->be;
1219 if (s == 0) {
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.
1225 return (NSS_SUCCESS);
1228 while (n_src < s->max_src) {
1229 nss_status_t res;
1230 struct __nsw_lookup_v1 *lkp = NULL;
1231 int n;
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;
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);
1249 res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
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);
1257 return (res);
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) {
1266 * This is the case where we failed to get the backend
1267 * for the last source. We exhausted all sources.
1269 nss_endent_u(rootp, initf, contextpp);
1270 return (NSS_NOTFOUND);
1272 contextp->n_src = n_src;
1273 contextp->be = be;
1274 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
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 */
1282 /*ARGSUSED*/
1283 void
1284 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1285 nss_getent_t *contextpp)
1287 char *me = "nss_endent_u";
1288 nscd_getent_context_t *contextp;
1290 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1291 (me, "rootp = %p, initf = %p, contextpp = %p \n",
1292 rootp, initf, contextpp);
1294 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1295 /* nss_endent() on an unused context is a no-op */
1296 return;
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;
1307 * _nss_db_state_destr() and nss_delete() do nothing in nscd
1308 * but is needed to make the caller (below nscd) happy
1310 /*ARGSUSED*/
1311 void
1312 _nss_db_state_destr(struct nss_db_state *s)
1314 /* nsw state in nscd is always reused, so do nothing here */
1317 /*ARGSUSED*/
1318 void
1319 nss_delete(nss_db_root_t *rootp)
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.
1329 * Start of nss_psearch/nss_psetent()/nss_pgetent()/nss_pendent()
1330 * buffers switch entry points
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.
1341 void
1342 nss_psearch(void *buffer, size_t length)
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";
1354 if (buffer == NULL || length == 0) {
1355 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1356 return;
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;
1367 * pass the address of the return data area
1368 * for the switch engine to return its own data
1370 (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1371 swret.pbuf = buffer;
1372 swret.pbufsiz = length;
1373 swret.datalen = pbuf->data_len;
1376 * use the generic nscd_initf for all database lookups
1377 * (the TSD key is the pointer to the packed header)
1379 rc = set_initf_key(pbuf);
1380 if (rc != 0) {
1381 NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1382 return;
1384 initf = nscd_initf;
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);
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
1397 if (swret.fallback == 1 && status == NSS_NOTFOUND) {
1398 OM_uint32 (*func)();
1399 OM_uint32 stat;
1400 nscd_rc_t rc;
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) {
1407 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1408 NSCD_LOG_LEVEL_DEBUG)
1409 (me, "NSS_ALTRETRY: fallback to main nscd needed\n");
1411 status = NSS_ALTRETRY;
1416 NSCD_SET_STATUS(pbuf, status, -1);
1417 errno = swret.errnum;
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.
1424 if (!swret.noarg && status != NSS_TRYLOCAL)
1425 nss_packed_set_status(buffer, length, status, &arg);
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));
1433 /* clear the TSD key used by the generic initf */
1434 clear_initf_key();
1435 pbuf->nscdpriv = 0;
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)
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;
1448 if (buffer == NULL) {
1449 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1450 return;
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;
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.
1465 * On the other hand, if a cookie number is passed in, we need
1466 * to validate the cookie number before returning.
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;
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.
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;
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);
1493 ctx = _nscd_is_getent_ctx(cookie->p1_cookie_num);
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);
1500 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1501 return;
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);
1510 _nscd_free_ctx_if_aborted(ctx);
1511 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1512 return;
1515 contextp->ctx = (struct nss_getent_context *)ctx;
1517 NSCD_SET_STATUS_SUCCESS(pbuf);
1520 void
1521 nss_psetent(void *buffer, size_t length, pid_t pid)
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";
1531 if (buffer == NULL || length == 0) {
1532 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1533 return;
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
1542 if (_whoami == NSCD_CHILD) {
1543 OM_uint32 (*func)();
1544 OM_uint32 stat;
1545 nscd_rc_t rc;
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) {
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;
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;
1566 /* set cookie number and sequence number */
1567 p0c = (nscd_getent_p0_cookie_t *)cookie_num_p;
1568 if (contextp->ctx == NULL) {
1570 * first setent (no existing getent context),
1571 * return a p0 cookie
1573 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1574 (me, "first setent, no getent context yet\n");
1575 } else {
1577 * doing setent on an existing getent context,
1578 * release resources allocated and return a
1579 * p0 cookie
1581 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1582 (me, "setent resetting sequence number = %lld\n", *seqnum_p);
1584 if (_nscd_is_getent_ctx_in_use((nscd_getent_context_t *)
1585 contextp->ctx) == 0) {
1587 * context not in use, release the backend and
1588 * return the context to the pool
1590 end_iter_u(NULL, contextp->ctx);
1591 _nscd_put_getent_ctx(
1592 (nscd_getent_context_t *)contextp->ctx);
1593 contextp->ctx = NULL;
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);
1604 NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
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)
1612 nscd_getent_context_t *ctx;
1613 nscd_sw_return_t swret = { 0 }, *swrp = &swret;
1614 char *me = "delayed_setent";
1617 * check credential
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;
1628 * pass the packed header buffer pointer to nss_setent
1630 (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1631 swret.pbuf = pbuf;
1633 /* Perform local setent and set context */
1634 nss_setent(NULL, initf, contextp);
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 {
1644 * not able to allocate a getent context, the
1645 * client should try the enumeration locally
1647 *cookie_num_p = NSCD_LOCAL_COOKIE;
1648 *seqnum_p = 0;
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;
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);
1661 NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1664 void
1665 nss_pgetent(void *buffer, size_t length)
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";
1680 if (buffer == NULL || length == 0) {
1681 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1682 return;
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;
1691 * use the generic nscd_initf for all the getent requests
1692 * (the TSD key is the pointer to the packed header)
1694 rc = set_initf_key(pbuf);
1695 if (rc != 0) {
1696 NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1697 return;
1699 initf = nscd_initf;
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;
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;
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;
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);
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;
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);
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;
1749 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1750 (me, "getent failed, status = %d, sequence # = %lld\n",
1751 status, *seqnum_p);
1754 /* clear the TSD key used by the generic initf */
1755 clear_initf_key();
1758 void
1759 nss_pendent(void *buffer, size_t length)
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";
1768 if (buffer == NULL || length == 0) {
1769 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1770 return;
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;
1778 if (contextp->ctx == NULL)
1779 return;
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);
1785 /* Perform local endent and reset context */
1786 nss_endent(NULL, NULL, contextp);
1788 NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1791 /*ARGSUSED*/
1792 void
1793 nss_pdelete(void *buffer, size_t length)
1795 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1797 /* unnecessary, kept for completeness */
1798 NSCD_SET_STATUS_SUCCESS(pbuf);