#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <dlfcn.h>
#include <nss_dbdefs.h>
#include <exec_attr.h>
#include <gssapi/gssapi.h>
#include "nscd_door.h"
#include "nscd_switch.h"
#include "nscd_log.h"
#include "nscd_frontend.h"
#pragma weak nss_search = _nss_search
#define nss_search _nss_search
extern rwlock_t nscd_smf_service_state_lock;
extern int _whoami;
static int
retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
{
if (res != NSS_TRYAGAIN && res != NSS_NISSERVDNS_TRYAGAIN) {
if (res == NSS_SUCCESS) {
__NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
__NSW_UNPAUSE_ACTION(
lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
}
return (0);
}
if ((res == NSS_TRYAGAIN &&
lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
(res == NSS_NISSERVDNS_TRYAGAIN &&
lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
return (1);
if (res == NSS_TRYAGAIN &&
lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
if (n <= lkp->max_retries)
return (1);
else {
lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
return (0);
}
if (res == NSS_NISSERVDNS_TRYAGAIN &&
lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
if (n <= lkp->max_retries)
return (1);
else {
lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
__NSW_TRYAGAIN_PAUSED;
return (0);
}
return (0);
}
static thread_key_t loopback_key = THR_ONCE_KEY;
typedef struct lb_key {
int srci;
int dbi;
int fnum;
int *lb_flagp;
} lb_key_t;
static int
set_loopback_key(lb_key_t *key) {
int rc;
rc = thr_keycreate_once(&loopback_key, NULL);
if (rc == 0 && pthread_getspecific(loopback_key) == NULL)
rc = thr_setspecific(loopback_key, key);
return (rc);
}
static lb_key_t *
get_loopback_key(void) {
char *me = "get_loopback_key";
lb_key_t *k = NULL;
k = pthread_getspecific(loopback_key);
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "get loopback key, key = %p\n", k);
return (k);
}
static void
clear_loopback_key(lb_key_t *key) {
char *me = "clear_loopback_key";
if (loopback_key != THR_ONCE_KEY && key != NULL) {
*key->lb_flagp = 0;
(void) thr_setspecific(loopback_key, NULL);
}
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "key %p cleared\n", key);
}
static thread_key_t initf_key = THR_ONCE_KEY;
static int
set_initf_key(void *pbuf) {
int rc;
rc = thr_keycreate_once(&initf_key, NULL);
if (rc == 0)
rc = thr_setspecific(initf_key, pbuf);
return (rc);
}
static void *
get_initf_key(void) {
char *me = "get_initf_key";
void *pbuf;
pbuf = pthread_getspecific(initf_key);
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "got initf pbuf, key = %p\n", pbuf);
return (pbuf);
}
static void
clear_initf_key(void) {
char *me = "clear_initf_key";
(void) thr_setspecific(initf_key, NULL);
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "initf pbuf cleared\n");
}
static nscd_rc_t
getparams(
int search_fnum,
nss_db_initf_t initf,
nscd_nsw_params_t *params)
{
nscd_rc_t rc = NSCD_SUCCESS;
nss_db_params_t *p;
int j;
char *dbn;
const char *n;
char *me = "getparams";
p = ¶ms->p;
(void) memset(params, 0, sizeof (nscd_nsw_params_t));
(*initf)(p);
params->dbi = -1;
params->cfgdbi = -1;
params->compati = -1;
params->dnsi = -1;
n = p->name;
for (j = 0; j < NSCD_NUM_DB; j++) {
dbn = NSCD_NSW_DB_NAME(j);
if (*n != *dbn)
continue;
if (strcmp(n, dbn) == 0) {
params->dbi = j;
if (*n != 'h' && *n != 'i' && *n != 's' && *n != 'a')
break;
if (strcmp(n, NSS_DBNAM_HOSTS) == 0 &&
search_fnum == NSS_DBOP_HOSTS_BYNAME)
params->dnsi = 0;
else if (strcmp(n, NSS_DBNAM_IPNODES) == 0 &&
search_fnum == NSS_DBOP_IPNODES_BYNAME)
params->dnsi = 1;
else if (strcmp(n, NSS_DBNAM_SHADOW) == 0)
params->privdb = 1;
break;
}
}
if (p->config_name != NULL) {
n = p->config_name;
for (j = 0; j < NSCD_NUM_DB; j++) {
dbn = NSCD_NSW_DB_NAME(j);
if (*n == *dbn) {
if (strcmp(n, dbn) == 0) {
params->cfgdbi = j;
break;
}
}
}
}
if (params->cfgdbi != -1) {
if (strstr(p->config_name, "_compat") != NULL) {
n = p->name;
for (j = params->cfgdbi; j < NSCD_NUM_DB; j++) {
dbn = NSCD_NSW_DB_NAME(j);
if (*n == *dbn) {
if (strcmp(n, dbn) == 0) {
params->compati = j;
break;
}
}
}
}
}
if (params->dbi == -1) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "unsupported database: %s\n", p->name);
return (NSCD_CFG_UNSUPPORTED_SWITCH_DB);
}
return (rc);
}
static void
nscd_initf(nss_db_params_t *p)
{
nss_pheader_t *pbuf;
nssuint_t off;
nss_dbd_t *pdbd;
char *me = "nscd_initf";
pbuf = (nss_pheader_t *)get_initf_key();
if (pbuf == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "ERROR: initf key not set\n");
return;
}
if (pbuf->dbd_len <= sizeof (nss_dbd_t)) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "invalid db front params data ? dbd_len = %d\n",
pbuf->dbd_len);
return;
}
off = pbuf->dbd_off;
pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
p->name = (char *)pdbd + pdbd->o_name;
p->config_name = (char *)pdbd + pdbd->o_config_name;
p->default_config = (char *)pdbd + pdbd->o_default_config;
p->flags = (enum nss_dbp_flags)pdbd->flags;
(void) memcpy(&p->private, &pbuf->nscdpriv, sizeof (p->private));
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "db frontend params: name =%s, config_name = %s, "
"default_config = %s, flags = %x\n", p->name,
(p->config_name && *p->config_name != '\0' ?
p->config_name : "<NOT SPECIFIED>"),
(p->default_config && *p->default_config != '\0' ?
p->default_config : "<NOT SPECIFIED>"),
p->flags);
}
static void
trace_result(
int dbi,
int srci,
int op,
nss_status_t res,
nss_XbyY_args_t *arg)
{
char *res_str;
char *src = "?";
char *db = "?";
char *data_str = "<NOT STRING FORMAT>";
int data_len = 0;
char *me = "trace_result";
switch (res) {
case NSS_SUCCESS:
res_str = "NSS_SUCCESS";
break;
case NSS_NOTFOUND:
res_str = "NSS_NOTFOUND";
break;
case NSS_UNAVAIL:
res_str = "NSS_UNAVAIL";
break;
case NSS_TRYAGAIN:
res_str = "NSS_TRYAGAIN";
break;
case NSS_NISSERVDNS_TRYAGAIN:
res_str = "NSS_NISSERVDNS_TRYAGAIN";
break;
default:
res_str = "UNKNOWN STATUS";
break;
}
if (dbi != -1)
db = NSCD_NSW_DB_NAME(dbi);
if (srci != -1)
src = NSCD_NSW_SRC_NAME(srci);
if (arg->buf.result == NULL) {
data_str = arg->buf.buffer;
data_len = arg->returnlen;
}
if (res == NSS_SUCCESS) {
_nscd_logit(me, "%s: database: %s, operation: %d, "
"source: %s returned >>%s<<, length = %d\n",
res_str, db, op, src, data_str, data_len);
return;
}
_nscd_logit(me, "%s: database: %s, operation: %d, source: %s, "
"erange= %d, herrno: %s (%d)\n",
res_str, db, op, src, arg->erange, hstrerror(arg->h_errno),
arg->h_errno);
}
static int
try_local(
int dbi,
void *arg)
{
struct nss_XbyY_args *ap = (struct nss_XbyY_args *)arg;
_priv_execattr *ep;
int rc = 0;
char *me = "try_local";
if (strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_EXECATTR) == 0) {
if ((ep = ap->key.attrp) != NULL && IS_GET_ALL(ep->search_flag))
rc = 1;
}
if (rc != 0) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "TRYLOCAL: exec_attr:GET_ALL\n");
}
return (rc);
}
static int
try_local2(
int dbi,
int srci)
{
int rc = 0;
char *me = "try_local2";
if (*NSCD_NSW_DB_NAME(dbi) == 's' &&
strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_SHADOW) == 0) {
if (strcmp(NSCD_NSW_SRC_NAME(srci), "compat") == 0)
rc = 1;
}
if (rc != 0) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "TRYLOCAL: database: shadow, source: %s\n",
NSCD_NSW_SRC_NAME(srci));
}
return (rc);
}
static nscd_rc_t
get_lib_func(void **handle, void **func, mutex_t *lock,
char *lib, char *name, void **func_p)
{
char *me = "get_lib_func";
void *sym;
if (func_p != NULL && *handle != NULL && *func != NULL) {
*func_p = *func;
return (NSCD_SUCCESS);
}
(void) mutex_lock(lock);
if (func_p == NULL) {
if (*handle != NULL) {
(void) dlclose(*handle);
*handle = NULL;
*func = NULL;
}
(void) mutex_unlock(lock);
return (NSCD_SUCCESS);
}
if (*handle != NULL && *func != NULL) {
*func_p = *func;
(void) mutex_unlock(lock);
return (NSCD_SUCCESS);
}
if (*handle == NULL) {
*handle = dlopen(lib, RTLD_LAZY);
if (*handle == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
(me, "unable to dlopen %s\n", lib);
(void) mutex_unlock(lock);
return (NSCD_CFG_DLOPEN_ERROR);
}
}
if ((sym = dlsym(*handle, name)) == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
(me, "unable to find symbol %s:%s\n", lib, name);
(void) mutex_unlock(lock);
return (NSCD_CFG_DLSYM_ERROR);
} else {
*func_p = sym;
*func = sym;
}
(void) mutex_unlock(lock);
return (NSCD_SUCCESS);
}
static nscd_rc_t
get_libc_nss_search(void **func_p)
{
static void *handle = NULL;
static void *func = NULL;
static mutex_t lock = DEFAULTMUTEX;
return (get_lib_func(&handle, &func, &lock,
"libc.so", "nss_search", func_p));
}
static nscd_rc_t
get_gss_func(void **func_p)
{
static void *handle = NULL;
static void *func = NULL;
static mutex_t lock = DEFAULTMUTEX;
return (get_lib_func(&handle, &func, &lock,
"libgss.so", "gss_inquire_cred", func_p));
}
static nscd_rc_t
get_sldap_shadow_func(void **func_p)
{
static void *handle = NULL;
static void *func = NULL;
static mutex_t lock = DEFAULTMUTEX;
return (get_lib_func(&handle, &func, &lock,
"libsldap.so", "__ns_ldap_is_shadow_update_enabled",
func_p));
}
static nscd_rc_t
get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname)
{
int si;
void **funcpp;
static void *handle[2] = { NULL, NULL };
static mutex_t func_lock[2] = { DEFAULTMUTEX, DEFAULTMUTEX };
static void *func[2][2] = {{NULL, NULL}, {NULL, NULL}};
static const char *lib[2] = { "nss_dns.so.1", "nss_mdns.so.1" };
static const char *func_name[2][2] =
{{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" },
{ "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }};
if (strcmp(srcname, "dns") == 0)
si = 0;
else
si = 1;
if (dnsi < 0) {
funcpp = NULL;
(void) mutex_lock(&func_lock[si]);
func[si][0] = NULL;
func[si][1] = NULL;
(void) mutex_unlock(&func_lock[si]);
} else
funcpp = (void **)func_p;
return (get_lib_func(&handle[si], &func[si][dnsi], &func_lock[si],
(char *)lib[si], (char *)func_name[si][dnsi], funcpp));
}
static nss_status_t
search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi)
{
nss_status_t (*func)();
nss_status_t res = NSS_UNAVAIL;
nscd_rc_t rc;
swret->noarg = 0;
if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0)
return (NSS_ERROR);
rc = get_dns_funcs(dnsi, &func, srcname);
if (rc == NSCD_SUCCESS) {
((nss_pheader_t *)swret->pbuf)->data_len =
swret->datalen;
res = (func)(NULL, &swret->pbuf, &swret->pbufsiz);
}
return (res);
}
static int
set_fallback_flag(char *srcname, nss_status_t rc)
{
char *me = "set_fallback_flag";
if (strcmp(srcname, "ldap") == 0 && rc == NSS_NOTFOUND) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "NSS_NOTFOUND (ldap): fallback to main nscd "
"may be needed\n");
return (1);
}
return (0);
}
nss_status_t
nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
void *search_args)
{
char *me = "nss_search";
nss_status_t res = NSS_UNAVAIL;
nscd_nsw_state_t *s = NULL;
int n_src;
unsigned int status_vec = 0;
int dbi, srci = -1;
int check_loopback = 0;
int state_thr = 0;
lb_key_t key, *k = NULL;
nss_db_root_t root_db;
nscd_nsw_params_t params;
nscd_sw_return_t *swret;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "rootp = %p, initf = %p, search_fnum = %d, "
"search_args = %p\n", rootp, initf,
search_fnum, search_args);
NSCD_SW_STATS_G.lookup_request_received_g++;
NSCD_SW_STATS_G.lookup_request_in_progress_g++;
NSCD_SW_STATS_G.lookup_request_queued_g++;
if (getparams(search_fnum, initf, ¶ms) ==
NSCD_CFG_UNSUPPORTED_SWITCH_DB) {
if (initf == nscd_initf) {
res = NSS_TRYLOCAL;
goto error_exit;
} else {
nss_status_t (*func)();
if (get_libc_nss_search((void **)&func) ==
NSCD_SUCCESS)
return ((func)(rootp, initf, search_fnum,
search_args));
else
goto error_exit;
}
}
dbi = params.dbi;
if (initf == nscd_initf) {
swret = (nscd_sw_return_t *)params.p.private;
swret->srci = -1;
} else {
swret = NULL;
params.dnsi = -1;
}
if (initf == nscd_initf && try_local(dbi, search_args) == 1) {
res = NSS_TRYLOCAL;
goto error_exit;
}
NSCD_SW_STATS(dbi).lookup_request_received++;
NSCD_SW_STATS(dbi).lookup_request_in_progress++;
NSCD_SW_STATS(dbi).lookup_request_queued++;
if (!(NSCD_SW_CFG_G.enable_lookup_g == nscd_true &&
NSCD_SW_CFG(dbi).enable_lookup == nscd_true)) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "lookup not enabled for %s\n", NSCD_NSW_DB_NAME(dbi));
goto error_exit;
}
if (NSCD_SW_CFG_G.enable_loopback_checking_g == nscd_true &&
NSCD_SW_CFG(dbi).enable_loopback_checking == nscd_true) {
check_loopback = 1;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "loopback checking enabled for %s\n",
NSCD_NSW_DB_NAME(dbi));
}
if (check_loopback) {
k = get_loopback_key();
if (k != NULL) {
if (k->dbi != dbi || k->fnum != search_fnum) {
clear_loopback_key(k);
k = NULL;
}
}
}
if (s == 0) {
nscd_rc_t rc;
if (check_loopback) {
rc = _nscd_get_nsw_state_thread(&root_db, ¶ms);
state_thr = 1;
} else
rc = _nscd_get_nsw_state(&root_db, ¶ms);
NSCD_SW_STATS_G.lookup_request_queued_g--;
NSCD_SW_STATS(dbi).lookup_request_queued--;
if (rc != NSCD_SUCCESS)
goto error_exit;
s = (nscd_nsw_state_t *)root_db.s;
}
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "database = %s, config = >>%s<<\n", NSCD_NSW_DB_NAME(dbi),
(*s->nsw_cfg_p)->nsw_cfg_str);
for (n_src = 0; n_src < s->max_src; n_src++) {
nss_backend_t *be = NULL;
nss_backend_op_t funcp = NULL;
struct __nsw_lookup_v1 *lkp;
int smf_state;
int n_loop = 0;
int max_retry = 10;
res = NSS_UNAVAIL;
if (n_src == 0)
lkp = s->config->lookups;
else
lkp = lkp->next;
if (lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
max_retry = lkp->max_retries;
srci = (*s->nsw_cfg_p)->src_idx[n_src];
if (swret != NULL)
swret->srci = srci;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "nsw source = %s\n", NSCD_NSW_SRC_NAME(srci));
if (params.privdb == 1 && swret != NULL) {
boolean_t (*is_shadow_update_enabled)();
boolean_t check_ldap_priv = B_FALSE;
if (strcmp(NSCD_NSW_SRC_NAME(srci), "ldap") == 0) {
if (get_sldap_shadow_func(
(void **)&is_shadow_update_enabled) ==
NSCD_SUCCESS &&
is_shadow_update_enabled()) {
check_ldap_priv = B_TRUE;
if (_whoami == NSCD_CHILD) {
res = NSS_ALTRETRY;
goto free_nsw_state;
}
}
}
if ((strcmp(NSCD_NSW_SRC_NAME(srci), "files") == 0 &&
_nscd_check_client_priv(NSCD_READ_PRIV) != 0) ||
(check_ldap_priv &&
_nscd_check_client_priv(NSCD_ALL_PRIV) != 0)) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "no privilege to look up, skip source\n");
goto next_src;
}
}
smf_state = _nscd_get_smf_state(srci, dbi, 0);
if (initf == nscd_initf &&
(smf_state == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
(smf_state == NSCD_SVC_STATE_FOREIGN_SRC &&
s->be_version_p[n_src] == NULL) ||
(params.privdb && try_local2(dbi, srci) == 1))) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "returning TRYLOCAL ... \n");
res = NSS_TRYLOCAL;
goto free_nsw_state;
}
if (check_loopback && k != NULL) {
if (k->srci == srci && k->dbi == dbi)
if (k->fnum == search_fnum) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "loopback detected: "
"source = %s, database = %s "
"search fnum = %d\n",
NSCD_NSW_SRC_NAME(srci),
NSCD_NSW_DB_NAME(dbi), search_fnum);
NSCD_SW_STATS_G.loopback_nsw_db_skipped_g++;
NSCD_SW_STATS(dbi).loopback_nsw_db_skipped++;
continue;
}
}
be = s->be[n_src];
if (be != NULL)
funcp = NSS_LOOKUP_DBOP(be, search_fnum);
if (be == NULL || (params.dnsi < 0 && (funcp == NULL ||
(smf_state != NSCD_SVC_STATE_UNINITED &&
smf_state != NSCD_SVC_STATE_UNSUPPORTED_SRC &&
smf_state != NSCD_SVC_STATE_FOREIGN_SRC &&
smf_state < SCF_STATE_ONLINE)))) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "unable to look up source %s: be = %p, "
"smf state = %d, funcp = %p\n",
NSCD_NSW_SRC_NAME(srci), be, smf_state, funcp);
goto next_src;
}
do {
if (n_loop > max_retry) {
if (swret != NULL)
res = NSS_TRYLOCAL;
goto free_nsw_state;
}
if (check_loopback && k == NULL) {
key.srci = srci;
key.dbi = dbi;
key.fnum = search_fnum;
key.lb_flagp = &check_loopback;
(void) set_loopback_key(&key);
k = &key;
}
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "looking up source = %s, loop# = %d \n",
NSCD_NSW_SRC_NAME(srci), n_loop);
if (params.dnsi >= 0) {
res = search_dns_withttl(swret,
NSCD_NSW_SRC_NAME(srci), params.dnsi);
if (res == NSS_ERROR)
res = (*funcp)(be, search_args);
else {
swret->noarg = 1;
}
} else
res = (*funcp)(be, search_args);
if (swret != NULL)
swret->errnum = errno;
if (res == NSS_UNAVAIL)
(void) _nscd_get_smf_state(srci, dbi, 1);
if (_whoami == NSCD_CHILD && swret != NULL)
swret->fallback = set_fallback_flag(
NSCD_NSW_SRC_NAME(srci), res);
_NSCD_LOG_IF(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG) {
if (swret != NULL && swret->noarg == 1) {
nss_pheader_t *phdr;
struct nss_XbyY_args *arg;
arg = (struct nss_XbyY_args *)
search_args;
phdr = (nss_pheader_t *)swret->pbuf;
arg->buf.buffer = (char *)phdr +
phdr->data_off;
arg->returnlen = phdr->data_len;
if (phdr->p_errno == ERANGE)
arg->erange = 1;
arg->h_errno = phdr->p_herrno;
}
trace_result(dbi, srci, search_fnum, res,
(nss_XbyY_args_t *)search_args);
}
n_loop++;
} while (retry_test(res, n_loop, lkp));
next_src:
status_vec |= (1 << res);
if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
break;
}
}
free_nsw_state:
if (state_thr == 1)
_nscd_put_nsw_state_thread(s);
else
_nscd_put_nsw_state(s);
if (check_loopback && k != NULL)
clear_loopback_key(k);
if (res != NSS_SUCCESS)
goto error_exit;
NSCD_SW_STATS_G.lookup_request_succeeded_g++;
NSCD_SW_STATS(dbi).lookup_request_succeeded++;
NSCD_SW_STATS_G.lookup_request_in_progress_g--;
NSCD_SW_STATS(dbi).lookup_request_in_progress--;
return (NSS_SUCCESS);
error_exit:
NSCD_SW_STATS_G.lookup_request_failed_g++;
NSCD_SW_STATS_G.lookup_request_in_progress_g--;
NSCD_SW_STATS(dbi).lookup_request_failed++;
NSCD_SW_STATS(dbi).lookup_request_in_progress--;
return (res);
}
static void nss_setent_u(nss_db_root_t *,
nss_db_initf_t,
nss_getent_t *);
static nss_status_t nss_getent_u(nss_db_root_t *,
nss_db_initf_t,
nss_getent_t *,
void *);
static void nss_endent_u(nss_db_root_t *,
nss_db_initf_t,
nss_getent_t *);
void
nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf,
nss_getent_t *contextpp)
{
if (contextpp == 0)
return;
nss_setent_u(rootp, initf, contextpp);
}
nss_status_t
nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
void *args)
{
nss_status_t status;
if (contextpp == 0) {
return (NSS_UNAVAIL);
}
status = nss_getent_u(rootp, initf, contextpp, args);
return (status);
}
void
nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf,
nss_getent_t *contextpp)
{
if (contextpp == 0)
return;
nss_endent_u(rootp, initf, contextpp);
}
static void
end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
{
nscd_getent_context_t *ctx;
nscd_nsw_state_t *s;
nss_backend_t *be;
int n_src;
ctx = (nscd_getent_context_t *)contextp;
s = ctx->nsw_state;
n_src = ctx->n_src;
be = ctx->be;
if (s != 0) {
if (n_src < s->max_src && be != 0) {
(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
ctx->be = 0;
}
}
ctx->n_src = 0;
}
static void
nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
nss_getent_t *contextpp)
{
char *me = "nss_setent_u";
nscd_nsw_state_t *s;
nscd_getent_context_t *contextp;
nscd_nsw_params_t params;
nss_db_root_t root;
nss_backend_t *be;
int n_src, i;
nscd_sw_return_t *swret = NULL;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "rootp = %p, initf = %p, contextpp = %p \n",
rootp, initf, contextpp);
if (getparams(-1, initf, ¶ms) == NSCD_CFG_UNSUPPORTED_SWITCH_DB)
return;
if (initf == nscd_initf)
swret = (nscd_sw_return_t *)params.p.private;
if (params.privdb == 1 && swret != NULL &&
_nscd_check_client_priv(NSCD_READ_PRIV) != 0) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "no privilege \n");
return;
}
if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
if ((_nscd_get_getent_ctx(contextpp, ¶ms)) !=
NSCD_SUCCESS) {
return;
}
contextp = (nscd_getent_context_t *)contextpp->ctx;
}
s = contextp->nsw_state;
if (s == 0) {
if (_nscd_get_nsw_state(&root, ¶ms) !=
NSCD_SUCCESS) {
return;
}
s = (nscd_nsw_state_t *)root.s;
contextp->nsw_state = s;
} else {
s = contextp->nsw_state;
n_src = contextp->n_src;
be = contextp->be;
if (n_src == 0 && be != 0) {
(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
return;
}
if (n_src < s->max_src && be != 0) {
(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
contextp->be = 0;
}
}
for (n_src = 0, be = 0; n_src < s->max_src &&
(be = s->be[n_src]) == 0; n_src++) {
;
}
contextp->n_src = n_src;
contextp->be = be;
if (be == 0) {
nss_endent_u(rootp, initf, contextpp);
return;
}
for (i = 0; i < s->max_src; i++) {
int st, srci;
if (s->be[i] == NULL)
continue;
srci = (*s->nsw_cfg_p)->src_idx[i];
st = _nscd_get_smf_state(srci, params.dbi, 1);
if (st == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
(st == NSCD_SVC_STATE_FOREIGN_SRC &&
s->be_version_p[i] == NULL && initf == nscd_initf) ||
st == NSCD_SVC_STATE_UNINITED ||
(params.privdb &&
try_local2(params.dbi, srci) == 1)) {
nss_endent_u(rootp, initf, contextpp);
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "backend (%s) not available (state = %d)\n",
NSCD_NSW_SRC_NAME(srci), st);
return;
}
}
(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
}
nss_status_t
nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
nss_getent_t *contextpp, void *args)
{
char *me = "nss_getent_u";
nscd_nsw_state_t *s;
nscd_getent_context_t *contextp;
int n_src;
nss_backend_t *be;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "rootp = %p, initf = %p, contextpp = %p, args = %p\n",
rootp, initf, contextpp, args);
if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
nss_setent_u(rootp, initf, contextpp);
if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_ERROR)
(me, "not able to obtain getent context ... give up\n");
return (NSS_UNAVAIL);
}
}
s = contextp->nsw_state;
n_src = contextp->n_src;
be = contextp->be;
if (s == 0) {
return (NSS_SUCCESS);
}
while (n_src < s->max_src) {
nss_status_t res;
struct __nsw_lookup_v1 *lkp = NULL;
int n;
lkp = s->config->lookups;
for (n = 0; n < n_src; n++)
lkp = lkp->next;
if (be == 0) {
res = NSS_UNAVAIL;
} else {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "database: %s, backend: %s, nsswitch config: %s\n",
NSCD_NSW_DB_NAME(s->dbi),
lkp->service_name,
(*s->nsw_cfg_p)->nsw_cfg_str);
res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
}
if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
if (res != __NSW_SUCCESS) {
end_iter_u(rootp,
(struct nss_getent_context *)contextp);
}
return (res);
}
(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
do {
n_src++;
} while (n_src < s->max_src &&
(be = s->be[n_src]) == 0);
if (be == 0) {
nss_endent_u(rootp, initf, contextpp);
return (NSS_NOTFOUND);
}
contextp->n_src = n_src;
contextp->be = be;
(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
}
end_iter_u(rootp, (struct nss_getent_context *)contextp);
return (NSS_SUCCESS);
}
void
nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
nss_getent_t *contextpp)
{
char *me = "nss_endent_u";
nscd_getent_context_t *contextp;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "rootp = %p, initf = %p, contextpp = %p \n",
rootp, initf, contextpp);
if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
return;
}
if (_nscd_is_getent_ctx_in_use(contextp) == 0) {
end_iter_u(rootp, (struct nss_getent_context *)contextp);
_nscd_put_getent_ctx(contextp);
contextpp->ctx = NULL;
}
}
void
_nss_db_state_destr(struct nss_db_state *s)
{
}
void
nss_delete(nss_db_root_t *rootp)
{
}
void
nss_psearch(void *buffer, size_t length)
{
nss_db_initf_t initf;
int dbop;
int rc;
nss_XbyY_args_t arg;
nss_status_t status;
nscd_sw_return_t swret = { 0 }, *swrp = &swret;
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
char *me = "nss_psearch";
if (buffer == NULL || length == 0) {
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
status = nss_packed_arg_init(buffer, length,
NULL, &initf, &dbop, &arg);
if (status != NSS_SUCCESS) {
NSCD_SET_STATUS(pbuf, status, -1);
return;
}
(void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
swret.pbuf = buffer;
swret.pbufsiz = length;
swret.datalen = pbuf->data_len;
rc = set_initf_key(pbuf);
if (rc != 0) {
NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
return;
}
initf = nscd_initf;
status = nss_search(NULL, initf, dbop, &arg);
if (swret.fallback == 1 && status == NSS_NOTFOUND) {
OM_uint32 (*func)();
OM_uint32 stat;
nscd_rc_t rc;
rc = get_gss_func((void **)&func);
if (rc == NSCD_SUCCESS) {
if (func(&stat, GSS_C_NO_CREDENTIAL,
NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "NSS_ALTRETRY: fallback to main nscd needed\n");
status = NSS_ALTRETRY;
}
}
}
NSCD_SET_STATUS(pbuf, status, -1);
errno = swret.errnum;
if (!swret.noarg && status != NSS_TRYLOCAL)
nss_packed_set_status(buffer, length, status, &arg);
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "switch engine result: source is %s, status %d, "
"herrno is %d, errno is %s\n",
(swret.srci != -1) ? NSCD_NSW_SRC_NAME(swret.srci) : "<NOTSET>",
pbuf->p_status, pbuf->p_herrno, strerror(pbuf->p_errno));
clear_initf_key();
pbuf->nscdpriv = 0;
}
static void
nscd_map_contextp(void *buffer, nss_getent_t *contextp,
nssuint_t **cookie_num_p, nssuint_t **seqnum_p, int setent)
{
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
nssuint_t off;
nscd_getent_context_t *ctx;
char *me = "nscd_map_contextp";
nscd_getent_p1_cookie_t *cookie;
if (buffer == NULL) {
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
off = pbuf->key_off;
cookie = (nscd_getent_p1_cookie_t *)((void *)((char *)buffer + off));
if (seqnum_p != NULL)
*seqnum_p = &cookie->p1_seqnum;
if (cookie_num_p != NULL)
*cookie_num_p = &cookie->p1_cookie_num;
if (setent == 1 && cookie->p1_cookie_num == NSCD_NEW_COOKIE) {
NSCD_SET_STATUS_SUCCESS(pbuf);
return;
}
if (cookie->p1_seqnum == NSCD_P0_COOKIE_SEQNUM) {
nscd_getent_p0_cookie_t *p0c =
(nscd_getent_p0_cookie_t *)cookie;
if (p0c->p0_time == _nscd_get_start_time()) {
NSCD_SET_STATUS_SUCCESS(pbuf);
return;
}
}
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "cookie # = %lld, sequence # = %lld\n",
cookie->p1_cookie_num, cookie->p1_seqnum);
ctx = _nscd_is_getent_ctx(cookie->p1_cookie_num);
if (ctx == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "No matching context found (cookie number: %lld)\n",
cookie->p1_cookie_num);
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
if (setent != 1 && ctx->seq_num !=
(nscd_seq_num_t)cookie->p1_seqnum) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "invalid sequence # (%lld)\n", cookie->p1_seqnum);
_nscd_free_ctx_if_aborted(ctx);
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
contextp->ctx = (struct nss_getent_context *)ctx;
NSCD_SET_STATUS_SUCCESS(pbuf);
}
void
nss_psetent(void *buffer, size_t length, pid_t pid)
{
nss_getent_t context = { 0 };
nss_getent_t *contextp = &context;
nssuint_t *cookie_num_p;
nssuint_t *seqnum_p;
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
nscd_getent_p0_cookie_t *p0c;
char *me = "nss_psetent";
if (buffer == NULL || length == 0) {
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
if (_whoami == NSCD_CHILD) {
OM_uint32 (*func)();
OM_uint32 stat;
nscd_rc_t rc;
rc = get_gss_func((void **)&func);
if (rc == NSCD_SUCCESS) {
if (func(&stat, GSS_C_NO_CREDENTIAL,
NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
NSCD_LOG_LEVEL_DEBUG)
(me, "NSS_TRYLOCAL: fallback to caller process\n");
NSCD_SET_STATUS(pbuf, NSS_TRYLOCAL, 0);
return;
}
}
}
nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 1);
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
p0c = (nscd_getent_p0_cookie_t *)cookie_num_p;
if (contextp->ctx == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "first setent, no getent context yet\n");
} else {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "setent resetting sequence number = %lld\n", *seqnum_p);
if (_nscd_is_getent_ctx_in_use((nscd_getent_context_t *)
contextp->ctx) == 0) {
end_iter_u(NULL, contextp->ctx);
_nscd_put_getent_ctx(
(nscd_getent_context_t *)contextp->ctx);
contextp->ctx = NULL;
}
}
p0c->p0_pid = pid;
p0c->p0_time = _nscd_get_start_time();
p0c->p0_seqnum = NSCD_P0_COOKIE_SEQNUM;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "returning a p0 cookie: pid = %ld, time = %ld, seq #= %llx\n",
p0c->p0_pid, p0c->p0_time, p0c->p0_seqnum);
NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
}
static void
delayed_setent(nss_pheader_t *pbuf, nss_db_initf_t initf,
nss_getent_t *contextp, nssuint_t *cookie_num_p,
nssuint_t *seqnum_p, pid_t pid)
{
nscd_getent_context_t *ctx;
nscd_sw_return_t swret = { 0 }, *swrp = &swret;
char *me = "delayed_setent";
_nscd_APP_check_cred(pbuf, &pid, "NSCD_DELAYED_SETENT",
NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR);
if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "invalid credential\n");
return;
}
(void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
swret.pbuf = pbuf;
nss_setent(NULL, initf, contextp);
ctx = (nscd_getent_context_t *)contextp->ctx;
if (ctx != NULL) {
*cookie_num_p = ctx->cookie_num;
*seqnum_p = ctx->seq_num;
ctx->pid = pid;
} else {
*cookie_num_p = NSCD_LOCAL_COOKIE;
*seqnum_p = 0;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "NSS_TRYLOCAL: cookie # = %lld, sequence # = %lld\n",
*cookie_num_p, *seqnum_p);
NSCD_SET_STATUS(pbuf, NSS_TRYLOCAL, 0);
return;
}
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "NSS_SUCCESS: cookie # = %lld, sequence # = %lld\n",
ctx->cookie_num, ctx->seq_num);
NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
}
void
nss_pgetent(void *buffer, size_t length)
{
nss_db_initf_t initf;
nss_getent_t context = { 0 };
nss_getent_t *contextp = &context;
nss_XbyY_args_t arg = { 0};
nss_status_t status;
nssuint_t *cookie_num_p;
nssuint_t *seqnum_p;
nscd_getent_context_t *ctx;
int rc;
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
char *me = "nss_pgetent";
if (buffer == NULL || length == 0) {
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
rc = set_initf_key(pbuf);
if (rc != 0) {
NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
return;
}
initf = nscd_initf;
if (contextp->ctx == NULL) {
nscd_getent_p0_cookie_t *p0c =
(nscd_getent_p0_cookie_t *)cookie_num_p;
delayed_setent(pbuf, initf, contextp, cookie_num_p,
seqnum_p, p0c->p0_pid);
if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
clear_initf_key();
return;
}
}
status = nss_packed_context_init(buffer, length,
NULL, &initf, &contextp, &arg);
if (status != NSS_SUCCESS) {
clear_initf_key();
_nscd_free_ctx_if_aborted(
(nscd_getent_context_t *)contextp->ctx);
NSCD_SET_STATUS(pbuf, status, -1);
return;
}
status = nss_getent(NULL, initf, contextp, &arg);
NSCD_SET_STATUS(pbuf, status, -1);
nss_packed_set_status(buffer, length, status, &arg);
if (status == NSS_SUCCESS) {
ctx = (nscd_getent_context_t *)contextp->ctx;
ctx->seq_num++;
*seqnum_p = ctx->seq_num;
*cookie_num_p = ctx->cookie_num;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "getent OK, new sequence # = %lld, len = %lld,"
" data = >>%s<<\n", *seqnum_p,
pbuf->data_len, (char *)buffer + pbuf->data_off);
_nscd_free_ctx_if_aborted(ctx);
} else {
ctx = (nscd_getent_context_t *)contextp->ctx;
if (ctx != NULL && _nscd_is_getent_ctx_in_use(ctx) == 0) {
_nscd_put_getent_ctx(ctx);
contextp->ctx = NULL;
}
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "getent failed, status = %d, sequence # = %lld\n",
status, *seqnum_p);
}
clear_initf_key();
}
void
nss_pendent(void *buffer, size_t length)
{
nss_getent_t context = { 0 };
nss_getent_t *contextp = &context;
nssuint_t *seqnum_p;
nssuint_t *cookie_num_p;
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
char *me = "nss_pendent";
if (buffer == NULL || length == 0) {
NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
return;
}
nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
if (contextp->ctx == NULL)
return;
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
(me, "endent, cookie = %lld, sequence # = %lld\n",
*cookie_num_p, *seqnum_p);
nss_endent(NULL, NULL, contextp);
NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
}
void
nss_pdelete(void *buffer, size_t length)
{
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
NSCD_SET_STATUS_SUCCESS(pbuf);
}