#include <stdlib.h>
#include <alloca.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <door.h>
#include <zone.h>
#include <resolv.h>
#include <sys/socket.h>
#include <net/route.h>
#include <string.h>
#include <net/if.h>
#include <sys/stat.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[];
static nscd_cfg_global_frontend_t frontend_cfg_g;
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 mutex_t create_lock = DEFAULTMUTEX;
static int num_servers = 0;
static thread_key_t server_key;
static void *
server_tsd_bind(void *arg)
{
static void *value = "NON-NULL TSD";
(void) thr_setname(thr_self(), "server_tsd_bind");
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
(void) thr_setspecific(server_key, value);
(void) door_return(NULL, 0, NULL, 0);
return (NULL);
}
static void
server_create(door_info_t *dip)
{
(void) mutex_lock(&create_lock);
if (++num_servers > max_servers) {
num_servers--;
(void) mutex_unlock(&create_lock);
return;
}
(void) mutex_unlock(&create_lock);
(void) thr_create(NULL, 0, server_tsd_bind, NULL,
THR_BOUND|THR_DETACHED, NULL);
}
static void
server_destroy(void *arg)
{
(void) mutex_lock(&create_lock);
num_servers--;
(void) mutex_unlock(&create_lock);
(void) thr_setspecific(server_key, NULL);
}
int
_nscd_get_clearance(sema_t *sema)
{
if (sema_trywait(&common_sema) == 0) {
(void) thr_setspecific(lookup_state_key, NULL);
return (0);
}
if (sema_trywait(sema) == 0) {
(void) thr_setspecific(lookup_state_key, (void*)1);
return (0);
}
return (1);
}
int
_nscd_release_clearance(sema_t *sema)
{
int which;
(void) thr_getspecific(lookup_state_key, (void**)&which);
if (which == 0) {
(void) sema_post(&common_sema);
return (0);
}
(void) sema_post(sema);
return (1);
}
static void
dozip(int signal __unused)
{
}
void
_nscd_restart_if_cfgfile_changed()
{
static mutex_t nsswitch_lock = DEFAULTMUTEX;
static timestruc_t last_nsswitch_check = { 0 };
static timestruc_t last_nsswitch_modified = { 0 };
static timestruc_t last_resolv_modified = { -1, 0 };
static mutex_t restarting_lock = DEFAULTMUTEX;
static int restarting = 0;
int restart = 0;
time_t now = time(NULL);
char *me = "_nscd_restart_if_cfgfile_changed";
#define FLAG_RESTART_REQUIRED if (restarting == 0) {\
(void) mutex_lock(&restarting_lock);\
if (restarting == 0) {\
restarting = 1;\
restart = 1;\
}\
(void) mutex_unlock(&restarting_lock);\
}
if (restarting == 1)
return;
if (now - last_nsswitch_check.tv_sec < _NSC_FILE_CHECK_TIME)
return;
(void) mutex_lock(&nsswitch_lock);
if (now - last_nsswitch_check.tv_sec >= _NSC_FILE_CHECK_TIME) {
struct stat nss_buf;
struct stat res_buf;
last_nsswitch_check.tv_sec = now;
last_nsswitch_check.tv_nsec = 0;
(void) mutex_unlock(&nsswitch_lock);
if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
return;
} else if (last_nsswitch_modified.tv_sec == 0) {
last_nsswitch_modified = nss_buf.st_mtim;
}
if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec ||
(last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec &&
last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) {
FLAG_RESTART_REQUIRED;
}
if (restart == 0) {
if (stat("/etc/resolv.conf", &res_buf) < 0) {
if (last_resolv_modified.tv_sec > 0) {
FLAG_RESTART_REQUIRED;
} else if (last_resolv_modified.tv_sec == -1) {
last_resolv_modified.tv_sec = 0;
}
} else if (last_resolv_modified.tv_sec == -1) {
last_resolv_modified = res_buf.st_mtim;
} else if (last_resolv_modified.tv_sec == 0) {
FLAG_RESTART_REQUIRED;
} else if (last_resolv_modified.tv_sec <
res_buf.st_mtim.tv_sec ||
(last_resolv_modified.tv_sec ==
res_buf.st_mtim.tv_sec &&
last_resolv_modified.tv_nsec <
res_buf.st_mtim.tv_nsec)) {
FLAG_RESTART_REQUIRED;
}
}
if (restart == 1) {
char *fmri;
if (_nscd_is_self_cred_on(0, NULL)) {
_nscd_kill_forker();
_nscd_kill_all_children();
}
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
(me, "nscd restart due to %s or %s change\n",
"/etc/nsswitch.conf", "resolv.conf");
if ((fmri = getenv("SMF_FMRI")) == NULL) {
(void) execv(main_execname, main_argv);
exit(1);
}
if (smf_restart_instance(fmri) == 0)
(void) sleep(10);
exit(1);
}
} else
(void) mutex_unlock(&nsswitch_lock);
}
uid_t
_nscd_get_client_euid()
{
ucred_t *uc = NULL;
uid_t id;
char *me = "get_client_euid";
if (door_ucred(&uc) != 0) {
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
(me, "door_ucred: %s\n", strerror(errno));
return ((uid_t)-1);
}
id = ucred_geteuid(uc);
ucred_free(uc);
return (id);
}
int
_nscd_check_client_priv(int required_priv)
{
int rc = 0;
ucred_t *uc = NULL;
const priv_set_t *eset;
char *me = "_nscd_check_client_read_priv";
priv_set_t *zs;
if (door_ucred(&uc) != 0) {
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "door_ucred: %s\n", strerror(errno));
return (-1);
}
if (ucred_geteuid(uc) == 0) {
ucred_free(uc);
return (0);
}
eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
switch (required_priv) {
case NSCD_ALL_PRIV:
zs = priv_str_to_set("zone", ",", NULL);
if (!priv_isequalset(eset, zs)) {
_NSCD_LOG(NSCD_LOG_FRONT_END,
NSCD_LOG_LEVEL_ERROR)
(me, "missing all zones privileges\n");
rc = -1;
}
priv_freeset(zs);
break;
case NSCD_READ_PRIV:
if (!priv_ismember(eset, PRIV_FILE_DAC_READ))
rc = -1;
break;
default:
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "unknown required_priv: %d\n", required_priv);
rc = -1;
break;
}
ucred_free(uc);
return (rc);
}
static void
N2N_check_priv(
void *buf,
char *dc_str)
{
nss_pheader_t *phdr = (nss_pheader_t *)buf;
ucred_t *uc = NULL;
const priv_set_t *eset;
zoneid_t zoneid;
int errnum;
char *me = "N2N_check_priv";
if (door_ucred(&uc) != 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
(me, "door_ucred: %s\n", strerror(errno));
NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
return;
}
eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
zoneid = ucred_getzoneid(uc);
if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
ucred_geteuid(uc) != 0) {
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
(me, "%s call failed(cred): caller pid %d, uid %d, "
"euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
ucred_getruid(uc), ucred_geteuid(uc), zoneid);
ucred_free(uc);
NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
return;
}
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
(me, "nscd received %s cmd from pid %d, uid %d, "
"euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
ucred_getruid(uc), ucred_geteuid(uc), zoneid);
ucred_free(uc);
NSCD_SET_STATUS_SUCCESS(phdr);
}
void
_nscd_APP_check_cred(
void *buf,
pid_t *pidp,
char *dc_str,
int log_comp,
int log_level)
{
nss_pheader_t *phdr = (nss_pheader_t *)buf;
ucred_t *uc = NULL;
uid_t ruid;
uid_t euid;
pid_t pid;
int errnum;
char *me = "_nscd_APP_check_cred";
if (door_ucred(&uc) != 0) {
errnum = errno;
_NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR)
(me, "door_ucred: %s\n", strerror(errno));
NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
return;
}
NSCD_SET_STATUS_SUCCESS(phdr);
pid = ucred_getpid(uc);
if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc),
euid = ucred_geteuid(uc))) {
if (pidp != NULL) {
if (*pidp == (pid_t)-1)
*pidp = pid;
else if (*pidp != pid) {
NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
}
}
} else {
NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
}
ucred_free(uc);
if (NSCD_STATUS_IS_NOT_OK(phdr)) {
_NSCD_LOG(log_comp, log_level)
(me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
"euid %d, header ruid %d, header euid %d\n", dc_str,
pid, (pidp != NULL) ? *pidp : -1, ruid, euid,
((nss_pheader_t *)(buf))->p_ruid,
((nss_pheader_t *)(buf))->p_euid);
}
}
static int
pheader_error(nss_pheader_t *phdr, uint32_t call_number)
{
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;
}
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
("pheader_error", "call number %s: invalid packed buffer header\n",
call_num_str);
NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
return (-1);
}
static int
validate_pheader(
void *argp,
size_t arg_size,
uint32_t call_number)
{
nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
nssuint_t l1, l2;
if (phdr->p_version != NSCD_HEADER_REV ||
phdr->dbd_off != sizeof (nss_pheader_t))
return (pheader_error(phdr, call_number));
if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) ||
(phdr->data_off & 3))
return (pheader_error(phdr, call_number));
if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN)
return (pheader_error(phdr, call_number));
l1 = phdr->key_off - phdr-> dbd_off;
if (l1 < phdr->dbd_len)
return (pheader_error(phdr, call_number));
l2 = phdr->data_off - phdr->key_off;
if (l2 < phdr->key_len)
return (pheader_error(phdr, call_number));
if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off)
return (pheader_error(phdr, call_number));
if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
return (pheader_error(phdr, call_number));
return (0);
}
static int
N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number)
{
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;
}
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
("N2Nbuf_error", "call number %s: invalid N2N buffer\n", call_num_str);
NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
NSCD_DOOR_BUFFER_CHECK_FAILED);
return (-1);
}
static int
validate_N2Nbuf(
void *argp,
size_t arg_size,
uint32_t call_number)
{
nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
if (phdr->p_version != NSCD_HEADER_REV ||
phdr->dbd_off != sizeof (nss_pheader_t))
return (N2Nbuf_error(phdr, call_number));
if (phdr->dbd_off != phdr->key_off ||
phdr->dbd_off != phdr->data_off)
return (N2Nbuf_error(phdr, call_number));
if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN)
return (N2Nbuf_error(phdr, call_number));
if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
return (N2Nbuf_error(phdr, call_number));
return (0);
}
static void
lookup(char *argp, size_t arg_size)
{
nsc_lookup_args_t largs;
char space[NSCD_LOOKUP_BUFSIZE];
nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space,
sizeof (space));
(void) memset((char *)phdr + phdr->data_off, 0, 16);
(void) memset(&largs, 0, sizeof (largs));
largs.buffer = argp;
largs.bufsize = arg_size;
nsc_lookup(&largs, 0);
if (_whoami == NSCD_CHILD) {
(void) mutex_lock(&activity_lock);
++activity;
(void) mutex_unlock(&activity_lock);
}
NSCD_SET_RETURN_ARG(phdr, arg_size);
(void) door_return(argp, arg_size, NULL, 0);
}
static void
getent(char *argp, size_t arg_size)
{
char space[NSCD_LOOKUP_BUFSIZE];
nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp;
NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space));
nss_pgetent(argp, arg_size);
NSCD_SET_RETURN_ARG(phdr, arg_size);
(void) door_return(argp, arg_size, NULL, 0);
}
static int
is_db_per_user(void *buf, char *dblist)
{
nss_pheader_t *phdr = (nss_pheader_t *)buf;
nss_dbd_t *pdbd;
char *dbname, *dbn;
int len;
pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off));
dbname = (char *)pdbd + pdbd->o_name;
len = strlen(dbname);
dbn = alloca(len + 2);
(void) memcpy(dbn, dbname, len);
dbn[len] = ',';
dbn[len + 1] = '\0';
if (strstr(dblist, dbn) != NULL)
return (1);
dbn[len] = '\0';
if (strstr(dblist, dbn) != NULL)
return (1);
return (0);
}
static int
need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist)
{
nss_pheader_t *phdr = (nss_pheader_t *)buf;
NSCD_SET_STATUS_SUCCESS(phdr);
if (whoami == NSCD_CHILD)
return (0);
if (whoami == NSCD_FORKER) {
NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
return (0);
}
if (uid == 0)
return (0);
if (_nscd_is_self_cred_on(0, dblist) == 0)
return (-1);
if (is_db_per_user(phdr, *dblist) == 0)
return (0);
return (1);
}
static void
if_selfcred_return_per_user_door(char *argp, size_t arg_size,
door_desc_t *dp, int whoami)
{
nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp);
char *dblist;
int door = -1;
int rc = 0;
door_desc_t desc;
char *space;
int len;
if (per_user_is_on == 1) {
rc = need_per_user_door(argp, whoami,
_nscd_get_client_euid(), &dblist);
if (rc == -1)
per_user_is_on = 0;
}
if (rc <= 0) {
if (NSCD_STATUS_IS_OK(phdr))
return;
else
(void) door_return(argp, phdr->data_off, NULL, 0);
}
_nscd_proc_alt_get(argp, &door);
if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) {
(void) door_return(argp, phdr->data_off, NULL, 0);
}
len = strlen(dblist) + 1;
space = alloca(arg_size + len);
phdr->data_len = len;
(void) memcpy(space, phdr, arg_size);
(void) strncpy((char *)space + arg_size, dblist, len);
dp = &desc;
dp->d_attributes = DOOR_DESCRIPTOR;
dp->d_data.d_desc.d_descriptor = door;
arg_size += len;
(void) door_return(space, arg_size, dp, 1);
}
static void
switcher(void *cookie, char *argp, size_t arg_size,
door_desc_t *dp, uint_t n_desc)
{
int iam;
pid_t ent_pid = -1;
nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp);
void *uptr;
int len;
size_t buflen;
int callnum;
char *me = "switcher";
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
(me, "switcher ...\n");
if (argp == DOOR_UNREF_DATA) {
(void) printf("Door Slam... exiting\n");
exit(0);
}
if (argp == NULL) {
(void) door_return(NULL, 0, 0, 0);
}
if (_whoami == NSCD_MAIN)
_nscd_restart_if_cfgfile_changed();
if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) {
if (validate_pheader(argp, arg_size,
phdr->nsc_callnumber) == -1)
(void) door_return(argp, arg_size, NULL, 0);
switch (phdr->nsc_callnumber) {
case NSCD_SEARCH:
if (phdr->p_status != NSS_ALTRETRY)
if_selfcred_return_per_user_door(argp, arg_size,
dp, _whoami);
lookup(argp, arg_size);
break;
case NSCD_SETENT:
_nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT",
NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT);
if (NSCD_STATUS_IS_OK(phdr)) {
if_selfcred_return_per_user_door(argp, arg_size,
dp, _whoami);
nss_psetent(argp, arg_size, ent_pid);
}
break;
case NSCD_GETENT:
getent(argp, arg_size);
break;
case NSCD_ENDENT:
nss_pendent(argp, arg_size);
break;
case NSCD_PUT:
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "door call NSCD_PUT not supported yet\n");
NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
break;
case NSCD_GETHINTS:
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "door call NSCD_GETHINTS not supported yet\n");
NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
break;
default:
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "Unknown name service door call op %x\n",
phdr->nsc_callnumber);
NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
break;
}
(void) door_return(argp, arg_size, NULL, 0);
}
iam = NSCD_MAIN;
callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI;
if (callnum == NSCD_IMHERE ||
callnum == NSCD_PULSE || callnum == NSCD_FORK)
iam = phdr->nsc_callnumber & NSCD_WHOAMI;
else
callnum = phdr->nsc_callnumber;
if (validate_N2Nbuf(argp, arg_size, callnum) == -1)
(void) door_return(argp, arg_size, NULL, 0);
switch (callnum) {
case NSCD_PING:
NSCD_SET_STATUS_SUCCESS(phdr);
break;
case NSCD_IMHERE:
_nscd_proc_iamhere(argp, dp, n_desc, iam);
break;
case NSCD_PULSE:
N2N_check_priv(argp, "NSCD_PULSE");
if (NSCD_STATUS_IS_OK(phdr))
_nscd_proc_pulse(argp, iam);
break;
case NSCD_FORK:
N2N_check_priv(argp, "NSCD_FORK");
if (NSCD_STATUS_IS_OK(phdr))
_nscd_proc_fork(argp, iam);
break;
case NSCD_KILL:
N2N_check_priv(argp, "NSCD_KILL");
if (NSCD_STATUS_IS_OK(phdr))
exit(0);
break;
case NSCD_REFRESH:
N2N_check_priv(argp, "NSCD_REFRESH");
if (NSCD_STATUS_IS_OK(phdr)) {
if (_nscd_refresh() != NSCD_SUCCESS)
exit(1);
NSCD_SET_STATUS_SUCCESS(phdr);
}
break;
case NSCD_GETPUADMIN:
if (_nscd_is_self_cred_on(0, NULL)) {
_nscd_peruser_getadmin(argp, sizeof (nscd_admin_t));
} else {
NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
NSCD_SELF_CRED_NOT_CONFIGURED);
}
break;
case NSCD_GETADMIN:
len = _nscd_door_getadmin((void *)argp);
if (len == 0)
break;
NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen);
*(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp);
((nss_pheader_t *)uptr)->pbufsiz = buflen;
(void) _nscd_door_getadmin((void *)uptr);
(void) door_return(uptr, buflen, NULL, 0);
break;
case NSCD_SETADMIN:
N2N_check_priv(argp, "NSCD_SETADMIN");
if (NSCD_STATUS_IS_OK(phdr))
_nscd_door_setadmin(argp);
break;
case NSCD_KILLSERVER:
N2N_check_priv(argp, "NSCD_KILLSERVER");
if (NSCD_STATUS_IS_OK(phdr)) {
_nscd_kill_forker();
exit(0);
}
break;
default:
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "Unknown name service door call op %d\n",
phdr->nsc_callnumber);
NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
(void) door_return(argp, arg_size, NULL, 0);
break;
}
(void) door_return(argp, arg_size, NULL, 0);
}
int
_nscd_setup_server(char *execname, char **argv)
{
int fd;
int errnum;
int bind_failed = 0;
mode_t old_mask;
struct stat buf;
sigset_t myset;
struct sigaction action;
char *me = "_nscd_setup_server";
main_execname = execname;
main_argv = argv;
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "signal (SIGPIPE): %s\n", strerror(errnum));
return (-1);
}
keep_open_dns_socket();
max_servers_set = 1;
(void) thr_keycreate(&lookup_state_key, NULL);
(void) sema_init(&common_sema, frontend_cfg_g.common_worker_threads,
USYNC_THREAD, 0);
(void) door_server_create(server_create);
if (thr_keycreate(&server_key, server_destroy) != 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "thr_keycreate (server thread): %s\n",
strerror(errnum));
return (-1);
}
if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "door_create: %s\n", strerror(errnum));
return (-1);
}
if (_whoami != NSCD_MAIN)
return (fd);
if (is_system_labeled() && (getzoneid() == GLOBAL_ZONEID)) {
if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) {
int newfd;
old_mask = umask(0);
if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END,
NSCD_LOG_LEVEL_ERROR)
(me, "Cannot create %s: %s\n",
TSOL_NAME_SERVICE_DOOR,
strerror(errnum));
bind_failed = 1;
}
(void) umask(old_mask);
(void) close(newfd);
}
if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) {
if (errno != EEXIST) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END,
NSCD_LOG_LEVEL_ERROR)
(me, "Cannot symlink %s: %s\n",
NAME_SERVICE_DOOR, strerror(errnum));
bind_failed = 1;
}
}
} else if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
int newfd;
old_mask = umask(0);
if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR,
strerror(errnum));
bind_failed = 1;
}
(void) umask(old_mask);
(void) close(newfd);
}
if (bind_failed == 1) {
(void) door_revoke(fd);
return (-1);
}
if (fattach(fd, NAME_SERVICE_DOOR) < 0) {
if ((errno != EBUSY) ||
(fdetach(NAME_SERVICE_DOOR) < 0) ||
(fattach(fd, NAME_SERVICE_DOOR) < 0)) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "fattach: %s\n", strerror(errnum));
(void) door_revoke(fd);
return (-1);
}
}
if (thr_create(NULL, 0,
(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "thr_create (routing socket monitor): %s\n",
strerror(errnum));
(void) door_revoke(fd);
return (-1);
}
action.sa_handler = dozip;
action.sa_flags = 0;
(void) sigemptyset(&action.sa_mask);
(void) sigemptyset(&myset);
(void) sigaddset(&myset, SIGHUP);
if (sigaction(SIGHUP, &action, NULL) < 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "sigaction (SIGHUP): %s\n", strerror(errnum));
(void) door_revoke(fd);
return (-1);
}
return (fd);
}
int
_nscd_setup_child_server(int did)
{
int errnum;
int fd;
nscd_rc_t rc;
char *me = "_nscd_setup_child_server";
(void) door_server_create(server_create);
if (thr_keycreate(&server_key, server_destroy) != 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
(me, "thr_keycreate failed: %s", strerror(errnum));
return (-1);
}
(void) close(did);
if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
(me, "door_create failed: %s", strerror(errnum));
return (-1);
}
if (thr_create(NULL, 0,
(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
errnum = errno;
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "thr_create (routing socket monitor): %s\n",
strerror(errnum));
(void) door_revoke(fd);
return (-1);
}
rc = _nscd_init_smf_monitor();
if (rc != NSCD_SUCCESS) {
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "unable to start the SMF monitor (rc = %d)\n", rc);
(void) door_revoke(fd);
return (-1);
}
return (fd);
}
nscd_rc_t
_nscd_alloc_frontend_cfg()
{
frontend_cfg = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t));
if (frontend_cfg == NULL)
return (NSCD_NO_MEMORY);
return (NSCD_SUCCESS);
}
nscd_rc_t
_nscd_cfg_frontend_notify(
void *data,
struct nscd_cfg_param_desc *pdesc,
nscd_cfg_id_t *nswdb,
nscd_cfg_flag_t dflag,
nscd_cfg_error_t **errorp,
void *cookie)
{
void *dp;
if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) ||
_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
frontend_cfg_g = *(nscd_cfg_global_frontend_t *)data;
else
frontend_cfg[nswdb->index] =
*(nscd_cfg_frontend_t *)data;
} else {
if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
dp = (char *)&frontend_cfg_g + pdesc->p_offset;
else
dp = (char *)&frontend_cfg[nswdb->index] +
pdesc->p_offset;
(void) memcpy(dp, data, pdesc->p_size);
}
return (NSCD_SUCCESS);
}
nscd_rc_t
_nscd_cfg_frontend_verify(
void *data,
struct nscd_cfg_param_desc *pdesc,
nscd_cfg_id_t *nswdb,
nscd_cfg_flag_t dflag,
nscd_cfg_error_t **errorp,
void **cookie)
{
char *me = "_nscd_cfg_frontend_verify";
if (max_servers_set) {
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
(me, "changing of the frontend configuration not allowed now");
return (NSCD_CFG_CHANGE_NOT_ALLOWED);
}
return (NSCD_SUCCESS);
}
nscd_rc_t
_nscd_cfg_frontend_get_stat(
void **stat,
struct nscd_cfg_stat_desc *sdesc,
nscd_cfg_id_t *nswdb,
nscd_cfg_flag_t *dflag,
void (**free_stat)(void *stat),
nscd_cfg_error_t **errorp)
{
return (NSCD_SUCCESS);
}
void
_nscd_init_cache_sema(sema_t *sema, char *cache_name)
{
int i, j;
char *dbn;
if (max_servers == 0)
max_servers = frontend_cfg_g.common_worker_threads +
frontend_cfg_g.cache_hit_threads;
for (i = 0; i < NSCD_NUM_DB; i++) {
dbn = NSCD_NSW_DB_NAME(i);
if (strcasecmp(dbn, cache_name) == 0) {
j = frontend_cfg[i].worker_thread_per_nsw_db;
(void) sema_init(sema, j, USYNC_THREAD, 0);
max_servers += j;
break;
}
}
}
static void
rts_mon(void)
{
int rt_sock, rdlen, idx;
union {
struct {
struct rt_msghdr rtm;
struct sockaddr_storage addrs[RTA_NUMBITS];
} r;
struct if_msghdr ifm;
struct ifa_msghdr ifam;
} mbuf;
struct ifa_msghdr *ifam = &mbuf.ifam;
char *me = "rts_mon";
(void) thr_setname(thr_self(), me);
rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
if (rt_sock < 0) {
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "Failed to open routing socket: %s\n", strerror(errno));
thr_exit(0);
}
for (;;) {
rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
if (rdlen <= 0) {
if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
_NSCD_LOG(NSCD_LOG_FRONT_END,
NSCD_LOG_LEVEL_ERROR)
(me, "routing socket read: %s\n",
strerror(errno));
thr_exit(0);
}
continue;
}
if (ifam->ifam_version != RTM_VERSION) {
_NSCD_LOG(NSCD_LOG_FRONT_END,
NSCD_LOG_LEVEL_ERROR)
(me, "rx unknown version (%d) on "
"routing socket.\n",
ifam->ifam_version);
continue;
}
switch (ifam->ifam_type) {
case RTM_NEWADDR:
case RTM_DELADDR:
idx = get_cache_idx("ipnodes");
if (cache_ctx_p[idx] == NULL ||
cache_ctx_p[idx]->reaper_on != nscd_true)
break;
nsc_invalidate(cache_ctx_p[idx], NULL, NULL);
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:
case RTM_CHGADDR:
case RTM_FREEADDR:
break;
default:
_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
(me, "rx unknown msg type (%d) on routing socket.\n",
ifam->ifam_type);
break;
}
}
}
static void
keep_open_dns_socket(void)
{
_res.options |= RES_STAYOPEN;
}