nscd_switch.c revision 7d7551bcfe5ded1738ddbe3268520996a32023b4
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2012 Milan Jurik. All rights reserved.
*/
#include <stdlib.h> /* getenv() */
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <dlfcn.h>
#include <nss_dbdefs.h>
#include <exec_attr.h>
#include "nscd_door.h"
#include "nscd_switch.h"
#include "nscd_log.h"
#include "nscd_frontend.h"
#define nss_search _nss_search
extern rwlock_t nscd_smf_service_state_lock;
/* nscd id: main, forker, or child */
extern int _whoami;
static int
{
if (res == NSS_SUCCESS) {
}
return (0);
}
if ((res == NSS_TRYAGAIN &&
(res == NSS_NISSERVDNS_TRYAGAIN &&
return (1);
if (res == NSS_TRYAGAIN &&
if (n <= lkp->max_retries)
return (1);
else {
return (0);
}
if (res == NSS_NISSERVDNS_TRYAGAIN &&
if (n <= lkp->max_retries)
return (1);
else {
return (0);
}
return (0);
}
typedef struct lb_key {
int srci;
int dbi;
int fnum;
int *lb_flagp;
} lb_key_t;
static int
int rc;
/* set key if not already set */
return (rc);
}
static lb_key_t *
get_loopback_key(void) {
char *me = "get_loopback_key";
(me, "get loopback key, key = %p\n", k);
return (k);
}
static void
char *me = "clear_loopback_key";
/*
* key->lb_flagp points to the location of the
* flag, check_flag, in the stack where it was
* first set; clearing the flag tells that
* stack the loopback error has been resolved
*/
}
}
static int
set_initf_key(void *pbuf) {
int rc;
if (rc == 0)
return (rc);
}
static void *
get_initf_key(void) {
char *me = "get_initf_key";
void *pbuf;
return (pbuf);
}
static void
clear_initf_key(void) {
char *me = "clear_initf_key";
(me, "initf pbuf cleared\n");
}
/*
* Call the input initf function to extract the
* NSS front end parameters and examine them to
* determine if an NSS lookup is to be performed
* on a regular or a pseudo (called from compat
* backend) database. Then set the necessary
* parameters for later data structures creation
* and processing.
*/
static nscd_rc_t
int search_fnum,
{
nss_db_params_t *p;
int j;
char *dbn;
const char *n;
char *me = "getparams";
p = ¶ms->p;
(void) memset(p, 0, sizeof (*p));
(*initf)(p);
/* map database name to index */
n = p->name;
for (j = 0; j < NSCD_NUM_DB; j++) {
dbn = NSCD_NSW_DB_NAME(j);
if (*n != *dbn)
continue;
if (*n != 'h' && *n != 'i' && *n != 's' && *n != 'a')
break;
if (strcmp(n, NSS_DBNAM_HOSTS) == 0 &&
else if (strcmp(n, NSS_DBNAM_IPNODES) == 0 &&
else if (strcmp(n, NSS_DBNAM_SHADOW) == 0)
break;
}
}
/*
* use the switch policy for passwd_compat or
* group_compat?
*/
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) {
break;
}
}
}
}
/* map the database name to the pseudo database index */
n = p->name;
dbn = NSCD_NSW_DB_NAME(j);
if (*n == *dbn) {
break;
}
}
}
}
}
/*
* if unsupported database, let caller determine what to do next
*/
return (NSCD_CFG_UNSUPPORTED_SWITCH_DB);
}
return (rc);
}
static void
{
char *me = "nscd_initf";
(me, "ERROR: initf key not set\n");
return;
}
(me, "invalid db front params data ? dbd_len = %d\n",
return;
}
(me, "db frontend params: name =%s, config_name = %s, "
"default_config = %s, flags = %x\n", p->name,
p->config_name : "<NOT SPECIFIED>"),
p->default_config : "<NOT SPECIFIED>"),
p->flags);
}
static void
int dbi,
int srci,
int op,
{
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)
if (srci != -1)
}
if (res == NSS_SUCCESS) {
"source: %s returned >>%s<<, length = %d\n",
return;
}
"erange= %d, herrno: %s (%d)\n",
}
/*
* Determine if a request should be done locally in the getXbyY caller's
* process. Return none zero if yes, 0 otherwise. This should be called
* This function returns 1 if:
* -- the database is exec_attr and the search_flag is GET_ALL
*/
static int
int dbi,
void *arg)
{
int rc = 0;
char *me = "try_local";
rc = 1;
}
if (rc != 0) {
(me, "TRYLOCAL: exec_attr:GET_ALL\n");
}
return (rc);
}
/*
* Determine if a request should be done locally in the getXbyY caller's
* process. Return none zero if yes, 0 otherwise. This should be called
* before the switch engine invokes any backend.
* This function returns 1 if:
* -- the database is shadow and the source is compat
*/
static int
int dbi,
int srci)
{
int rc = 0;
char *me = "try_local2";
rc = 1;
}
if (rc != 0) {
(me, "TRYLOCAL: database: shadow, source: %s\n",
}
return (rc);
}
static nscd_rc_t
{
char *me = "get_lib_func";
void *sym;
return (NSCD_SUCCESS);
}
(void) mutex_lock(lock);
/* close the handle if requested */
}
(void) mutex_unlock(lock);
return (NSCD_SUCCESS);
}
(void) mutex_unlock(lock);
return (NSCD_SUCCESS);
}
(void) mutex_unlock(lock);
return (NSCD_CFG_DLOPEN_ERROR);
}
}
(void) mutex_unlock(lock);
return (NSCD_CFG_DLSYM_ERROR);
} else {
}
(void) mutex_unlock(lock);
return (NSCD_SUCCESS);
}
static nscd_rc_t
get_libc_nss_search(void **func_p)
{
}
static nscd_rc_t
get_gss_func(void **func_p)
{
}
static nscd_rc_t
get_sldap_shadow_func(void **func_p)
{
"libsldap.so", "__ns_ldap_is_shadow_update_enabled",
func_p));
}
/*
* get_dns_funcs returns pointers to gethostbyname functions in the
* dynamically loaded nss_dns & nss_mdns modules that return host
* lookup results along with the TTL value in the DNS resource
* records. The dnsi parameter indicates whether the lookup database
* is hosts(0) or ipnodes(1). The srcname parameter identifies the DNS
* gethostbyname function in func_p variable.
*/
static nscd_rc_t
{
int si;
void **funcpp;
{{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" },
{ "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }};
/* source index: 0 = dns, 1 = mdns */
si = 0;
else
si = 1;
/*
* function index (func[si][dnsi]):
*/
if (dnsi < 0) { /* close handle */
} else
}
static nss_status_t
{
nss_status_t (*func)();
return (NSS_ERROR);
if (rc == NSCD_SUCCESS) {
/*
* data_len in the packed buf header may be changed
* by the dns or mdns backend, reset it just in
* case
*/
}
return (res);
}
/*
* Returns a flag to indicate if needs to fall back to the
* main nscd when a per-user lookup failed with rc NSS_NOTFOUND.
*/
static int
{
char *me = "set_fallback_flag";
(me, "NSS_NOTFOUND (ldap): fallback to main nscd "
"may be needed\n");
return (1);
}
return (0);
}
void *search_args)
{
char *me = "nss_search";
nscd_nsw_state_t *s = NULL;
int n_src;
unsigned int status_vec = 0;
int check_loopback = 0;
int state_thr = 0;
(me, "rootp = %p, initf = %p, search_fnum = %d, "
/* determine db index, cfg db index, etc */
/*
* if unsupported database and the request is from the
* the door, tell the door client to try it locally
*/
if (initf == nscd_initf) {
res = NSS_TRYLOCAL;
goto error_exit;
} else { /* otherwise, let libc:nss_search() handle it */
nss_status_t (*func)();
if (get_libc_nss_search((void **)&func) ==
search_args));
else
goto error_exit;
}
}
/* get address of the switch engine return data area */
if (initf == nscd_initf) {
} else {
}
/*
* for door request that should be processed by the client,
* send it back with status NSS_TRYLOCAL
*/
res = NSS_TRYLOCAL;
goto error_exit;
}
/* if lookup not enabled, return NSS_UNAVAIL */
goto error_exit;
}
/* determine if loopback checking is configured */
check_loopback = 1;
(me, "loopback checking enabled for %s\n",
}
if (check_loopback) {
k = get_loopback_key();
if (k != NULL) {
k = NULL;
}
}
}
if (s == 0) {
if (check_loopback) {
state_thr = 1;
} else
if (rc != NSCD_SUCCESS)
goto error_exit;
s = (nscd_nsw_state_t *)root_db.s;
}
(*s->nsw_cfg_p)->nsw_cfg_str);
struct __nsw_lookup_v1 *lkp;
int smf_state;
int n_loop = 0;
int max_retry = 10;
res = NSS_UNAVAIL;
if (n_src == 0)
else
/* set the number of max. retries */
/*
* If no privilege to look up, skip.
* 'files' requires PRIV_FILE_DAC_READ to read shadow(4) data,
* 'ldap' requires all zones privilege.
*/
(void **)&is_shadow_update_enabled) ==
NSCD_SUCCESS &&
/*
* A peruser nscd doesn't have
* the privileges to lookup a
* private database, such as shadow,
* returns NSS_ALTRETRY to have the
* main nscd do the job.
*/
if (_whoami == NSCD_CHILD) {
res = NSS_ALTRETRY;
goto free_nsw_state;
}
}
}
_nscd_check_client_priv(NSCD_READ_PRIV) != 0) ||
(check_ldap_priv &&
_nscd_check_client_priv(NSCD_ALL_PRIV) != 0)) {
(me, "no privilege to look up, skip source\n");
goto next_src;
}
}
/* get state of the (backend) client service */
/* stop if the source is one that should be TRYLOCAL */
(me, "returning TRYLOCAL ... \n");
res = NSS_TRYLOCAL;
goto free_nsw_state;
}
if (check_loopback && k != NULL) {
if (k->fnum == search_fnum) {
(me, "loopback detected: "
"source = %s, database = %s "
"search fnum = %d\n",
continue;
}
}
/* request could be from within nscd so check states again */
(smf_state != NSCD_SVC_STATE_UNINITED &&
smf_state < SCF_STATE_ONLINE)))) {
(me, "unable to look up source %s: be = %p, "
"smf state = %d, funcp = %p\n",
goto next_src;
}
do {
/*
* we can only retry max_retry times,
* otherwise threads may get stuck in this
* do-while loop forever
*/
res = NSS_TRYLOCAL;
goto free_nsw_state;
}
/*
* set up to prevent loopback
*/
if (check_loopback && k == NULL) {
(void) set_loopback_key(&key);
k = &key;
}
(me, "looking up source = %s, loop# = %d \n",
/*
* search the backend, if hosts lookups,
* try to get the hosts data with ttl first
*/
/*
* if not able to get ttl, fall back
* to the regular backend call
*/
else {
/*
* packed buffer, not
* search_args
*/
}
} else
/*
* backend is not up, check and update the
* smf state table
*/
if (res == NSS_UNAVAIL)
/*
* may need to fall back to use the main nscd
* if per-user lookup
*/
/*
*/
struct nss_XbyY_args *arg;
arg = (struct nss_XbyY_args *)
}
}
n_loop++;
break;
}
}
if (state_thr == 1)
else
if (check_loopback && k != NULL)
if (res != NSS_SUCCESS)
goto error_exit;
return (NSS_SUCCESS);
return (res);
}
static void nss_setent_u(nss_db_root_t *,
nss_getent_t *);
nss_getent_t *,
void *);
static void nss_endent_u(nss_db_root_t *,
nss_getent_t *);
void
{
if (contextpp == 0)
return;
}
void *args)
{
if (contextpp == 0) {
return (NSS_UNAVAIL);
}
return (status);
}
void
{
if (contextpp == 0)
return;
}
/*ARGSUSED*/
static void
{
nscd_nsw_state_t *s;
int n_src;
if (s != 0) {
}
}
}
static void
{
char *me = "nss_setent_u";
nscd_nsw_state_t *s;
int n_src, i;
(me, "rootp = %p, initf = %p, contextpp = %p \n",
/*
* Get the nsw db index via the initf function. If unsupported
* database, no need to continue
*/
return;
/* get address of the switch engine return data area */
if (initf == nscd_initf)
/* if no privilege to look up, return */
_nscd_check_client_priv(NSCD_READ_PRIV) != 0) {
(me, "no privilege \n");
return;
}
NSCD_SUCCESS) {
return;
}
}
if (s == 0) {
NSCD_SUCCESS) {
return;
}
s = (nscd_nsw_state_t *)root.s;
} else {
/*
* Optimization: don't do endent, don't change
* backends, just do the setent. Look Ma, no locks
* (nor any context that needs updating).
*/
return;
}
}
}
;
}
if (be == 0) {
return;
}
/*
* make sure all the backends are supported
*/
for (i = 0; i < s->max_src; i++) {
continue;
if (st == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
(st == NSCD_SVC_STATE_FOREIGN_SRC &&
st == NSCD_SVC_STATE_UNINITED ||
(me, "backend (%s) not available (state = %d)\n",
return;
}
}
}
{
char *me = "nss_getent_u";
nscd_nsw_state_t *s;
int n_src;
(me, "rootp = %p, initf = %p, contextpp = %p, args = %p\n",
/* Give up */
(me, "not able to obtain getent context ... give up\n");
return (NSS_UNAVAIL);
}
}
if (s == 0) {
/*
* We've done an end_iter() and haven't done nss_setent()
* or nss_endent() since; we should stick in this state
* until the caller invokes one of those two routines.
*/
return (NSS_SUCCESS);
}
int n;
/* get the nsw config for the current source */
for (n = 0; n < n_src; n++)
if (be == 0) {
/* If it's null it's a bug, but let's play safe */
res = NSS_UNAVAIL;
} else {
(me, "database: %s, backend: %s, nsswitch config: %s\n",
NSCD_NSW_DB_NAME(s->dbi),
(*s->nsw_cfg_p)->nsw_cfg_str);
}
if (res != __NSW_SUCCESS) {
(struct nss_getent_context *)contextp);
}
return (res);
}
do {
n_src++;
if (be == 0) {
/*
* This is the case where we failed to get the backend
* for the last source. We exhausted all sources.
*/
return (NSS_NOTFOUND);
}
}
/* Got to the end of the sources without finding another entry */
return (NSS_SUCCESS);
/* success is either a successful entry or end of the sources */
}
/*ARGSUSED*/
void
{
char *me = "nss_endent_u";
(me, "rootp = %p, initf = %p, contextpp = %p \n",
/* nss_endent() on an unused context is a no-op */
return;
}
if (_nscd_is_getent_ctx_in_use(contextp) == 0) {
}
}
/*
* _nss_db_state_destr() and nss_delete() do nothing in nscd
* but is needed to make the caller (below nscd) happy
*/
/*ARGSUSED*/
void
_nss_db_state_destr(struct nss_db_state *s)
{
/* nsw state in nscd is always reused, so do nothing here */
}
/*ARGSUSED*/
void
{
/*
* the only resource kept tracked by the nss_db_root_t
* is the nsw state which is always reused and no need
* to be freed. So just return.
*/
}
/*
* Start of nss_psearch/nss_psetent()/nss_pgetent()/nss_pendent()
* buffers switch entry points
*/
/*
* nss_psearch opens a packed structure header, assembles a local
* nss_XbyY_args_t structure and calls the local copy of nss_search.
* The return data is assembled in "files native format" in the
* return buffer location. Status if packed back up with the buffer
* and the whole wad is returned to the cache or the client.
*/
void
{
/* inputs */
int dbop;
int rc;
char *me = "nss_psearch";
return;
}
if (status != NSS_SUCCESS) {
return;
}
/*
* pass the address of the return data area
* for the switch engine to return its own data
*/
/*
* use the generic nscd_initf for all database lookups
* (the TSD key is the pointer to the packed header)
*/
if (rc != 0) {
return;
}
initf = nscd_initf;
/* Perform local search and pack results into return buffer */
/* nscd's search ignores db_root */
/*
* If status is NSS_NOTFOUND and ldap also returned
* NSS_NOTFOUND, it is possible that the user does
* not have a credential, so check and see if
* needs to return NSS_ALTRETRY to let the main
* nscd get a chance to process the lookup
*/
if (rc == NSCD_SUCCESS) {
(me, "NSS_ALTRETRY: fallback to main nscd needed\n");
}
}
}
/*
* arg was being used and rc from the switch engine is not
* NSS_TRYLOCAL.
*/
(me, "switch engine result: source is %s, status %d, "
"herrno is %d, errno is %s\n",
/* clear the TSD key used by the generic initf */
}
static void
{
char *me = "nscd_map_contextp";
return;
}
/*
* if called by nss_psetent, and the passed in cookie number
* is NSCD_NEW_COOKIE, then there is no cookie yet, return a
* pointer pointing to where the cookie number will be stored.
* Also because there is no cookie to validate, just return
* success.
*
* On the other hand, if a cookie number is passed in, we need
* to validate the cookie number before returning.
*/
if (cookie_num_p != NULL)
return;
}
/*
* If the sequence number and start time match nscd's p0 cookie,
* then either setent was done twice in a row or this is the
* first getent after the setent, return success as well.
*/
return;
}
}
(me, "cookie # = %lld, sequence # = %lld\n",
(me, "No matching context found (cookie number: %lld)\n",
return;
}
/* if not called by nss_psetent, verify sequence number */
return;
}
}
void
{
nss_getent_t context = { 0 };
char *me = "nss_psetent";
return;
}
/*
* If this is a per-user nscd, and the user does not have
* the necessary credential, return NSS_TRYLOCAL, so the
* setent call
*/
if (_whoami == NSCD_CHILD) {
if (rc == NSCD_SUCCESS) {
(me, "NSS_TRYLOCAL: fallback to caller process\n");
return;
}
}
}
/* check cookie number */
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
/* set cookie number and sequence number */
/*
* first setent (no existing getent context),
* return a p0 cookie
*/
(me, "first setent, no getent context yet\n");
} else {
/*
* doing setent on an existing getent context,
* release resources allocated and return a
* p0 cookie
*/
/*
* context not in use, release the backend and
* return the context to the pool
*/
}
}
(me, "returning a p0 cookie: pid = %ld, time = %ld, seq #= %llx\n",
}
static void
{
char *me = "delayed_setent";
/*
* check credential
*/
if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
(me, "invalid credential\n");
return;
}
/*
* pass the packed header buffer pointer to nss_setent
*/
/* Perform local setent and set context */
/* insert cookie info into packed buffer header */
} else {
/*
* not able to allocate a getent context, the
* client should try the enumeration locally
*/
*seqnum_p = 0;
(me, "NSS_TRYLOCAL: cookie # = %lld, sequence # = %lld\n",
*cookie_num_p, *seqnum_p);
return;
}
(me, "NSS_SUCCESS: cookie # = %lld, sequence # = %lld\n",
}
void
{
/* inputs */
nss_getent_t context = { 0 };
nss_XbyY_args_t arg = { 0};
int rc;
char *me = "nss_pgetent";
return;
}
/* verify the cookie passed in */
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
/*
* use the generic nscd_initf for all the getent requests
* (the TSD key is the pointer to the packed header)
*/
if (rc != 0) {
return;
}
initf = nscd_initf;
/* if no context yet, get one */
if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
return;
}
}
if (status != NSS_SUCCESS) {
return;
}
/* Perform local search and pack results into return buffer */
/* increment sequence number in the buffer and nscd context */
if (status == NSS_SUCCESS) {
(me, "getent OK, new sequence # = %lld, len = %lld,"
" data = >>%s<<\n", *seqnum_p,
} else {
/* release the resources used */
}
(me, "getent failed, status = %d, sequence # = %lld\n",
}
/* clear the TSD key used by the generic initf */
}
void
{
nss_getent_t context = { 0 };
char *me = "nss_pendent";
return;
}
/* map the contextp from the cookie information */
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
return;
(me, "endent, cookie = %lld, sequence # = %lld\n",
*cookie_num_p, *seqnum_p);
/* Perform local endent and reset context */
}
/*ARGSUSED*/
void
{
/* unnecessary, kept for completeness */
}