| /* |
| * 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 |
| * or http://www.opensolaris.org/os/licensing. |
| * 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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2012 Milan Jurik. All rights reserved. |
| * Copyright 2018 Joyent Inc. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <synch.h> |
| #include <thread.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <dlfcn.h> |
| #include <door.h> |
| #include <libscf.h> |
| #include <ucred.h> |
| #include <sys/varargs.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <sys/proc.h> |
| #include <procfs.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <libscf.h> |
| #include "nscd_door.h" |
| #include "nscd_config.h" |
| #include "nscd_log.h" |
| #include "nscd_frontend.h" |
| #include "nscd_selfcred.h" |
| #include "nscd_admin.h" |
| #include "nscd_common.h" |
| #include "ns_sldap.h" |
| |
| extern int _logfd; |
| static char *execpath; |
| static char **execargv; |
| static char *selfcred_dbs = NULL; |
| |
| static void *get_smf_prop(const char *var, char type, void *def_val); |
| |
| /* current self-cred configuration data being used */ |
| static nscd_cfg_global_selfcred_t nscd_selfcred_cfg_g; |
| |
| #define _NSCD_PUN_BLOCK 1024 |
| static uint8_t pu_nscd_enabled; |
| static int max_pu_nscd = _NSCD_PUN_BLOCK; |
| static int pu_nscd_ttl; |
| |
| static nscd_rc_t setup_ldap_backend(); |
| static nscd_rc_t init_user_proc_monitor(); |
| |
| /* |
| * clild state |
| */ |
| typedef enum { |
| CHILD_STATE_NONE = 0, |
| CHILD_STATE_UIDKNOWN, |
| CHILD_STATE_FORKSENT, |
| CHILD_STATE_PIDKNOWN |
| } child_state_t; |
| |
| |
| typedef struct _child { |
| int child_slot; |
| int child_door; |
| pid_t child_pid; |
| uid_t child_uid; |
| gid_t child_gid; |
| child_state_t child_state; |
| int next_open; |
| mutex_t *mutex; |
| cond_t *cond; |
| } child_t; |
| |
| static child_t **child = NULL; |
| static mutex_t child_lock = DEFAULTMUTEX; |
| static int open_head; |
| static int open_tail; |
| static int used_slot; |
| |
| /* nscd door id */ |
| extern int _doorfd; |
| static pid_t main_uid = 0; |
| |
| /* nscd id: main, forker, or child */ |
| extern int _whoami; |
| |
| /* forker nscd pid */ |
| static pid_t forker_pid = 0; |
| static pid_t forker_uid = 0; |
| |
| long activity = 0; |
| mutex_t activity_lock = DEFAULTMUTEX; |
| |
| static int forking_door = -1; |
| static mutex_t forking_lock = DEFAULTMUTEX; |
| |
| static void |
| free_slot(int s) |
| { |
| if (child[s] == NULL) |
| return; |
| free(child[s]->mutex); |
| free(child[s]->cond); |
| free(child[s]); |
| child[s] = NULL; |
| } |
| |
| void |
| _nscd_free_cslots() |
| { |
| |
| int i; |
| |
| (void) mutex_lock(&child_lock); |
| |
| for (i = 0; i < max_pu_nscd; i++) |
| free_slot(i); |
| |
| open_head = -1; |
| open_tail = -1; |
| used_slot = -1; |
| |
| (void) mutex_unlock(&child_lock); |
| |
| } |
| |
| static int |
| init_slot(int s) |
| { |
| child_t *ch; |
| char *me = "init_slot"; |
| |
| if (child[s] == NULL) { |
| child[s] = (child_t *)calloc(1, sizeof (child_t)); |
| if (child[s] == NULL) |
| return (-1); |
| ch = child[s]; |
| |
| if ((ch->mutex = (mutex_t *)calloc(1, |
| sizeof (mutex_t))) == NULL) { |
| free(ch); |
| return (-1); |
| } |
| (void) mutex_init(ch->mutex, USYNC_THREAD, NULL); |
| |
| if ((ch->cond = (cond_t *)calloc(1, |
| sizeof (cond_t))) == NULL) { |
| free(ch->mutex); |
| free(ch); |
| return (-1); |
| } |
| (void) cond_init(ch->cond, USYNC_THREAD, NULL); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "slot %d allocated\n", s); |
| } else |
| ch = child[s]; |
| |
| ch->child_slot = s; |
| ch->child_door = 0; |
| ch->child_state = CHILD_STATE_NONE; |
| ch->child_pid = 0; |
| ch->child_uid = 0; |
| ch->child_gid = 0; |
| ch->next_open = -1; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "slot %d initialized\n", s); |
| |
| return (0); |
| } |
| |
| static int |
| _nscd_init_cslots() |
| { |
| (void) mutex_lock(&child_lock); |
| |
| child = (child_t **)calloc(max_pu_nscd, sizeof (child_t *)); |
| if (child == NULL) |
| return (-1); |
| |
| open_head = -1; |
| open_tail = -1; |
| used_slot = -1; |
| |
| (void) mutex_unlock(&child_lock); |
| |
| return (0); |
| } |
| |
| static child_t * |
| get_cslot( |
| uid_t uid, |
| int no_alloc) |
| { |
| int i; |
| child_t *ch, *ret = NULL; |
| char *me = "get_cslot"; |
| |
| (void) mutex_lock(&child_lock); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "looking for uid %d (slot used = %d)\n", uid, used_slot); |
| |
| /* first find the slot with a matching uid */ |
| for (i = 0; i <= used_slot; i++) { |
| ch = child[i]; |
| if (ch->child_state >= CHILD_STATE_UIDKNOWN && |
| ch->child_uid == uid) { |
| ret = ch; |
| (void) mutex_unlock(&child_lock); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "slot %d found with uid %d\n", |
| ret->child_slot, ret->child_uid); |
| |
| return (ret); |
| } |
| } |
| |
| /* if no need to allocate a new slot, return NULL */ |
| if (no_alloc == 1) { |
| (void) mutex_unlock(&child_lock); |
| return (ret); |
| } |
| |
| /* no open slot ? get a new one */ |
| if (open_head == -1) { |
| /* if no slot available, allocate more */ |
| if (used_slot >= max_pu_nscd - 1) { |
| child_t **tmp; |
| int newmax = max_pu_nscd + _NSCD_PUN_BLOCK; |
| |
| tmp = (child_t **)calloc(newmax, sizeof (child_t *)); |
| if (tmp == NULL) { |
| (void) mutex_unlock(&child_lock); |
| return (ret); |
| } |
| (void) memcpy(tmp, child, sizeof (child_t) * |
| max_pu_nscd); |
| free(child); |
| child = tmp; |
| max_pu_nscd = newmax; |
| } |
| used_slot++; |
| if (init_slot(used_slot) == -1) { |
| used_slot--; |
| (void) mutex_unlock(&child_lock); |
| return (ret); |
| } |
| ch = child[used_slot]; |
| } else { |
| ch = child[open_head]; |
| open_head = ch->next_open; |
| /* got last one ? reset tail */ |
| if (open_head == -1) |
| open_tail = -1; |
| ch->next_open = -1; |
| } |
| |
| ch->child_uid = uid; |
| ch->child_state = CHILD_STATE_UIDKNOWN; |
| ret = ch; |
| |
| (void) mutex_unlock(&child_lock); |
| |
| return (ret); |
| } |
| |
| static void |
| return_cslot_nolock(child_t *ch) |
| { |
| |
| int slot = ch->child_slot; |
| |
| /* have open slot ? add to and reset tail */ |
| if (open_tail != -1) { |
| child[open_tail]->next_open = slot; |
| open_tail = slot; |
| } else { |
| /* no open slot ? make one */ |
| open_head = open_tail = slot; |
| } |
| |
| (void) init_slot(ch->child_slot); |
| } |
| |
| static void |
| return_cslot(child_t *ch) |
| { |
| |
| char *me = "return_cslot"; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "returning slot %d\n", ch->child_slot); |
| |
| /* return if the slot has been returned by another thread */ |
| if (ch->child_state == CHILD_STATE_NONE) |
| return; |
| |
| (void) mutex_lock(&child_lock); |
| |
| /* check one more time */ |
| if (ch->child_state == CHILD_STATE_NONE) { |
| (void) mutex_unlock(&child_lock); |
| return; |
| } |
| |
| return_cslot_nolock(ch); |
| |
| (void) mutex_unlock(&child_lock); |
| } |
| |
| static int |
| selfcred_kill( |
| int fd) |
| { |
| int ret; |
| char *me = "selfcred_kill"; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "sending kill to door %d\n", fd); |
| |
| if (fd != -1) |
| ret = _nscd_doorcall_fd(fd, NSCD_KILL, NULL, 0, |
| NULL, 0, NULL); |
| else |
| ret = _nscd_doorcall(NSCD_KILL); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "kill request sent to door %d (rc = %d)\n", fd, ret); |
| |
| return (ret); |
| } |
| |
| |
| void |
| _nscd_kill_forker() |
| { |
| (void) mutex_lock(&forking_lock); |
| if (forking_door != -1) |
| (void) selfcred_kill(forking_door); |
| forking_door = -1; |
| (void) mutex_unlock(&forking_lock); |
| } |
| |
| void |
| _nscd_kill_all_children() |
| { |
| int i; |
| int ret; |
| char *me = "_nscd_kill_all_children"; |
| |
| (void) mutex_lock(&child_lock); |
| for (i = 0; i <= used_slot; i++) { |
| if (child[i] == NULL) |
| continue; |
| |
| if (child[i]->child_state >= CHILD_STATE_PIDKNOWN) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "killing child process %d (doorfd %d)\n", |
| child[i]->child_pid, child[i]->child_door); |
| |
| ret = selfcred_kill(child[i]->child_door); |
| |
| if (ret != -1) |
| (void) kill(child[i]->child_pid, SIGTERM); |
| } |
| if (child[i]->child_state != CHILD_STATE_NONE) |
| (void) return_cslot_nolock(child[i]); |
| } |
| (void) mutex_unlock(&child_lock); |
| } |
| static int |
| selfcred_pulse( |
| int fd) |
| { |
| int ret; |
| char *me = "selfcred_pulse"; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "start monitoring door %d\n", fd); |
| |
| ret = _nscd_doorcall_fd(fd, NSCD_PULSE |(_whoami & NSCD_WHOAMI), |
| NULL, 0, NULL, 0, NULL); |
| |
| /* Close door because the other side exited. */ |
| (void) close(fd); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door (%d) monitor exited (rc = %d)\n", fd, ret); |
| |
| return (ret); |
| } |
| |
| /*ARGSUSED*/ |
| static void * |
| forker_monitor( |
| void *arg) |
| { |
| pid_t fpid; |
| char *fmri; |
| char *me = "forker_monitor"; |
| |
| (void) thr_setname(thr_self(), me); |
| |
| /* wait until forker exits */ |
| fpid = forker_pid; |
| (void) selfcred_pulse(forking_door); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "forker (pid = %d) exited or crashed, " |
| "killing all child processes\n", fpid); |
| |
| (void) mutex_lock(&forking_lock); |
| forking_door = -1; |
| forker_pid = -1; |
| (void) mutex_unlock(&forking_lock); |
| |
| /* forker exited/crashed, kill all the child processes */ |
| _nscd_kill_all_children(); |
| |
| /* restart forker */ |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "restarting the forker ...\n"); |
| |
| switch (fpid = fork1()) { |
| case (pid_t)-1: |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "unable to fork and start the forker ...\n"); |
| |
| /* enter the maintenance mode */ |
| if ((fmri = getenv("SMF_FMRI")) != NULL) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "entering maintenance mode ...\n"); |
| (void) smf_maintain_instance(fmri, SMF_TEMPORARY); |
| } |
| return ((void *)1); |
| case 0: |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "execv path = %s\n", execpath); |
| |
| (void) execv(execpath, execargv); |
| exit(0); |
| default: |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "new forker's pid is %d\n", fpid); |
| forker_pid = fpid; |
| break; |
| } |
| |
| return (NULL); |
| } |
| |
| static void * |
| child_monitor( |
| void *arg) |
| { |
| child_t *ch = (child_t *)arg; |
| pid_t cpid; |
| char *me = "child_monitor"; |
| |
| /* wait until child exits */ |
| cpid = ch->child_pid; |
| (void) selfcred_pulse(ch->child_door); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "child (pid = %d) exited or crashed ...\n", cpid); |
| |
| /* return the slot used by the child */ |
| return_cslot(ch); |
| |
| return (NULL); |
| } |
| |
| |
| void |
| _nscd_proc_iamhere( |
| void *buf, |
| door_desc_t *dp, |
| uint_t n_desc, |
| int iam) |
| { |
| int cslot; |
| child_t *ch; |
| int errnum; |
| ucred_t *uc = NULL; |
| uid_t uid; |
| nscd_imhere_t *ih; |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| char *me = "_nscd_proc_iamhere"; |
| |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "%d receives iamhere from %d\n", _whoami, iam); |
| |
| if (door_ucred(&uc) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door_ucred failed: %s\n", strerror(errnum)); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, errnum, |
| NSCD_DOOR_UCRED_ERROR); |
| return; |
| } |
| uid = ucred_geteuid(uc); |
| |
| switch (iam) { |
| |
| case NSCD_MAIN: |
| if (_whoami == NSCD_MAIN || uid != main_uid) { |
| /* |
| * I'm main, or uid from door is not correct, |
| * this must be an imposter |
| */ |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "MAIN IMPOSTER CAUGHT!\n"); |
| |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_MAIN_IMPOSTER); |
| } |
| break; |
| |
| case NSCD_FORKER: |
| if (_whoami == NSCD_FORKER || uid != forker_uid) { |
| /* |
| * I'm forker, or uid from door is not correct, |
| * this must be an imposter |
| */ |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "FORKER IMPOSTER CAUGHT!\n"); |
| |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_FORKER_IMPOSTER); |
| break; |
| } |
| |
| /* only main needs to know the forker */ |
| if (_whoami != NSCD_MAIN) { |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_WRONG_NSCD); |
| break; |
| } |
| |
| if (ucred_getpid(uc) != forker_pid) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "FORKER IMPOSTER CAUGHT: pid = %d should be %d\n", |
| ucred_getpid(uc), forker_pid); |
| |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_FORKER_IMPOSTER); |
| break; |
| } |
| |
| if (n_desc < 1) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "BAD FORKER, NO DOOR!\n"); |
| |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_NO_DOOR); |
| break; |
| } |
| |
| if ((dp->d_attributes & DOOR_DESCRIPTOR) && |
| dp->d_data.d_desc.d_descriptor > 0 && |
| dp->d_data.d_desc.d_id != 0) { |
| (void) mutex_lock(&forking_lock); |
| if (forking_door != -1) |
| (void) close(forking_door); |
| forking_door = dp->d_data.d_desc.d_descriptor; |
| (void) mutex_unlock(&forking_lock); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "forking door is %d\n", forking_door); |
| |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| } else { |
| NSCD_SET_STATUS(phdr, NSS_ALTRETRY, 0); |
| break; |
| } |
| |
| /* monitor the forker nscd */ |
| (void) thr_create(NULL, 0, forker_monitor, NULL, |
| THR_DETACHED, NULL); |
| |
| break; |
| |
| case NSCD_CHILD: |
| if (_whoami != NSCD_MAIN) { |
| /* child nscd can only talk to the main nscd */ |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "CHILD IMPOSTER CAUGHT!\n"); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_CHILD_IMPOSTER); |
| break; |
| } |
| |
| /* get the main nscd assigned slot number */ |
| ih = NSCD_N2N_DOOR_DATA(nscd_imhere_t, buf); |
| cslot = ih->slot; |
| (void) mutex_lock(&child_lock); |
| if (cslot < 0 || cslot >= max_pu_nscd) |
| ch = NULL; |
| else |
| ch = child[cslot]; |
| (void) mutex_unlock(&child_lock); |
| |
| if (ch == NULL) { |
| /* Bad slot number */ |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "bad slot number %d\n", cslot); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_INVALID_SLOT_NUMBER); |
| break; |
| } |
| |
| if (uid != ch->child_uid) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "CHILD IMPOSTER CAUGHT: uid = %d should be %d\n", |
| uid, ch->child_uid); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_CHILD_IMPOSTER); |
| break; |
| } |
| |
| if (ch->child_state != CHILD_STATE_UIDKNOWN && |
| ch->child_state != CHILD_STATE_FORKSENT) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "invalid slot/child state (%d) for uid %d\n", |
| ch->child_state, uid); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_INVALID_SLOT_STATE); |
| break; |
| } |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "d_descriptor = %d, d_id = %lld\n", |
| dp->d_data.d_desc.d_descriptor, dp->d_data.d_desc.d_id); |
| |
| if ((dp->d_attributes & DOOR_DESCRIPTOR) && |
| dp->d_data.d_desc.d_descriptor > 0 && |
| dp->d_data.d_desc.d_id != 0) { |
| (void) mutex_lock(ch->mutex); |
| if (ch->child_door != -1) |
| (void) close(ch->child_door); |
| ch->child_door = dp->d_data.d_desc.d_descriptor; |
| ch->child_pid = ucred_getpid(uc); |
| ch->child_state = CHILD_STATE_PIDKNOWN; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "child in slot %d has door %d\n", |
| cslot, ch->child_door); |
| |
| /* |
| * let waiters know that the child is ready to |
| * serve |
| */ |
| (void) cond_broadcast(ch->cond); |
| (void) mutex_unlock(ch->mutex); |
| |
| /* monitor the child nscd */ |
| (void) thr_create(NULL, 0, child_monitor, |
| ch, THR_DETACHED, NULL); |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| break; |
| } else { |
| NSCD_SET_STATUS(phdr, NSS_ALTRETRY, 0); |
| } |
| break; |
| } |
| |
| ucred_free(uc); |
| uc = NULL; |
| } |
| |
| void |
| _nscd_proc_pulse( |
| void *buf, |
| int iam) |
| { |
| long last_active; |
| int done = 0; |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| char *me = "_nscd_proc_pulse"; |
| |
| /* only main nscd sends pulse */ |
| if (iam != NSCD_MAIN) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "MAIN IMPOSTER CAUGHT! i am %d not NSCD_MAIN\n", iam); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_MAIN_IMPOSTER); |
| return; |
| } |
| |
| /* forker doesn't return stats, it just pauses */ |
| if (_whoami == NSCD_FORKER) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "forker ready to pause ...\n"); |
| |
| for (;;) |
| (void) pause(); |
| } |
| |
| /* remember the current activity sequence number */ |
| (void) mutex_lock(&activity_lock); |
| last_active = activity; |
| (void) mutex_unlock(&activity_lock); |
| |
| while (!done) { |
| |
| /* allow per_user_nscd_ttl seconds of inactivity */ |
| (void) sleep(pu_nscd_ttl); |
| |
| (void) mutex_lock(&activity_lock); |
| if (last_active == activity) |
| done = 1; |
| else { |
| last_active = activity; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "active, sleep again for %d seconds\n", |
| pu_nscd_ttl); |
| } |
| (void) mutex_unlock(&activity_lock); |
| } |
| |
| /* no activity in the specified seconds, exit and disconnect */ |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "no activity in the last %d seconds, exit\n", pu_nscd_ttl); |
| exit(0); |
| } |
| |
| void |
| _nscd_proc_fork( |
| void *buf, |
| int iam) |
| { |
| int slot; |
| int ret; |
| char *fmri; |
| pid_t cid; |
| uid_t set2uid; |
| gid_t set2gid; |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| char *me = "_nscd_proc_fork"; |
| nscd_fork_t *f; |
| nscd_imhere_t ih; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "%d receives fork request from %d\n", _whoami, iam); |
| |
| /* only main nscd sends fork requests */ |
| if (iam != NSCD_MAIN) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "MAIN IMPOSTER CAUGHT! i am %d not NSCD_MAIN\n", iam); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_MAIN_IMPOSTER); |
| return; |
| } |
| |
| /* only forker handles fork requests */ |
| if (_whoami != NSCD_FORKER) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "MAIN IMPOSTER CAUGHT! I AM NOT FORKER!\n"); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_WRONG_NSCD); |
| return; |
| } |
| |
| /* fork a child for the slot assigned by the main nscd */ |
| f = NSCD_N2N_DOOR_DATA(nscd_fork_t, buf); |
| slot = f->slot; |
| /* set the uid/gid as assigned by the main nscd */ |
| set2uid = f->uid; |
| set2gid = f->gid; |
| |
| /* ignore bad slot number */ |
| if (slot < 0 || slot >= max_pu_nscd) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "bas slot number\n"); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_INVALID_SLOT_NUMBER); |
| return; |
| } |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "before fork1() ...\n"); |
| |
| if ((cid = fork1()) == 0) { |
| _whoami = NSCD_CHILD; |
| |
| /* |
| * remember when this child nscd starts |
| * (replace the forker start time) |
| */ |
| _nscd_set_start_time(1); |
| |
| /* close all except the log file */ |
| if (_logfd > 0) { |
| int i; |
| for (i = 0; i < _logfd; i++) |
| (void) close(i); |
| closefrom(_logfd + 1); |
| } else |
| closefrom(0); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "child %d\n", getpid()); |
| |
| (void) setgid(set2gid); |
| (void) setuid(set2uid); |
| |
| /* set up the door and server thread pool */ |
| if ((_doorfd = _nscd_setup_child_server(_doorfd)) == -1) |
| exit(-1); |
| |
| /* tell libsldap to do self cred only */ |
| (void) setup_ldap_backend(); |
| |
| /* notify main that child is active */ |
| ih.slot = slot; |
| for (ret = NSS_ALTRETRY; ret == NSS_ALTRETRY; ) |
| ret = _nscd_doorcall_sendfd(_doorfd, |
| NSCD_IMHERE | (NSCD_CHILD & NSCD_WHOAMI), |
| &ih, sizeof (ih), NULL); |
| |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| return; |
| } if (cid == (pid_t)-1) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "forker unable to fork ...\n"); |
| |
| /* enter the maintenance mode */ |
| if ((fmri = getenv("SMF_FMRI")) != NULL) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "entering maintenance mode ...\n"); |
| (void) smf_maintain_instance(fmri, SMF_TEMPORARY); |
| } |
| exit(0); |
| } else { |
| /* |
| * start the monitor so as to exit as early as |
| * possible if no other processes are running |
| * with the same PUN uid (i.e., this PUN is |
| * not needed any more) |
| */ |
| (void) init_user_proc_monitor(); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "child forked: parent pid = %d, child pid = %d\n", |
| getpid(), cid); |
| |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| } |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "after fork\n"); |
| } |
| |
| static void |
| selfcred_fork( |
| void *buf, |
| int doorfd, |
| int cslot, |
| uid_t uid, |
| gid_t gid) |
| { |
| int ret; |
| nscd_fork_t f; |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| char *me = "selfcred_fork"; |
| |
| /* if no door fd, do nothing */ |
| if (doorfd == -1) { |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_NO_DOOR); |
| } |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "sending fork request to door %d for slot %d " |
| "(uid = %d, gid = %d)\n", doorfd, cslot, uid, gid); |
| |
| f.slot = cslot; |
| f.uid = uid; |
| f.gid = gid; |
| |
| ret = _nscd_doorcall_fd(doorfd, NSCD_FORK|(_whoami&NSCD_WHOAMI), |
| &f, sizeof (f), NULL, 0, phdr); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "fork request sent to door %d for slot %d (rc = %d)\n", |
| doorfd, cslot, ret); |
| |
| if (NSCD_STATUS_IS_NOT_OK(phdr)) { |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "fork request sent to door %d for slot %d failed: " |
| "status = %d, errno = %s, nscd status = %d\n", doorfd, |
| cslot, NSCD_GET_STATUS(phdr), |
| strerror(NSCD_GET_ERRNO(phdr)), |
| NSCD_GET_NSCD_STATUS(phdr)); |
| |
| } |
| } |
| |
| void |
| _nscd_proc_alt_get( |
| void *buf, |
| int *door) |
| { |
| int errnum; |
| uid_t set2uid; |
| gid_t set2gid; |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| char *me = "_nscd_proc_alt_get"; |
| ucred_t *uc = NULL; |
| child_t *ch; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "getting an alternate door ...\n"); |
| |
| /* make sure there is a door to talk to the forker */ |
| if (forking_door == -1) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_ERROR) |
| (me, "no door to talk to the forker\n"); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_NO_FORKER); |
| return; |
| } |
| |
| /* get door client's credential information */ |
| if (door_ucred(&uc) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door_ucred failed: %s\n", strerror(errnum)); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, errnum, |
| NSCD_DOOR_UCRED_ERROR); |
| return; |
| } |
| |
| /* get door client's effective uid and effective gid */ |
| set2uid = ucred_geteuid(uc); |
| set2gid = ucred_getegid(uc); |
| ucred_free(uc); |
| uc = NULL; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "child uid = %d, gid = %d\n", set2uid, set2gid); |
| |
| /* is a slot available ? if not, no one to serve */ |
| if (child == NULL || (ch = get_cslot(set2uid, 0)) == NULL) { |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "no child slot available (child array = %p, slot = %d)\n", |
| child, ch->child_slot); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_NO_CHILD_SLOT); |
| return; |
| } |
| |
| /* create the per user nscd if necessary */ |
| if (ch->child_state != CHILD_STATE_PIDKNOWN) { |
| |
| nss_pheader_t phdr1; |
| NSCD_CLEAR_STATUS(&phdr1); |
| |
| (void) mutex_lock(ch->mutex); |
| if (ch->child_state == CHILD_STATE_UIDKNOWN) { |
| |
| /* ask forker to fork a new child */ |
| selfcred_fork(&phdr1, forking_door, ch->child_slot, |
| set2uid, set2gid); |
| if (NSCD_STATUS_IS_NOT_OK(&phdr1)) { |
| (void) mutex_unlock(ch->mutex); |
| NSCD_COPY_STATUS(phdr, &phdr1); |
| return; |
| } |
| ch->child_state = CHILD_STATE_FORKSENT; |
| } |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "waiting for door (slot = %d, uid = %d, gid = %d)\n", |
| ch->child_slot, set2uid, set2gid); |
| |
| /* wait for the per user nscd to become available */ |
| while (ch->child_state == CHILD_STATE_FORKSENT) { |
| timestruc_t to; |
| int err; |
| int ttl = 5; |
| |
| to.tv_sec = ttl; |
| to.tv_nsec = 0; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "cond_reltimedwait %d seconds\n", ttl); |
| err = cond_reltimedwait(ch->cond, ch->mutex, &to); |
| if (err == ETIME) { |
| ch->child_state = CHILD_STATE_UIDKNOWN; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, |
| NSCD_LOG_LEVEL_DEBUG) |
| (me, "door wait timedout (slot = %d)\n", |
| ch->child_slot); |
| break; |
| } |
| } |
| (void) mutex_unlock(ch->mutex); |
| } |
| |
| if (ch->child_state != CHILD_STATE_PIDKNOWN) { |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_INVALID_SLOT_STATE); |
| return; |
| } |
| |
| *door = ch->child_door; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "returning door %d for slot %d, uid %d, gid = %d\n", |
| *door, ch->child_slot, set2uid, set2gid); |
| |
| NSCD_SET_STATUS(phdr, NSS_ALTRETRY, 0); |
| } |
| |
| static char ** |
| cpargv( |
| int argc, |
| char **inargv) |
| { |
| char **newargv; |
| int c = 4; |
| int i = 0, j, k = 0, n = 0; |
| |
| newargv = (char **)calloc(c + 1, sizeof (char *)); |
| if (newargv == NULL) |
| return (NULL); |
| |
| newargv[n] = strdup(inargv[0]); |
| if (newargv[n++] == NULL) { |
| free(newargv); |
| return (NULL); |
| } |
| |
| newargv[n] = strdup("-F"); |
| if (newargv[n++] == NULL) { |
| free(newargv[0]); |
| free(newargv); |
| return (NULL); |
| } |
| |
| for (i = 1; i < argc; i++) { |
| if (strcmp(inargv[i], "-f") == 0) |
| k = 2; |
| if (k == 0) |
| continue; |
| |
| newargv[n] = strdup(inargv[i]); |
| if (newargv[n] == NULL) { |
| for (j = 0; j < n; j++) |
| free(newargv[j]); |
| free(newargv); |
| return (NULL); |
| } |
| |
| k--; |
| n++; |
| } |
| return (newargv); |
| } |
| |
| |
| void |
| _nscd_start_forker( |
| char *path, |
| int argc, |
| char **argv) |
| { |
| pid_t cid; |
| |
| /* if self cred is not configured, do nothing */ |
| if (!_nscd_is_self_cred_on(1, NULL)) |
| return; |
| |
| /* save pathname and generate the new argv for the forker */ |
| execpath = strdup(path); |
| execargv = cpargv(argc, argv); |
| if (execpath == NULL || execargv == NULL) |
| exit(1); |
| |
| switch (cid = fork1()) { |
| case (pid_t)-1: |
| exit(1); |
| break; |
| case 0: |
| /* start the forker nscd */ |
| (void) execv(path, execargv); |
| exit(0); |
| break; |
| default: |
| /* main nscd */ |
| /* remember process id of the forker */ |
| forker_pid = cid; |
| |
| /* enable child nscd management */ |
| (void) _nscd_init_cslots(); |
| break; |
| } |
| } |
| |
| static nscd_rc_t |
| get_ldap_funcs( |
| char *name, |
| void **func_p) |
| { |
| char *me = "get_ldap_funcs"; |
| static void *handle = NULL; |
| void *sym; |
| |
| if (name == NULL && handle != NULL) { |
| (void) dlclose(handle); |
| return (NSCD_SUCCESS); |
| } |
| /* no handle to close, it's OK */ |
| if (name == NULL) |
| return (NSCD_SUCCESS); |
| |
| if (handle == NULL) { |
| handle = dlopen("libsldap.so.1", RTLD_LAZY); |
| if (handle == NULL) { |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_ERROR) |
| (me, "unable to dlopen libsldap.so.1"); |
| return (NSCD_CFG_DLOPEN_ERROR); |
| } |
| } |
| |
| if ((sym = dlsym(handle, name)) == NULL) { |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_ERROR) |
| (me, "unable to find symbol %s", name); |
| return (NSCD_CFG_DLSYM_ERROR); |
| } else |
| (void) memcpy(func_p, &sym, sizeof (void *)); |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| |
| int |
| _nscd_is_self_cred_on(int recheck, char **dblist) |
| { |
| static int checked = 0; |
| static int is_on = 0; |
| static int (*ldap_func)(); |
| char *srcs = "ldap"; /* only ldap support self cred */ |
| int ldap_on = 0; |
| |
| char *ldap_sc_func = "__ns_ldap_self_gssapi_config"; |
| ns_ldap_self_gssapi_config_t ldap_config; |
| |
| if (checked && !recheck) { |
| if (is_on && dblist != NULL) |
| *dblist = selfcred_dbs; |
| return (is_on); |
| } |
| |
| if (selfcred_dbs != NULL) |
| free(selfcred_dbs); |
| selfcred_dbs = _nscd_srcs_in_db_nsw_policy(1, &srcs); |
| |
| if (selfcred_dbs == NULL) { |
| is_on = 0; |
| checked = 1; |
| return (0); |
| } |
| |
| /* |
| * also check the ldap backend to see if |
| * the configuration there is good for |
| * doing self credentialing |
| */ |
| if (ldap_func == NULL) |
| (void) get_ldap_funcs(ldap_sc_func, (void **)&ldap_func); |
| if (ldap_func != NULL) { |
| if (ldap_func(&ldap_config) == NS_LDAP_SUCCESS && |
| ldap_config != NS_LDAP_SELF_GSSAPI_CONFIG_NONE) |
| ldap_on = 1; |
| } |
| |
| is_on = (pu_nscd_enabled == nscd_true) && ldap_on; |
| |
| checked = 1; |
| |
| if (is_on && dblist != NULL) |
| *dblist = selfcred_dbs; |
| |
| return (is_on); |
| } |
| |
| static nscd_rc_t |
| setup_ldap_backend() |
| { |
| nscd_rc_t rc; |
| static void (*ldap_func)(); |
| char *ldap_sc_func = "__ns_ldap_self_gssapi_only_set"; |
| if (ldap_func == NULL) |
| rc = get_ldap_funcs(ldap_sc_func, (void **)&ldap_func); |
| if (ldap_func != NULL) { |
| ldap_func(1); |
| return (NSCD_SUCCESS); |
| } |
| return (rc); |
| } |
| |
| /*ARGSUSED*/ |
| void |
| _nscd_peruser_getadmin( |
| void *buf, |
| int buf_size) |
| { |
| void *result_mn = NSCD_N2N_DOOR_DATA(void, buf); |
| int errnum = 0; |
| int ret; |
| uid_t uid; |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| char *me = "_nscd_peruser_getadmin"; |
| ucred_t *uc = NULL; |
| child_t *ch; |
| |
| /* get door client's credential information */ |
| if (door_ucred(&uc) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door_ucred failed: %s\n", strerror(errnum)); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, errnum, |
| NSCD_DOOR_UCRED_ERROR); |
| return; |
| } |
| |
| /* get door client's effective uid */ |
| uid = ucred_geteuid(uc); |
| ucred_free(uc); |
| uc = NULL; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "per user get admin ... (uid = %d)\n", uid); |
| |
| /* is the per-user nscd running ? if not, no one to serve */ |
| ch = get_cslot(uid, 1); |
| if (ch == NULL) { |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_NO_CHILD_SLOT); |
| return; |
| } |
| |
| ret = _nscd_doorcall_fd(ch->child_door, NSCD_GETADMIN, |
| NULL, sizeof (nscd_admin_t), result_mn, |
| sizeof (nscd_admin_t), phdr); |
| |
| if (ret == NSS_SUCCESS) { |
| phdr->data_len = sizeof (nscd_admin_t); |
| return; |
| } |
| } |
| |
| static void |
| set_selfcred_cfg( |
| char param, |
| void *data) |
| { |
| int64_t prop_int; |
| uint8_t prop_boolean; |
| char *me = "set_selfcred_cfg"; |
| |
| if (param == 'e') { |
| prop_boolean = *(uint8_t *)data; |
| pu_nscd_enabled = *(uint8_t *)get_smf_prop( |
| "enable_per_user_lookup", 'b', &prop_boolean); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "self cred config: enabled = %d\n", pu_nscd_enabled); |
| } |
| |
| if (param == 't') { |
| prop_int = *(int *)data; |
| pu_nscd_ttl = *(int64_t *)get_smf_prop( |
| "per_user_nscd_time_to_live", 'i', &prop_int); |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "self cred config: PUN TTL = %d\n", pu_nscd_ttl); |
| } |
| } |
| |
| /* ARGSUSED */ |
| nscd_rc_t |
| _nscd_cfg_selfcred_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) |
| { |
| |
| nscd_cfg_global_selfcred_t *sc_cfg = &nscd_selfcred_cfg_g; |
| int off; |
| |
| /* |
| * At init time, the whole group of config params are received. |
| * At update time, group or individual parameter value could |
| * be received. |
| */ |
| |
| if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { |
| |
| *sc_cfg = *(nscd_cfg_global_selfcred_t *)data; |
| |
| off = offsetof(nscd_cfg_global_selfcred_t, |
| enable_selfcred); |
| set_selfcred_cfg('e', (char *)data + off); |
| |
| off = offsetof(nscd_cfg_global_selfcred_t, |
| per_user_nscd_ttl); |
| set_selfcred_cfg('t', (char *)data + off); |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| /* |
| * individual config parameter |
| */ |
| off = offsetof(nscd_cfg_global_selfcred_t, enable_selfcred); |
| if (pdesc->p_offset == off) { |
| sc_cfg->enable_selfcred = *(nscd_bool_t *)data; |
| set_selfcred_cfg('e', data); |
| return (NSCD_SUCCESS); |
| } |
| |
| off = offsetof(nscd_cfg_global_selfcred_t, per_user_nscd_ttl); |
| if (pdesc->p_offset == off) { |
| sc_cfg->per_user_nscd_ttl = *(int *)data; |
| set_selfcred_cfg('t', data); |
| return (NSCD_SUCCESS); |
| } |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| /* ARGSUSED */ |
| nscd_rc_t |
| _nscd_cfg_selfcred_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) |
| { |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| /* ARGSUSED */ |
| nscd_rc_t |
| _nscd_cfg_selfcred_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); |
| } |
| |
| static int |
| check_uid(char *pid_name) |
| { |
| char pname[PATH_MAX]; |
| static pid_t pid = 0; |
| static uid_t uid = 0; |
| static uid_t euid = 0; |
| int pfd; /* file descriptor for /proc/<pid>/psinfo */ |
| psinfo_t info; /* process information from /proc */ |
| |
| if (uid == 0) { |
| pid = getpid(); |
| uid = getuid(); |
| euid = geteuid(); |
| } |
| |
| (void) snprintf(pname, sizeof (pname), "/proc/%s/psinfo", pid_name); |
| retry: |
| if ((pfd = open(pname, O_RDONLY)) == -1) { |
| /* Process may have exited */ |
| return (1); |
| } |
| |
| /* |
| * Get the info structure for the process and close quickly. |
| */ |
| if (read(pfd, (char *)&info, sizeof (info)) < 0) { |
| int saverr = errno; |
| |
| (void) close(pfd); |
| if (saverr == EAGAIN) |
| goto retry; |
| if (saverr != ENOENT) |
| return (1); |
| } |
| (void) close(pfd); |
| |
| if (info.pr_pid != pid && |
| info.pr_uid == uid && info.pr_euid == euid) |
| return (0); |
| else |
| return (1); |
| } |
| |
| |
| /* |
| * FUNCTION: check_user_process |
| */ |
| /*ARGSUSED*/ |
| static void * |
| check_user_process(void *arg) |
| { |
| |
| DIR *dp; |
| struct dirent *ep; |
| int found; |
| char *me = "check_user_process"; |
| |
| (void) thr_setname(thr_self(), me); |
| |
| for (;;) { |
| (void) sleep(60); |
| |
| found = 0; |
| |
| /* |
| * search the /proc directory and look at each process |
| */ |
| if ((dp = opendir("/proc")) == NULL) { |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_ERROR) |
| (me, "unable to open the /proc directory\n"); |
| continue; |
| } |
| |
| /* for each active process */ |
| while (ep = readdir(dp)) { |
| if (ep->d_name[0] == '.') /* skip . and .. */ |
| continue; |
| if (check_uid(ep->d_name) == 0) { |
| found = 1; |
| break; |
| } |
| } |
| |
| /* |
| * if no process running as the PUN uid found, exit |
| * to kill this PUN |
| */ |
| if (found == 0) { |
| (void) closedir(dp); |
| exit(1); |
| } |
| (void) closedir(dp); |
| } |
| /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/ |
| } |
| |
| static nscd_rc_t |
| init_user_proc_monitor() { |
| |
| int errnum; |
| char *me = "init_user_proc_monitor"; |
| |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_DEBUG) |
| (me, "initializing the user process monitor\n"); |
| |
| /* |
| * start a thread to make sure there is at least a process |
| * running as the PUN user. If not, terminate this PUN. |
| */ |
| if (thr_create(NULL, NULL, check_user_process, |
| NULL, THR_DETACHED, NULL) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_ERROR) |
| (me, "thr_create: %s\n", strerror(errnum)); |
| return (NSCD_THREAD_CREATE_ERROR); |
| } |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| static void * |
| get_smf_prop(const char *var, char type, void *def_val) |
| { |
| scf_simple_prop_t *prop; |
| void *val; |
| char *me = "get_smf_prop"; |
| |
| prop = scf_simple_prop_get(NULL, NULL, "config", var); |
| if (prop) { |
| switch (type) { |
| case 'b': |
| val = scf_simple_prop_next_boolean(prop); |
| if (val != NULL) |
| (void) memcpy(def_val, val, sizeof (uint8_t)); |
| break; |
| |
| case 'i': |
| val = scf_simple_prop_next_integer(prop); |
| if (val != NULL) |
| (void) memcpy(def_val, val, sizeof (int64_t)); |
| break; |
| } |
| scf_simple_prop_free(prop); |
| } |
| |
| if (prop == NULL || val == NULL) { |
| char vs[64]; |
| |
| switch (type) { |
| case 'b': |
| if (*(uint8_t *)def_val) |
| (void) strcpy(vs, "yes"); |
| else |
| (void) strcpy(vs, "no"); |
| |
| break; |
| |
| case 'i': |
| (void) sprintf(vs, "%lld", *(int64_t *)def_val); |
| break; |
| |
| } |
| _NSCD_LOG(NSCD_LOG_SELF_CRED, NSCD_LOG_LEVEL_ALERT) |
| (me, "no value for config/%s (%s). " |
| "Using default \"%s\"\n", var, |
| scf_strerror(scf_error()), vs); |
| } |
| |
| return (def_val); |
| } |