nscd_switch.c revision c70a8a3b92fb0488ef2ca1ae9e282c8b86ffa6d1
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h> /* getenv() */
#include <assert.h>
#include <unistd.h>
#include <string.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);
}
static thread_key_t loopback_key;
static int loopback_key_created = 0;
typedef struct lb_key {
int srci;
int dbi;
int fnum;
int *lb_flagp;
} lb_key_t;
static int
int rc = 0;
lb_key_t *k;
if (!loopback_key_created) {
(void) mutex_lock(&loopback_key_lock);
if (!loopback_key_created) {
NULL)) == 0)
loopback_key_created = 1;
}
(void) mutex_unlock(&loopback_key_lock);
}
if (rc == 0) {
/* set key if not already set */
if (thr_getspecific(loopback_key, (void **)&k) == 0 &&
k == NULL) {
}
}
return (rc);
}
static lb_key_t *
get_loopback_key(void) {
char *me = "get_loopback_key";
int rc = 0;
if (!loopback_key_created)
return (NULL);
return (k);
return (NULL);
}
static void
char *me = "clear_loopback_key";
if (loopback_key_created && key != 0) {
/*
* 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 thread_key_t initf_key;
static int initf_key_created = 0;
static int
set_initf_key(void *pbuf) {
int rc = 0;
if (!initf_key_created) {
(void) mutex_lock(&initf_key_lock);
if (!initf_key_created) {
initf_key_created = 1;
}
(void) mutex_unlock(&initf_key_lock);
}
if (rc == 0)
return (rc);
}
static void *
get_initf_key(void) {
char *me = "get_initf_key";
void *pbuf;
int rc = 0;
if (!initf_key_created)
return (NULL);
return (pbuf);
return (NULL);
}
static void
clear_initf_key(void) {
char *me = "clear_initf_key";
if (initf_key_created)
(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;
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)
else if (strcmp(n, NSS_DBNAM_AUDITUSER) == 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;
}
}
}
}
}
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 *me = "nss_search";
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) {
"%s: database: %s, operation: %d, source: %s returned \"%s\", length = %d\n",
return;
}
"%s: database: %s, operation: %d, source: %s, erange= %d, errno: %s \n",
}
/*
* Determine if a request should be done locally in the getXbyY caller's
* process. Return none zero if yes, 0 otherwise.
* This function returnis 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);
}
static nscd_rc_t
get_gss_func(void **func_p)
{
char *me = "get_gss_func";
void *sym;
char *func_name = "gss_inquire_cred";
return (NSCD_SUCCESS);
}
(void) mutex_lock(&func_lock);
/* close the handle if requested */
}
(void) mutex_unlock(&func_lock);
return (NSCD_SUCCESS);
}
(void) mutex_unlock(&func_lock);
return (NSCD_SUCCESS);
}
(void) mutex_unlock(&func_lock);
return (NSCD_CFG_DLOPEN_ERROR);
}
}
(void) mutex_unlock(&func_lock);
return (NSCD_CFG_DLSYM_ERROR);
} else {
}
(void) mutex_unlock(&func_lock);
return (NSCD_SUCCESS);
}
static nscd_rc_t
{
char *me = "get_dns_funcs";
void *sym;
"_nss_get_dns_ipnodes_name" };
return (NSCD_SUCCESS);
}
(void) mutex_lock(&func_lock);
/* close the handle if requested */
if (dnsi < 0) {
}
(void) mutex_unlock(&func_lock);
return (NSCD_SUCCESS);
}
(void) mutex_unlock(&func_lock);
return (NSCD_SUCCESS);
}
(void) mutex_unlock(&func_lock);
return (NSCD_CFG_DLOPEN_ERROR);
}
}
(void) mutex_unlock(&func_lock);
return (NSCD_CFG_DLSYM_ERROR);
} else {
}
(void) mutex_unlock(&func_lock);
return (NSCD_SUCCESS);
}
static nss_status_t
{
nss_status_t (*func)();
return (NSS_ERROR);
if (rc == NSCD_SUCCESS)
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 */
/* get address of the switch engine return data area */
if (initf == nscd_initf) {
} else {
}
/*
* for 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 */
_nscd_get_client_euid() != 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 */
if (smf_state == NSCD_SVC_STATE_UNKNOWN_SRC) {
(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;
}
}
(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 */
/* get address of the switch engine return data area */
if (initf == nscd_initf)
/* if no privilege to look up, return */
(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++) {
if (st == NSCD_SVC_STATE_UNKNOWN_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_SUCCESS);
}
}
/* 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;
}
}
/*
* _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";
}
if (status != NSS_SUCCESS) {
}
/*
* 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) {
}
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
*/
(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";
struct cookie_seqnum {
} *csp;
}
/*
* if called by nss_psetent, and the passed in cookie is
* NSCD_NEW_COOKIE, then there is no cookie yet, return
* a pointer pointing to where the cookie will be stored.
* Also because there is no cookie to validate, just
* return success.
*
* On the other hand, if a cookie is passed in, we need
* to validate the cookie before returning.
*/
}
(me, "cookie = %lld, sequence number = %lld\n",
}
if (setent == 1) {
/* if called by nss_psetent, reset the seq number */
}
}
void
{
/* inputs */
nss_getent_t context = { 0 };
int rc;
char *me = "nss_psetent";
}
/*
* 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");
}
}
}
if (status != NSS_SUCCESS) {
}
/*
* use the generic nscd_initf for all the setent requests
* (the TSD key is the pointer to the packed header)
*/
if (rc != 0) {
}
initf = nscd_initf;
/* get address of cookie and seqnum for later updates */
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
/*
* pass the packed header buffer pointer to nss_setent
*/
/* Perform local setent and set context */
/* insert cookie info into buffer and return */
} else {
/*
* not able to allocate a getent context, the
* client should try the enumeration locally
*/
*seqnump = 0;
}
(me, "cookie = %lld, sequence number = %lld\n",
(me, "cookie = %lld, sequence number = %lld\n",
}
/* clear the TSD key used by the generic initf */
if (*cookiep == NSCD_LOCAL_COOKIE) {
} else {
}
}
void
{
/* inputs */
int rc;
char *me = "nss_pgetent";
}
if (status != NSS_SUCCESS) {
}
/*
* use the generic nscd_initf for all the getent requests
* (the TSD key is the pointer to the packed header)
*/
if (rc != 0) {
}
initf = nscd_initf;
/* verify the cookie passed in */
if (NSCD_STATUS_IS_NOT_OK(pbuf))
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 number = %lld, len = %lld,"
" data = [ %s ]\n", *seqnump,
} else {
/* release the resources used */
}
(me, "getent failed, status = %d, sequence number = %lld\n",
}
/* clear the TSD key used by the generic initf */
}
void
{
char *me = "nss_pendent";
}
/* map the contextp from the cookie information */
if (NSCD_STATUS_IS_NOT_OK(pbuf))
return;
(me, "endent, cookie = %lld, sequence number = %lld\n",
/* Perform local endent and reset context */
}
/*ARGSUSED*/
void
{
/* unnecessary, kept for completeness */
}