nscd_frontend.c revision bf1e3bee1b13b3a914f0dd817a04f6e0ce8e0691
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <alloca.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <door.h>
#include <zone.h>
#include <resolv.h>
#include <string.h>
#include <fcntl.h>
#include "nscd_common.h"
#include "nscd_door.h"
#include "nscd_config.h"
#include "nscd_switch.h"
#include "nscd_log.h"
#include "nscd_selfcred.h"
#include "nscd_frontend.h"
#include "nscd_admin.h"
static void rts_mon(void);
static void keep_open_dns_socket(void);
extern nsc_ctx_t *cache_ctx_p[];
/*
* Current active Configuration data for the frontend component
*/
static nscd_cfg_frontend_t *frontend_cfg;
static int max_servers = 0;
static int max_servers_set = 0;
static int per_user_is_on = 1;
static char *main_execname;
static char **main_argv;
extern int _whoami;
extern long activity;
extern mutex_t activity_lock;
static sema_t common_sema;
static thread_key_t lookup_state_key;
static int num_servers = 0;
static thread_key_t server_key;
/*
* Bind a TSD value to a server thread. This enables the destructor to
* error, but better safe than sorry.
*/
/*ARGSUSED*/
static void *
server_tsd_bind(void *arg)
{
static void *value = 0;
/* disable cancellation to avoid hangs if server threads disappear */
/* make lint happy */
return (NULL);
}
/*
* Server threads are created here.
*/
/*ARGSUSED*/
static void
{
(void) mutex_lock(&create_lock);
if (++num_servers > max_servers) {
num_servers--;
(void) mutex_unlock(&create_lock);
return;
}
(void) mutex_unlock(&create_lock);
}
/*
* Server thread are destroyed here
*/
/*ARGSUSED*/
static void
server_destroy(void *arg)
{
(void) mutex_lock(&create_lock);
num_servers--;
(void) mutex_unlock(&create_lock);
}
/*
* get clearance
*/
int
if (sema_trywait(&common_sema) == 0) {
return (0);
}
if (sema_trywait(sema) == 0) {
return (0);
}
return (1);
}
/*
* release clearance
*/
int
int which;
if (which == 0) /* from common pool */ {
(void) sema_post(&common_sema);
return (0);
}
return (1);
}
static void
dozip(void)
{
/* not much here */
}
static void
{
static time_t last_nsswitch_check = 0;
static time_t last_nsswitch_modified = 0;
static time_t last_resolv_modified = 0;
char *me = "restart_if_cfgfile_changed";
return;
(void) mutex_lock(&nsswitch_lock);
/*
* This code keeps us from statting resolv.conf
* if it doesn't exist, yet prevents us from ignoring
* it if it happens to disappear later on for a bit.
*/
if (last_resolv_modified >= 0) {
if (last_resolv_modified == 0)
last_resolv_modified = -1;
else
} else if (last_resolv_modified == 0) {
}
}
/*EMPTY*/;
} else if (last_nsswitch_modified == 0) {
((last_resolv_modified > 0) &&
char *fmri;
/*
* if in self cred mode, kill the forker and
* child nscds
*/
if (_nscd_is_self_cred_on(0, NULL)) {
}
/*
* time for restart
*/
(me, "nscd restart due to %s or %s change\n",
"/etc/nsswitch.conf", "resolv.conf");
/*
* try to restart under smf
*/
/* not running under smf - reexec */
}
/* prevent multiple restarts */
(void) mutex_lock(&exit_lock);
if (smf_restart_instance(fmri) == 0)
}
} else
(void) mutex_unlock(&nsswitch_lock);
}
{
char *me = "get_client_euid";
if (door_ucred(&uc) != 0) {
return ((uid_t)-1);
}
ucred_free(uc);
return (id);
}
/*
* Check to see if the door client has PRIV_FILE_DAC_READ privilege.
* Return 0 if yes, -1 otherwise.
*/
int
{
int rc = 0;
const priv_set_t *eset;
char *me = "_nscd_check_client_read_priv";
if (door_ucred(&uc) != 0) {
return (-1);
}
rc = -1;
ucred_free(uc);
return (rc);
}
static void
void *buf,
char *dc_str)
{
const priv_set_t *eset;
int errnum;
char *me = "N2N_check_priv";
if (door_ucred(&uc) != 0) {
}
ucred_geteuid(uc) != 0) {
(me, "%s call failed(cred): caller pid %d, uid %d, "
ucred_free(uc);
}
(me, "nscd received %s cmd from pid %d, uid %d, "
ucred_free(uc);
}
void
void *buf,
char *dc_str,
int log_comp,
int log_level)
{
int errnum;
char *me = "_nscd_APP_check_cred";
if (door_ucred(&uc) != 0) {
}
}
}
} else {
}
ucred_free(uc);
if (NSCD_STATUS_IS_NOT_OK(phdr)) {
(me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
"euid %d, header ruid %d, header euid %d\n", dc_str,
}
}
/* log error and return -1 when an invalid packed buffer header is found */
static int
{
char *call_num_str;
switch (call_number) {
case NSCD_SEARCH:
call_num_str = "NSCD_SEARCH";
break;
case NSCD_SETENT:
call_num_str = "NSCD_SETENT";
break;
case NSCD_GETENT:
call_num_str = "NSCD_GETENT";
break;
case NSCD_ENDENT:
call_num_str = "NSCD_ENDENT";
break;
case NSCD_PUT:
call_num_str = "NSCD_PUT";
break;
case NSCD_GETHINTS:
call_num_str = "NSCD_GETHINTS";
break;
default:
call_num_str = "UNKNOWN";
break;
}
("pheader_error", "call number %s: invalid packed buffer header\n",
return (-1);
}
/*
* Return 0 if good, -1 otherwise.
*
* A valid header looks like the following (size is arg_size, does
* not include the output area):
* +----------------------------------+ --
* | nss_pheader_t (header fixed part)| ^
* | | |
* | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t)
* | data_off .... | |
* | | v
* +----------------------------------+ <----- dbd_off
* | dbd (database description) | ^
* | nss_dbd_t + up to 3 strings | |
* | length = sizeof(nss_dbd_t) + | len = key_off - dbd_off
* | length of 3 strings + | |
* | length of padding | |
* | (total length in multiple of 4) | v
* +----------------------------------+ <----- key_off
* | lookup key | ^
* | nss_XbyY_key_t, content varies, | |
* | based on database and lookup op | len = data_off - key_off
* | length = data_off - key_off | |
* | including padding, multiple of 4 | v
* +----------------------------------+ <----- data_off (= arg_size)
* | | ^
* | area to hold results | |
* | | len = data_len (= pbufsiz -
* | | | data_off)
* | | v
* +----------------------------------+ <----- pbufsiz
*/
static int
void *argp,
{
/*
* current version is NSCD_HEADER_REV, length of the fixed part
* of the header must match the size of nss_pheader_t
*/
/*
* buffer size and offsets must be in multiple of 4
*/
/*
* the input arg_size is the length of the request header
* and should be less than NSCD_PHDR_MAXLEN
*/
/* get length of the dbd area */
/*
* dbd area may contain padding, so length of dbd should
* not be less than the length of the actual data
*/
/* get length of the key area */
/*
* key area may contain padding, so length of key area should
* not be less than the length of the actual data
*/
/*
* length of fixed part + lengths of dbd and key area = length of
* the request header
*/
/* header length + data length = buffer length */
return (0);
}
/* log error and return -1 when an invalid nscd to nscd buffer is found */
static int
{
char *call_num_str;
switch (call_number) {
case NSCD_PING:
call_num_str = "NSCD_PING";
break;
case NSCD_IMHERE:
call_num_str = "NSCD_IMHERE";
break;
case NSCD_PULSE:
call_num_str = "NSCD_PULSE";
break;
case NSCD_FORK:
call_num_str = "NSCD_FORK";
break;
case NSCD_KILL:
call_num_str = "NSCD_KILL";
break;
case NSCD_REFRESH:
call_num_str = "NSCD_REFRESH";
break;
case NSCD_GETPUADMIN:
call_num_str = "NSCD_GETPUADMIN";
break;
case NSCD_GETADMIN:
call_num_str = "NSCD_GETADMIN";
break;
case NSCD_SETADMIN:
call_num_str = "NSCD_SETADMIN";
break;
case NSCD_KILLSERVER:
call_num_str = "NSCD_KILLSERVER";
break;
default:
call_num_str = "UNKNOWN";
break;
}
return (-1);
}
/*
* Validate the buffer of an nscd to nscd request.
* Return 0 if good, -1 otherwise.
*
* A valid buffer looks like the following (size is arg_size):
* +----------------------------------+ --
* | nss_pheader_t (header fixed part)| ^
* | | |
* | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t)
* | data_off .... | |
* | | v
* +----------------------------------+ <---dbd_off = key_off = data_off
* | | ^
* | OR no data | len = data_len (= pbufsiz -
* | | | data_off)
* | | | len could be zero
* | | v
* +----------------------------------+ <--- pbufsiz
*/
static int
void *argp,
{
/*
* current version is NSCD_HEADER_REV, length of the fixed part
* of the header must match the size of nss_pheader_t
*/
/*
* There are no dbd and key data, so the dbd, key, data
* offsets should be equal
*/
/*
* the input arg_size is the buffer length and should
* be less or equal than NSCD_N2NBUF_MAXLEN
*/
/* header length + data length = buffer length */
return (0);
}
static void
{
char space[NSCD_LOOKUP_BUFSIZE];
sizeof (space));
/*
* make sure the first couple bytes of the data area is null,
* so that bad strings in the packed header stop here
*/
nsc_lookup(&largs, 0);
/*
* only the PUN needs to keep track of the
* activity count to determine when to
* terminate itself
*/
if (_whoami == NSCD_CHILD) {
(void) mutex_lock(&activity_lock);
++activity;
(void) mutex_unlock(&activity_lock);
}
}
static void
{
char space[NSCD_LOOKUP_BUFSIZE];
}
static int
{
int len;
/* copy db name into a temp buffer */
/* check if <dbname> + ',' can be found in the dblist string */
return (1);
/*
* check if <dbname> can be found in the last part
* of the dblist string
*/
return (1);
return (0);
}
/*
* Check to see if all conditions are met for processing per-user
* requests. Returns 1 if yes, -1 if backend is not configured,
* 0 otherwise.
*/
static int
{
/* if already a per-user nscd, no need to get per-user door */
if (whoami == NSCD_CHILD)
return (0);
/* forker shouldn't be asked */
if (whoami == NSCD_FORKER) {
return (0);
}
/* if door client is root, no need for a per-user door */
if (uid == 0)
return (0);
/*
* if per-user lookup is not configured, no per-user
* door available
*/
if (_nscd_is_self_cred_on(0, dblist) == 0)
return (-1);
/*
* if per-user lookup is not configured for the db,
* don't bother
*/
return (0);
return (1);
}
static void
{
char *dblist;
int door = -1;
int rc = 0;
char *space;
int len;
/*
* check to see if self-cred is configured and
* need to return an alternate PUN door
*/
if (per_user_is_on == 1) {
_nscd_get_client_euid(), &dblist);
if (rc == -1)
per_user_is_on = 0;
}
if (rc <= 0) {
/*
* self-cred not configured, and no error detected,
* return to continue the door call processing
*/
if (NSCD_STATUS_IS_OK(phdr))
return;
else
/*
* configured but error detected,
* stop the door call processing
*/
}
/* get the alternate PUN door */
}
/* return the alternate door descriptor */
}
/*ARGSUSED*/
static void
{
int iam;
void *uptr;
int len;
int callnum;
char *me = "switcher";
(me, "switcher ...\n");
if (argp == DOOR_UNREF_DATA) {
(void) printf("Door Slam... exiting\n");
exit(0);
}
}
/*
* need to restart if main nscd and config file(s) changed
*/
/* make sure the packed buffer header is good */
switch (phdr->nsc_callnumber) {
case NSCD_SEARCH:
/* if a fallback to main nscd, skip per-user setup */
break;
case NSCD_SETENT:
if (NSCD_STATUS_IS_OK(phdr)) {
}
break;
case NSCD_GETENT:
break;
case NSCD_ENDENT:
break;
case NSCD_PUT:
(me, "door call NSCD_PUT not supported yet\n");
break;
case NSCD_GETHINTS:
(me, "door call NSCD_GETHINTS not supported yet\n");
break;
default:
(me, "Unknown name service door call op %x\n",
break;
}
}
if (callnum == NSCD_IMHERE ||
else
/* nscd -> nscd v2 calls */
/* make sure the buffer is good */
switch (callnum) {
case NSCD_PING:
break;
case NSCD_IMHERE:
break;
case NSCD_PULSE:
if (NSCD_STATUS_IS_OK(phdr))
break;
case NSCD_FORK:
if (NSCD_STATUS_IS_OK(phdr))
break;
case NSCD_KILL:
if (NSCD_STATUS_IS_OK(phdr))
exit(0);
break;
case NSCD_REFRESH:
if (NSCD_STATUS_IS_OK(phdr)) {
if (_nscd_refresh() != NSCD_SUCCESS)
exit(1);
}
break;
case NSCD_GETPUADMIN:
if (_nscd_is_self_cred_on(0, NULL)) {
} else {
}
break;
case NSCD_GETADMIN:
if (len == 0)
break;
/* size of door buffer not big enough, allocate one */
/* copy packed header */
/* set new buffer size */
/* try one more time */
(void) _nscd_door_getadmin((void *)uptr);
break;
case NSCD_SETADMIN:
if (NSCD_STATUS_IS_OK(phdr))
break;
case NSCD_KILLSERVER:
if (NSCD_STATUS_IS_OK(phdr)) {
/* also kill the forker nscd if one is running */
exit(0);
}
break;
default:
(me, "Unknown name service door call op %d\n",
break;
}
}
int
{
int fd;
int errnum;
int bind_failed = 0;
char *me = "_nscd_setup_server";
/*
* the max number of server threads should be fixed now, so
* set flag to indicate that no in-flight change is allowed
*/
max_servers_set = 1;
USYNC_THREAD, 0);
/* Establish server thread pool */
(void) door_server_create(server_create);
(me, "thr_keycreate (server thread): %s\n",
return (-1);
}
/* Create a door */
DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
return (-1);
}
/* if not main nscd, no more setup to do */
return (fd);
/* bind to file system */
int newfd;
(me, "Cannot create %s: %s\n",
bind_failed = 1;
}
}
(me, "Cannot symlink %s: %s\n",
bind_failed = 1;
}
}
int newfd;
bind_failed = 1;
}
}
if (bind_failed == 1) {
(void) door_revoke(fd);
return (-1);
}
(fdetach(NAME_SERVICE_DOOR) < 0) ||
(void) door_revoke(fd);
return (-1);
}
}
/*
* kick off routing socket monitor thread
*/
(me, "thr_create (routing socket monitor): %s\n",
(void) door_revoke(fd);
return (-1);
}
/*
* set up signal handler for SIGHUP
*/
(void) sigemptyset(&myset);
(void) door_revoke(fd);
return (-1);
}
return (fd);
}
int
{
int errnum;
int fd;
char *me = "_nscd_setup_child_server";
/* Re-establish our own server thread pool */
(void) door_server_create(server_create);
return (-1);
}
/*
* Create a new door.
* Keep DOOR_REFUSE_DESC (self-cred nscds don't fork)
*/
return (-1);
}
/*
* kick off routing socket monitor thread
*/
(me, "thr_create (routing socket monitor): %s\n",
(void) door_revoke(fd);
return (-1);
}
/*
* start monitoring the states of the name service clients
*/
rc = _nscd_init_smf_monitor();
if (rc != NSCD_SUCCESS) {
(void) door_revoke(fd);
return (-1);
}
return (fd);
}
{
if (frontend_cfg == NULL)
return (NSCD_NO_MEMORY);
return (NSCD_SUCCESS);
}
/* ARGSUSED */
void *data,
struct nscd_cfg_param_desc *pdesc,
void *cookie)
{
void *dp;
/*
* At init time, the whole group of config params are received.
* At update time, group or individual parameter value could
* be received.
*/
/*
* group data is received, copy in the
* entire strcture
*/
else
*(nscd_cfg_frontend_t *)data;
} else {
/*
* individual paramater is received: copy in the
* parameter value.
*/
else
}
return (NSCD_SUCCESS);
}
/* ARGSUSED */
void *data,
struct nscd_cfg_param_desc *pdesc,
void **cookie)
{
char *me = "_nscd_cfg_frontend_verify";
/*
* if max. number of server threads is set and in effect,
* don't allow changing of the frontend configuration
*/
if (max_servers_set) {
(me, "changing of the frontend configuration not allowed now");
return (NSCD_CFG_CHANGE_NOT_ALLOWED);
}
return (NSCD_SUCCESS);
}
/* ARGSUSED */
void **stat,
struct nscd_cfg_stat_desc *sdesc,
{
return (NSCD_SUCCESS);
}
void
{
int i, j;
char *dbn;
if (max_servers == 0)
for (i = 0; i < NSCD_NUM_DB; i++) {
dbn = NSCD_NSW_DB_NAME(i);
j = frontend_cfg[i].worker_thread_per_nsw_db;
max_servers += j;
break;
}
}
}
/*
* Monitor the routing socket. Address lists stored in the ipnodes
* cache are sorted based on destination address selection rules,
* so when things change that could affect that sorting (interfaces
* go up or down, flags change, etc.), we clear that cache so the
* list will be re-ordered the next time the hostname is resolved.
*/
static void
rts_mon(void)
{
union {
struct {
} r;
struct ifa_msghdr ifam;
} mbuf;
char *me = "rts_mon";
if (rt_sock < 0) {
thr_exit(0);
}
for (;;) {
if (rdlen <= 0) {
(me, "routing socket read: %s\n",
thr_exit(0);
}
continue;
}
(me, "rx unknown version (%d) on "
"routing socket.\n",
ifam->ifam_version);
continue;
}
case RTM_NEWADDR:
case RTM_DELADDR:
/* if no ipnodes cache, then nothing to do */
break;
break;
case RTM_ADD:
case RTM_DELETE:
case RTM_CHANGE:
case RTM_GET:
case RTM_LOSING:
case RTM_REDIRECT:
case RTM_MISS:
case RTM_LOCK:
case RTM_OLDADD:
case RTM_OLDDEL:
case RTM_RESOLVE:
case RTM_IFINFO:
break;
default:
(me, "rx unknown msg type (%d) on routing socket.\n",
break;
}
}
}
static void
keep_open_dns_socket(void)
{
}