/*
* 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 2012 Milan Jurik. All rights reserved.
*/
#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 <signal.h>
#include <unistd.h>
#include <dirent.h>
#include <procfs.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;
/* current self-cred configuration data being used */
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,
typedef struct _child {
int child_slot;
int child_door;
int next_open;
} child_t;
static int open_head;
static int open_tail;
static int used_slot;
/* nscd door id */
extern int _doorfd;
/* nscd id: main, forker, or child */
extern int _whoami;
/* forker nscd pid */
long activity = 0;
static void
free_slot(int s)
{
return;
}
void
{
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)
{
return (-1);
return (-1);
}
return (-1);
}
(me, "slot %d allocated\n", s);
} else
ch->child_slot = s;
ch->child_door = 0;
(me, "slot %d initialized\n", s);
return (0);
}
static int
{
(void) mutex_lock(&child_lock);
return (-1);
open_head = -1;
open_tail = -1;
used_slot = -1;
(void) mutex_unlock(&child_lock);
return (0);
}
static child_t *
int no_alloc)
{
int i;
(void) mutex_lock(&child_lock);
/* first find the slot with a matching uid */
for (i = 0; i <= used_slot; i++) {
(void) mutex_unlock(&child_lock);
(me, "slot %d found with uid %d\n",
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 */
(void) mutex_unlock(&child_lock);
return (ret);
}
}
used_slot++;
used_slot--;
(void) mutex_unlock(&child_lock);
return (ret);
}
} else {
/* got last one ? reset tail */
if (open_head == -1)
open_tail = -1;
}
(void) mutex_unlock(&child_lock);
return (ret);
}
static void
{
/* have open slot ? add to and reset tail */
if (open_tail != -1) {
} else {
/* no open slot ? make one */
}
}
static void
{
/* return if the slot has been returned by another thread */
return;
(void) mutex_lock(&child_lock);
/* check one more time */
(void) mutex_unlock(&child_lock);
return;
}
(void) mutex_unlock(&child_lock);
}
static int
int fd)
{
int ret;
if (fd != -1)
else
return (ret);
}
void
{
(void) mutex_lock(&forking_lock);
if (forking_door != -1)
(void) selfcred_kill(forking_door);
forking_door = -1;
(void) mutex_unlock(&forking_lock);
}
void
{
int i;
int ret;
(void) mutex_lock(&child_lock);
for (i = 0; i <= used_slot; i++) {
continue;
(me, "killing child process %d (doorfd %d)\n",
if (ret != -1)
}
(void) return_cslot_nolock(child[i]);
}
(void) mutex_unlock(&child_lock);
}
static int
int fd)
{
int ret;
/* Close door because the other side exited. */
return (ret);
}
/*ARGSUSED*/
static void *
void *arg)
{
char *fmri;
/* wait until forker exits */
fpid = forker_pid;
(void) selfcred_pulse(forking_door);
(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);
/* restart forker */
(me, "restarting the forker ...\n");
case (pid_t)-1:
(me, "unable to fork and start the forker ...\n");
/* enter the maintenance mode */
(me, "entering maintenance mode ...\n");
}
return ((void *)1);
case 0:
exit(0);
default:
forker_pid = fpid;
break;
}
return (NULL);
}
static void *
void *arg)
{
/* wait until child exits */
/* return the slot used by the child */
return (NULL);
}
void
void *buf,
int iam)
{
int cslot;
int errnum;
if (door_ucred(&uc) != 0) {
return;
}
switch (iam) {
case NSCD_MAIN:
/*
* I'm main, or uid from door is not correct,
* this must be an imposter
*/
(me, "MAIN IMPOSTER CAUGHT!\n");
}
break;
case NSCD_FORKER:
/*
* I'm forker, or uid from door is not correct,
* this must be an imposter
*/
(me, "FORKER IMPOSTER CAUGHT!\n");
break;
}
/* only main needs to know the forker */
break;
}
(me, "FORKER IMPOSTER CAUGHT: pid = %d should be %d\n",
break;
}
if (n_desc < 1) {
(me, "BAD FORKER, NO DOOR!\n");
break;
}
(void) mutex_lock(&forking_lock);
if (forking_door != -1)
(void) close(forking_door);
(void) mutex_unlock(&forking_lock);
} else {
break;
}
/* monitor the forker nscd */
THR_DETACHED, NULL);
break;
case NSCD_CHILD:
/* child nscd can only talk to the main nscd */
(me, "CHILD IMPOSTER CAUGHT!\n");
break;
}
/* get the main nscd assigned slot number */
(void) mutex_lock(&child_lock);
else
(void) mutex_unlock(&child_lock);
/* Bad slot number */
break;
}
(me, "CHILD IMPOSTER CAUGHT: uid = %d should be %d\n",
break;
}
break;
}
(me, "d_descriptor = %d, d_id = %lld\n",
(me, "child in slot %d has door %d\n",
/*
* let waiters know that the child is ready to
* serve
*/
/* monitor the child nscd */
break;
} else {
}
break;
}
ucred_free(uc);
}
void
void *buf,
int iam)
{
long last_active;
int done = 0;
/* only main nscd sends pulse */
return;
}
/* forker doesn't return stats, it just pauses */
if (_whoami == NSCD_FORKER) {
(me, "forker ready to pause ...\n");
for (;;)
(void) pause();
}
/* remember the current activity sequence number */
(void) mutex_lock(&activity_lock);
(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 {
(me, "active, sleep again for %d seconds\n",
}
(void) mutex_unlock(&activity_lock);
}
/* no activity in the specified seconds, exit and disconnect */
exit(0);
}
void
void *buf,
int iam)
{
int slot;
int ret;
char *fmri;
nscd_fork_t *f;
/* only main nscd sends fork requests */
return;
}
/* only forker handles fork requests */
if (_whoami != NSCD_FORKER) {
(me, "MAIN IMPOSTER CAUGHT! I AM NOT FORKER!\n");
return;
}
/* fork a child for the slot assigned by the main nscd */
/* ignore bad slot number */
(me, "bas slot number\n");
return;
}
(me, "before fork1() ...\n");
/*
* remember when this child nscd starts
* (replace the forker start time)
*/
/* close all except the log file */
if (_logfd > 0) {
int i;
for (i = 0; i < _logfd; i++)
(void) close(i);
} else
closefrom(0);
/* set up the door and server thread pool */
exit(-1);
/* tell libsldap to do self cred only */
(void) setup_ldap_backend();
/* notify main that child is active */
return;
(me, "forker unable to fork ...\n");
/* enter the maintenance mode */
(me, "entering maintenance mode ...\n");
}
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();
(me, "child forked: parent pid = %d, child pid = %d\n",
}
(me, "after fork\n");
}
static void
void *buf,
int doorfd,
int cslot,
{
int ret;
nscd_fork_t f;
/* if no door fd, do nothing */
if (doorfd == -1) {
}
(me, "sending fork request to door %d for slot %d "
(me, "fork request sent to door %d for slot %d (rc = %d)\n",
if (NSCD_STATUS_IS_NOT_OK(phdr)) {
(me, "fork request sent to door %d for slot %d failed: "
"status = %d, errno = %s, nscd status = %d\n", doorfd,
}
}
void
void *buf,
int *door)
{
int errnum;
(me, "getting an alternate door ...\n");
/* make sure there is a door to talk to the forker */
if (forking_door == -1) {
(me, "no door to talk to the forker\n");
return;
}
/* get door client's credential information */
if (door_ucred(&uc) != 0) {
return;
}
/* get door client's effective uid and effective gid */
ucred_free(uc);
/* is a slot available ? if not, no one to serve */
(me, "no child slot available (child array = %p, slot = %d)\n",
return;
}
/* create the per user nscd if necessary */
/* ask forker to fork a new child */
if (NSCD_STATUS_IS_NOT_OK(&phdr1)) {
return;
}
}
(me, "waiting for door (slot = %d, uid = %d, gid = %d)\n",
/* wait for the per user nscd to become available */
int err;
(me, "door wait timedout (slot = %d)\n",
ch->child_slot);
break;
}
}
}
return;
}
(me, "returning door %d for slot %d, uid %d, gid = %d\n",
}
static char **
int argc,
char **inargv)
{
char **newargv;
int c = 4;
int i = 0, j, k = 0, n = 0;
return (NULL);
return (NULL);
}
return (NULL);
}
for (i = 1; i < argc; i++) {
k = 2;
if (k == 0)
continue;
for (j = 0; j < n; j++)
return (NULL);
}
k--;
n++;
}
return (newargv);
}
void
char *path,
int argc,
char **argv)
{
/* if self cred is not configured, do nothing */
return;
/* save pathname and generate the new argv for the forker */
exit(1);
case (pid_t)-1:
exit(1);
break;
case 0:
/* start the forker nscd */
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
char *name,
void **func_p)
{
void *sym;
return (NSCD_SUCCESS);
}
/* no handle to close, it's OK */
return (NSCD_SUCCESS);
(me, "unable to dlopen libsldap.so.1");
return (NSCD_CFG_DLOPEN_ERROR);
}
}
return (NSCD_CFG_DLSYM_ERROR);
} else
return (NSCD_SUCCESS);
}
int
{
static int checked = 0;
static int is_on = 0;
static int (*ldap_func)();
int ldap_on = 0;
*dblist = selfcred_dbs;
return (is_on);
}
if (selfcred_dbs != NULL)
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
*/
ldap_on = 1;
}
checked = 1;
*dblist = selfcred_dbs;
return (is_on);
}
static nscd_rc_t
{
static void (*ldap_func)();
ldap_func(1);
return (NSCD_SUCCESS);
}
return (rc);
}
/*ARGSUSED*/
void
void *buf,
int buf_size)
{
int errnum = 0;
int ret;
/* get door client's credential information */
if (door_ucred(&uc) != 0) {
return;
}
/* get door client's effective uid */
ucred_free(uc);
/* is the per-user nscd running ? if not, no one to serve */
return;
}
sizeof (nscd_admin_t), phdr);
if (ret == NSS_SUCCESS) {
return;
}
}
static void
char param,
void *data)
{
if (param == 'e') {
}
if (param == 't') {
}
}
/* ARGSUSED */
void *data,
struct nscd_cfg_param_desc *pdesc,
void *cookie)
{
int off;
/*
* At init time, the whole group of config params are received.
* At update time, group or individual parameter value could
* be received.
*/
return (NSCD_SUCCESS);
}
/*
* individual config parameter
*/
return (NSCD_SUCCESS);
}
return (NSCD_SUCCESS);
}
return (NSCD_SUCCESS);
}
/* ARGSUSED */
void *data,
struct nscd_cfg_param_desc *pdesc,
void **cookie)
{
return (NSCD_SUCCESS);
}
/* ARGSUSED */
void **stat,
struct nscd_cfg_stat_desc *sdesc,
{
return (NSCD_SUCCESS);
}
static int
{
if (uid == 0) {
}
/* Process may have exited */
return (1);
}
/*
* Get the info structure for the process and close quickly.
*/
goto retry;
return (1);
}
return (0);
else
return (1);
}
/*
* FUNCTION: check_user_process
*/
/*ARGSUSED*/
static void *
{
int found;
for (;;) {
(void) sleep(60);
found = 0;
/*
* search the /proc directory and look at each process
*/
(me, "unable to open the /proc directory\n");
continue;
}
/* for each active process */
continue;
found = 1;
break;
}
}
/*
* if no process running as the PUN uid found, exit
* to kill this PUN
*/
if (found == 0) {
exit(1);
}
}
/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
}
static nscd_rc_t
int errnum;
(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.
*/
return (NSCD_THREAD_CREATE_ERROR);
}
return (NSCD_SUCCESS);
}
static void *
{
void *val;
if (prop) {
switch (type) {
case 'b':
break;
case 'i':
break;
}
}
switch (type) {
case 'b':
else
break;
case 'i':
break;
}
(me, "no value for config/%s (%s). "
"Using default \"%s\"\n", var,
}
return (def_val);
}