Commit a9bb16e3 authored by Niels Möller's avatar Niels Möller
Browse files

Deleted old code to handle utmp and wtmp.

That work is now delegated to a helper process, which can run with
sufficient privileges.
(make_unix_process): Made static.
(send_helper_request): New function.
(class logout_notice): New attributes helper_fd and helper_ref.
(do_logout_notice): Send a PTY_REQUEST_LOGOUT to the helper
process.
(exec_shell): Made static.
(spawn_error): New arguments helper_fd and helper_ref. Send a
PTY_REQUEST_DESTROY to the helper process.
(make_logout_notice): New arguments helper_fd and helper_ref.
(spawn_parent): Pass helper_fd and helper_ref to
make_logout_notice.
(spawn_child): New arguments helper_fd and helper_ref. Send a
PTY_REQUEST_LOGIN to the helper process.
(spawn_shell): New arguments helper_fd. Send a PTY_REQUEST_CREATE
to the helper process. Pass on helper_Fd and helper_ref to help
functions.

Rev: src/unix_process.c:1.16.2.5
parent abbf35d1
......@@ -40,33 +40,11 @@
#include <sys/types.h>
#include <sys/wait.h>
/* FIXME: Should this be done in configure instead? Doesn't hurt though */
#if WITH_UTMP
# if HAVE_UTMP_H
# include <utmp.h>
# ifndef WTMP_FILE
# define WTMP_FILE "/var/adm/wtmp"
# endif
# endif
# if HAVE_UTMPX_H
# include <utmpx.h>
# ifndef WTMPX_FILE
# define WTMPX_FILE "/var/adm/wtmpx"
# endif
# endif
#endif
#if HAVE_LIBUTIL_H
# include <libutil.h>
#endif
#include "environ.h"
#include "format.h"
#include "lsh_string.h"
#include "lsh_process.h"
#include "pty-helper.h"
#include "reaper.h"
#include "server_pty.h"
#include "werror.h"
......@@ -79,57 +57,6 @@
#include "unix_process.c.x"
/* Figure out which flavour of utmp/utmpx to use. */
#if HAVE_UTMPX_H && HAVE_PUTUTXLINE
# define USE_UTMPX 1
# define PUTUTXLINE pututxline
# define GETUTXID getutxid
# define SETUTXENT setutxent
# define UTMPX utmpx
# define UTMPX_UT_EXIT HAVE_STRUCT_UTMPX_UT_EXIT
# define UTMPX_UT_EXIT_E_TERMINATION HAVE_STRUCT_UTMPX_UT_EXIT_E_TERMINATION
# define UTMPX_UT_EXIT___E_TERMINATION HAVE_STRUCT_UTMPX_UT_EXIT___E_TERMINATION
# define UTMPX_UT_EXIT_UT_TERMINATION HAVE_STRUCT_UTMPX_UT_EXIT_UT_TERMINATION
# define UTMPX_UT_PID HAVE_STRUCT_UTMPX_UT_PID
# define UTMPX_UT_USER HAVE_STRUCT_UTMPX_UT_USER
# define UTMPX_UT_TV HAVE_STRUCT_UTMPX_UT_TV_TV_SEC
# define UTMPX_UT_TIME HAVE_STRUCT_UTMPX_UT_TIME
# define UTMPX_UT_ADDR HAVE_STRUCT_UTMPX_UT_ADDR
# define UTMPX_UT_ADDR_V6 HAVE_STRUCT_UTMPX_UT_ADDR_V6
# define UTMPX_UT_HOST HAVE_STRUCT_UTMPX_UT_HOST
# define UTMPX_UT_SYSLEN HAVE_STRUCT_UTMPX_UT_SYSLEN
# define WTMPX WTMPX_FILE
# if HAVE_UPDWTMPX
# define UPDWTMPX updwtmpx
# endif
#else
# if HAVE_UTMP_H && HAVE_PUTUTLINE
# define USE_UTMPX 1
# define PUTUTXLINE pututline
# define GETUTXID getutid
# define SETUTXENT setutent
# define UTMPX utmp
# define UTMPX_UT_EXIT HAVE_STRUCT_UTMP_UT_EXIT
# define UTMPX_UT_EXIT_E_TERMINATION HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION
# define UTMPX_UT_EXIT___E_TERMINATION HAVE_STRUCT_UTMP_UT_EXIT___E_TERMINATION
# define UTMPX_UT_EXIT_UT_TERMINATION HAVE_STRUCT_UTMP_UT_EXIT_UT_TERMINATION
# define UTMPX_UT_PID HAVE_STRUCT_UTMP_UT_PID
# define UTMPX_UT_USER HAVE_STRUCT_UTMP_UT_USER
# define UTMPX_UT_TV HAVE_STRUCT_UTMP_UT_TV_TV_SEC
# define UTMPX_UT_TIME HAVE_STRUCT_UTMP_UT_TIME
# define UTMPX_UT_ADDR HAVE_STRUCT_UTMP_UT_ADDR
# define UTMPX_UT_ADDR_V6 HAVE_STRUCT_UTMP_UT_ADDR_V6
# define UTMPX_UT_HOST HAVE_STRUCT_UTMP_UT_HOST
# define UTMPX_UT_SYSLEN HAVE_STRUCT_UTMP_UT_SYSLEN
# define WTMPX WTMP_FILE
# if HAVE_UPDWTMP
# define UPDWTMPX updwtmp
# endif
# else
# define USE_UTMPX 0
# endif
#endif
/* GABA:
(class
(name unix_process)
......@@ -170,7 +97,7 @@ do_signal_process(struct lsh_process *s, int signal)
}
struct lsh_process *
static struct lsh_process *
make_unix_process(pid_t pid, int signal)
{
NEW(unix_process, self);
......@@ -186,13 +113,61 @@ make_unix_process(pid_t pid, int signal)
return &self->super;
}
static int
send_helper_request(int helper_fd, int ref,
enum pty_request_type type,
int fd)
{
struct pty_message msg;
int err;
memset(&msg, 0, sizeof(msg));
msg.header.type = type;
msg.header.ref = ref;
msg.header.length = 0;
msg.has_creds = 1;
msg.creds.uid = getuid();
msg.creds.gid = getgid();
msg.creds.pid = getpid();
msg.fd = fd;
err = pty_send_message(helper_fd, &msg);
if (err)
{
werror("Sending message to pty helper failed: %e.\n", err);
return -1;
}
err = pty_recv_message(helper_fd, &msg);
if (err != 0)
{
if (err < 0)
werror("End of file from pty helper failed.\n");
else
werror("Receiving message from pty helper failed: %e.\n", err);
return -1;
}
if (msg.fd >= 0)
close(msg.fd);
if (msg.header.type)
{
werror("Pty helper operation failed: %e.\n", msg.header.type);
return -1;
}
return msg.header.ref;
}
/* GABA:
(class
(name logout_notice)
(super exit_callback)
(vars
(process object resource)
(c object exit_callback)))
(c object exit_callback)
(helper_fd . int)
(helper_ref . int)))
*/
static void
......@@ -206,17 +181,28 @@ do_logout_notice(struct exit_callback *s,
/* No need to signal the process. */
self->process->alive = 0;
if (self->helper_fd != -1)
{
/* FIXME: Pass termination status? */
send_helper_request(self->helper_fd,
self->helper_ref,
PTY_REQUEST_LOGOUT,
-1);
}
EXIT_CALLBACK(self->c, signaled, core, value);
}
static struct exit_callback *
make_logout_notice(struct resource *process,
struct exit_callback *c)
struct exit_callback *c,
int helper_fd, int helper_ref)
{
NEW(logout_notice, self);
self->super.exit = do_logout_notice;
self->process = process;
self->c = c;
self->helper_fd = helper_fd;
self->helper_ref = helper_ref;
return &self->super;
}
......@@ -236,7 +222,7 @@ format_env_pair(const char *name, const char *value)
/* Helper functions for the process spawning. */
int
static int
exec_shell(struct spawn_info *info)
{
/* Environment consists of SHELL, other inherited values, caller's
......@@ -323,8 +309,8 @@ exec_shell(struct spawn_info *info)
}
static void
spawn_error(struct spawn_info *info,
const int *sync)
spawn_error(struct spawn_info *info, const int *sync,
int helper_fd, int helper_ref)
{
safe_close(sync[0]); safe_close(sync[1]);
......@@ -335,14 +321,17 @@ spawn_error(struct spawn_info *info,
* both /dev/null. */
if (info->err[1] != info->out[1])
safe_close(info->err[1]);
if (helper_fd != -1)
send_helper_request(helper_fd, helper_ref, PTY_REQUEST_DESTROY, -1);
}
/* Parent processing */
static struct lsh_process *
spawn_parent(struct spawn_info *info,
struct exit_callback *c,
pid_t child,
const int *sync)
pid_t child, const int *sync,
int helper_fd, int helper_ref)
{
/* Parent */
struct lsh_process *process;
......@@ -366,8 +355,8 @@ spawn_parent(struct spawn_info *info,
* tty, after which it should close its end of the
* syncronization pipe, and our read will return 0.
*
* We need the syncronization only if we're actually using a
* pty, but for simplicity, we do it every time. */
* It also helps for syncronization of the parent's and the child's
* requests to the pty helper process. */
do
res = read(sync[0], &dummy, 1);
......@@ -377,9 +366,9 @@ spawn_parent(struct spawn_info *info,
trace("spawn_parent: after sync\n");
/* FIXME: Doesn't do any utmp/wtmp logging */
process = make_unix_process(child, SIGHUP);
reaper_handle(child, make_logout_notice(&process->super, c));
reaper_handle(child, make_logout_notice(&process->super, c,
helper_fd, helper_ref));
trace("spawn_parent: parent after process setup\n");
......@@ -387,8 +376,8 @@ spawn_parent(struct spawn_info *info,
}
static void
spawn_child(struct spawn_info *info,
const int *sync)
spawn_child(struct spawn_info *info, const int *sync,
int helper_fd, int helper_ref)
{
int tty = -1;
......@@ -415,7 +404,10 @@ spawn_child(struct spawn_info *info,
#endif /* WITH_PTY_SUPPORT */
trace("spawn_child: after pty\n");
if (helper_fd != -1)
send_helper_request(helper_fd, helper_ref, PTY_REQUEST_LOGIN, -1);
/* Now any tty processing is done, so notify our parent by
* closing the syncronization pipe. */
......@@ -466,24 +458,36 @@ spawn_child(struct spawn_info *info,
}
struct lsh_process *
spawn_shell(struct spawn_info *info,
spawn_shell(struct spawn_info *info, int helper_fd,
struct exit_callback *c)
{
/* Pipe used for syncronization. */
int sync[2];
pid_t child;
int helper_ref = -1;
if (!lsh_make_pipe(sync))
{
werror("do_spawn: Failed to create syncronization pipe.\n");
return NULL;
}
if (helper_fd != -1)
{
helper_ref
= send_helper_request(helper_fd, -1,
PTY_REQUEST_CREATE,
info->pty ? info->pty->master : -1);
if (helper_ref < 0)
/* We can't use the helper. */
helper_fd = -1;
}
child = fork();
if (child < 0)
{
werror("spawn_shell: fork failed %e\n", errno);
spawn_error(info, sync);
spawn_error(info, sync, helper_fd, helper_ref);
return NULL;
}
......@@ -492,294 +496,13 @@ spawn_shell(struct spawn_info *info,
/* Parent */
trace("spawn_shell: parent process\n");
return spawn_parent(info, c, child, sync);
return spawn_parent(info, c, child, sync, helper_fd, helper_ref);
}
else
{ /* Child */
spawn_child(info, sync);
spawn_child(info, sync, helper_fd, helper_ref);
exec_shell(info);
_exit(EXIT_FAILURE);
}
}
/* At the moment, utmp and wtmp can't work at all, due to lack of privileges. */
#if 0
/* ;; GABA:
(class
(name utmp_cleanup)
(super exit_callback)
(vars
(id string)
(line string)
(c object exit_callback)))
*/
#if WITH_UTMP
/* Helper macros for assigning utmp fields */
#define CLEAR(dst) (memset(&(dst), 0, sizeof(dst)))
static void
lsh_strncpy(char *dst, unsigned n, struct lsh_string *s)
{
unsigned length = MIN(n, lsh_string_length(s));
memcpy(dst, lsh_string_data(s), length);
if (length < n)
dst[length] = '\0';
}
#define CP(dst, src) lsh_strncpy(dst, sizeof(dst), src)
static void
do_utmp_cleanup(struct exit_callback *s,
int signaled, int core, int value)
{
CAST(utmp_cleanup, self, s);
#if USE_UTMPX
struct UTMPX entry;
#ifndef HAVE_LOGWTMP
struct UTMPX wc;
struct UTMPX *old;
#endif
trace("unix_process.c: do_utmp_cleanup (HAVE_UTMPX_H) \n");
/* Rewind the database */
SETUTXENT();
/* Start by clearing the whole entry */
memset(&entry, 0, sizeof(entry));
/* FIXME: Are there systems without ut_id? Do we care? */
CP(entry.ut_id, self->id);
entry.ut_type = DEAD_PROCESS;
/* The memset has cleared all fields so we need only set those
* entries that shouldn't be 0 */
#if UTMPX_UT_EXIT
# if UTMPX_UT_EXIT_E_TERMINATION
entry.ut_exit.e_exit = signaled ? 0 : value;
entry.ut_exit.e_termination = signaled ? value : 0;
# elif UTMPX_UT_EXIT_UT_TERMINATION
/* Names use on tru64 */
entry.ut_exit.ut_exit = signaled ? 0 : value;
entry.ut_exit.ut_termination = signaled ? value : 0;
# elif UTMPX_UT_EXIT___E_TERMINATION
/* HPUX uses these odd names in struct utmpx */
entry.ut_exit.__e_exit = signaled ? 0 : value;
entry.ut_exit.__e_termination = signaled ? value : 0;
# elif __GNUC__
# warning utmp.ut_exit exists, but no known sub attributes
# endif
#endif
#ifndef HAVE_LOGWTMP
/* Mumble. For utmp{,x} we want as much as possible of the entry to
* be cleared, but for wtmp{,x} we want to retain as much
* information as possible, so we get the entry from utmp{,x}, put
* the cleared one to utmp{,x}. */
trace("unix_process.c: do_utmp_cleanup without logwtmp, looking up old entry\n");
old = GETUTXID( &entry );
if( !old ) /* getut{,x}id failed? */
{
debug("unix_process.c: do_utmp_cleanup getut{,x}id failed\n");
wc = entry; /* Copy the entry we're going to write and fill it in as much as we can */
CP(wc.ut_line, self->line);
}
else
wc = *old; /* Copy the old entry (on Solaris, putut{,x}line invalidates *old) */
#endif /* HAVE_LOGWTMP */
if (!PUTUTXLINE(&entry))
werror("Updating utmpx for logout failed %e\n", errno);
#ifndef HAVE_LOGWTMP
/* Calculate timestamp for wtmp */
# if UTMPX_UT_TV
gettimeofday(&wc.ut_tv, NULL); /* Ignore the timezone */
# else
# if UTMPX_UT_TIME
time(&wc.ut_time);
# endif /* UTMPX_UT_TIME */
# endif /* UTMPX_UT_TV */
old->ut_type = DEAD_PROCESS; /* Mark as dead */
#endif /* HAVE_LOGWTMP */
#if HAVE_LOGWTMP
logwtmp(lsh_get_cstring(self->line), "", "");
#else /* HAVE_LOGWTMP */
# ifdef UPDWTMPX
UPDWTMPX(WTMPX, &wc);
# endif
#endif /* HAVE_LOGWTMP */
#endif /* USE_UTMPX */
EXIT_CALLBACK(self->c, signaled, core, value);
}
static struct utmp_cleanup *
make_utmp_cleanup(struct lsh_string *tty,
struct exit_callback *c)
{
NEW(utmp_cleanup, self);
uint32_t length = lsh_string_length(tty);
const uint8_t *data = lsh_string_data(tty);
self->super.exit = do_utmp_cleanup;
self->c = c;
if (length > 5 && !memcmp(data, "/dev/", 5))
{
data +=5; length -= 5;
}
self->line = ssh_format("%ls", length, data);
/* Construct ut_id following the linux utmp(5) man page:
*
* line = "pts/17" => id = "p17",
* line = "ttyxy" => id = "xy" (usually, x = 'p')
*
* NOTE: This is different from what rxvt does on my system, it sets
* id = "vt11" if line = "pts/17".
*/
if (length > 4 && !memcmp(data, "pts/", 4))
{ data += 4; length -= 4; }
else if (length > 3 && !memcmp(data, "tty", 3))
{ data += 3; length -= 3; }
else
{/* If the patterns above don't match, we set ut_id empty */
length = 0;
}
self->id = ssh_format("%ls", length, data);
return self;
}
static struct exit_callback *
utmp_book_keeping(struct lsh_string *name,
pid_t pid,
struct address_info *peer,
struct lsh_string *tty,
struct exit_callback *c)
{
struct utmp_cleanup *cleanup = make_utmp_cleanup(tty, c);
#if USE_UTMPX
struct UTMPX entry;
memset(&entry, 0, sizeof(entry));
SETUTXENT(); /* Rewind the database */
trace("unix_process.c: utmp_book_keeping\n");
/* Do not look for an existing entry, but trust putut{,x}line to
* reuse old entries if appropiate */
entry.ut_type = USER_PROCESS;
CP(entry.ut_line, cleanup->line);
CP(entry.ut_id, cleanup->id);
#if UTMPX_UT_PID
entry.ut_pid = pid;
#endif
#if UTMPX_UT_USER
CP(entry.ut_user, name);
#endif
#if UTMPX_UT_TV
gettimeofday(&entry.ut_tv, NULL); /* Ignore the timezone */
#else
# if UTMPX_UT_TIME
time(&entry.ut_time);
# endif
#endif
trace("unix_process.c: utmp_book_keeping, after name (HAVE_UTMPX_H)\n");
/* FIXME: We should store real values here. */
#if UTMPX_UT_ADDR
CLEAR(entry.ut_addr);
#endif
#if UTMPX_UT_ADDR_V6
CLEAR(entry.ut_addr_v6);
#endif
/* FIXME: Perform a reverse lookup. */
#if UTMPX_UT_HOST
CP(entry.ut_host, peer->ip);
#if UTMPX_UT_SYSLEN
/* ut_syslen is the significant length of ut_host (including NUL),
* i.e. the lesser of the length of peer->ip+1 and available storage
*/
entry.ut_syslen = MIN(sizeof(entry.ut_host),
lsh_string_length(peer->ip) + 1);
#endif /* UTMPX_UT_SYSLEN */
#endif /* UTMPX_UT_HOST */
trace("unix_process.c: utmp_book_keeping, after host (HAVE_UTMPX_H)\n");
if (!PUTUTXLINE(&entry))
werror("Updating utmp for login failed %e\n", errno);
trace("unix_process.c: utmp_book_keeping, after pututline (HAVE_UTMPX_H)\n");
#if HAVE_LOGWTMP
logwtmp(lsh_get_cstring(cleanup->line),
lsh_get_cstring(name),
lsh_get_cstring(peer->ip));
#else /* HAVE_LOGWTMP */
# ifdef UPDWTMPX
UPDWTMPX(WTMPX, &entry);
# endif
#endif /* HAVE_LOGWTMP */
#endif /* USE_UTMPX */
trace("unix_process.c: utmp_book_keeping, after logwtmp\n");
return &cleanup->super;
}
#endif /* WITH_UTMP */
struct lsh_process *
unix_process_setup(pid_t pid,
struct lsh_user *user,
struct exit_callback **c,
struct address_info *peer,
struct lsh_string *tty)
{
struct lsh_process *process = make_unix_process(pid, SIGHUP);
trace("unix_process.c: unix_process_setup\n");
#if WITH_UTMP
if (tty)
*c = utmp_book_keeping(user->name, pid, peer, tty, *c);
#endif
trace("unix_process.c: unix_process_setup, after utmp\n");
*c = make_logout_notice(&process->super, *c);
return process;
}
#endif
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment