Add back io logging (transcript) support. Currently, the open function

runs too early and it is not possible to use the io module independently
of the policy module.
This commit is contained in:
Todd C. Miller
2010-03-27 20:19:40 -04:00
parent 681c6faf3e
commit 05e8023e78
5 changed files with 352 additions and 13 deletions

View File

@@ -44,8 +44,8 @@ INSTALL = $(SHELL) $(top_srcdir)/install-sh -c
# Libraries
LIBS = $(top_srcdir)/@ac_config_libobj_dir@/libreplace.la
NET_LIBS = @NET_LIBS@
SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS)
REPLAY_LIBS = @REPLAY_LIBS@
SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS) @ZLIB@
REPLAY_LIBS = @REPLAY_LIBS@ @ZLIB@
# C preprocessor flags
CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(srcdir) @CPPFLAGS@
@@ -101,9 +101,10 @@ LIBSUDOERS_OBJS = alias.lo alloc.lo atobool.lo defaults.lo gram.lo list.lo \
zero_bytes.lo @NONUNIX_GROUPS_IMPL@
SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo fmt_string.lo \
plugin_error.lo env.lo getspwuid.lo gettime.lo goodpath.lo \
fileops.lo find_path.lo interfaces.lo lbuf.lo logging.lo \
parse.lo set_perms.lo sudoers.lo sudo_nss.lo @SUDOERS_OBJS@
plugin_error.lo env.lo getspwuid.lo gettime.lo \
goodpath.lo fileops.lo find_path.lo interfaces.lo lbuf.lo \
logging.lo parse.lo set_perms.lo sudoers.lo sudo_nss.lo \
iolog.lo @SUDOERS_OBJS@
# XXX - need top level error.o; perhaps make #ifdef PIC?
VISUDO_OBJS = visudo.o fileops.o gettime.o goodpath.o find_path.o error.o
@@ -145,7 +146,7 @@ visudo: libsudoers.la $(VISUDO_OBJS)
$(LIBTOOL) --mode=link $(CC) -o $@ $(VISUDO_OBJS) $(LDFLAGS) libsudoers.la $(LIBS) $(NET_LIBS)
sudoreplay: libsudoers.la $(REPLAY_OBJS)
$(LIBTOOL) --mode=link $(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) $(REPLAY_LIBS) libsudoers.la $(LIBS) @ZLIB@
$(LIBTOOL) --mode=link $(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) $(REPLAY_LIBS) libsudoers.la $(LIBS)
testsudoers: $(TEST_OBJS)
$(LIBTOOL) --mode=link $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS) libsudoers.la $(LIBS) $(NET_LIBS)
@@ -205,6 +206,8 @@ gram.lo: $(devdir)/gram.c $(SUDODEP) $(srcdir)/parse.h $(incdir)/list.h $(devdir
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(devdir)/gram.c
interfaces.lo: $(srcdir)/interfaces.c $(SUDODEP) $(srcdir)/interfaces.h
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/interfaces.c
iolog.lo: $(srcdir)/iolog.c $(SUDODEP)
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/iolog.c
ldap.lo: $(srcdir)/ldap.c $(SUDODEP) $(srcdir)/parse.h $(incdir)/list.h
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/ldap.c
logging.lo: $(srcdir)/logging.c $(SUDODEP)

329
plugins/sudoers/iolog.c Normal file
View File

@@ -0,0 +1,329 @@
/*
* Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <pwd.h>
#include <grp.h>
#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
#include "sudoers.h"
union script_fd {
FILE *f;
#ifdef HAVE_ZLIB
gzFile g;
#endif
void *v;
};
struct script_buf {
int len; /* buffer length (how much read in) */
int off; /* write position (how much already consumed) */
char buf[16 * 1024];
};
static void
io_nextid()
{
struct stat sb;
char buf[32], *ep;
int fd, i, ch;
unsigned long id = 0;
int len;
ssize_t nread;
char pathbuf[PATH_MAX];
/*
* Create _PATH_SUDO_TRANSCRIPT if it doesn't already exist.
*/
if (stat(_PATH_SUDO_TRANSCRIPT, &sb) != 0) {
if (mkdir(_PATH_SUDO_TRANSCRIPT, S_IRWXU) != 0)
log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_TRANSCRIPT);
} else if (!S_ISDIR(sb.st_mode)) {
log_error(0, "%s exists but is not a directory (0%o)",
_PATH_SUDO_TRANSCRIPT, (unsigned int) sb.st_mode);
}
/*
* Open sequence file
*/
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_TRANSCRIPT);
if (len <= 0 || len >= sizeof(pathbuf)) {
errno = ENAMETOOLONG;
log_error(USE_ERRNO, "%s/seq", pathbuf);
}
fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
if (fd == -1)
log_error(USE_ERRNO, "cannot open %s", pathbuf);
lock_file(fd, SUDO_LOCK);
/* Read seq number (base 36). */
nread = read(fd, buf, sizeof(buf));
if (nread != 0) {
if (nread == -1)
log_error(USE_ERRNO, "cannot read %s", pathbuf);
id = strtoul(buf, &ep, 36);
if (buf == ep || id >= 2176782336U)
log_error(0, "invalid sequence number %s", pathbuf);
}
id++;
/*
* Convert id to a string and stash in sudo_user.sessid.
* Note that that least significant digits go at the end of the string.
*/
for (i = 5; i >= 0; i--) {
ch = id % 36;
id /= 36;
buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
}
buf[6] = '\n';
/* Stash id logging purposes */
memcpy(sudo_user.sessid, buf, 6);
sudo_user.sessid[6] = '\0';
/* Rewind and overwrite old seq file. */
if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
log_error(USE_ERRNO, "Can't write to %s", pathbuf);
close(fd);
}
static int
build_idpath(char *pathbuf, size_t pathsize)
{
struct stat sb;
int i, len;
if (sudo_user.sessid[0] == '\0')
log_error(0, "tried to build a session id path without a session id");
/*
* Path is of the form /var/log/sudo-session/00/00/01.
*/
len = snprintf(pathbuf, pathsize, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_TRANSCRIPT,
sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
if (len <= 0 && len >= pathsize) {
errno = ENAMETOOLONG;
log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_TRANSCRIPT, sudo_user.sessid);
}
/*
* Create the intermediate subdirs as needed.
*/
for (i = 6; i > 0; i -= 3) {
pathbuf[len - i] = '\0';
if (stat(pathbuf, &sb) != 0) {
if (mkdir(pathbuf, S_IRWXU) != 0)
log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
} else if (!S_ISDIR(sb.st_mode)) {
log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
}
pathbuf[len - i] = '/';
}
return(len);
}
/* XXX */
static sudo_conv_t io_conv;
static sigset_t ttyblock;
static struct timeval last_time;
static union script_fd io_outfile, io_timfile;
/* XXX - need to defer this until after the policy check succeeds */
int
sudoers_io_open(unsigned int version, sudo_conv_t conversation,
char * const settings[], char * const user_info[], char * const user_env[])
{
char pathbuf[PATH_MAX];
FILE *io_logfile;
int fd, len;
io_conv = conversation;
/* XXX - def_transcript may not be set yet */
if (!def_transcript)
return FALSE;
/*
* Build a path containing the session id split into two-digit subdirs,
* so ID 000001 becomes /var/log/sudo-session/00/00/01.
*/
io_nextid();
len = build_idpath(pathbuf, sizeof(pathbuf));
if (len == -1)
return -1;
/*
* We create 3 files: a log file, one for the raw session data,
* and one for the timing info.
*/
fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
if (fd == -1)
log_error(USE_ERRNO, "Can't create %s", pathbuf);
io_logfile = fdopen(fd, "w");
if (io_logfile == NULL)
log_error(USE_ERRNO, "fdopen");
strlcat(pathbuf, ".scr", sizeof(pathbuf));
fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
if (fd == -1)
log_error(USE_ERRNO, "Can't create %s", pathbuf);
#ifdef HAVE_ZLIB
if (def_compress_transcript)
io_outfile.g = gzdopen(fd, "w");
else
#endif
io_outfile.f = fdopen(fd, "w");
if (io_outfile.v == NULL)
log_error(USE_ERRNO, "Can't open %s", pathbuf);
pathbuf[len] = '\0';
strlcat(pathbuf, ".tim", sizeof(pathbuf));
fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
if (fd == -1)
log_error(USE_ERRNO, "Can't create %s", pathbuf);
#ifdef HAVE_ZLIB
if (def_compress_transcript)
io_timfile.g = gzdopen(fd, "w");
else
#endif
io_timfile.f = fdopen(fd, "w");
if (io_timfile.v == NULL)
log_error(USE_ERRNO, "Can't open %s", pathbuf);
/* So we can block tty-generated signals */
sigemptyset(&ttyblock);
sigaddset(&ttyblock, SIGINT);
sigaddset(&ttyblock, SIGQUIT);
sigaddset(&ttyblock, SIGTSTP);
sigaddset(&ttyblock, SIGTTIN);
sigaddset(&ttyblock, SIGTTOU);
gettimeofday(&last_time, NULL);
/* XXX - too early, don't even have user_cmnd yet */
/* XXX - log more stuff? window size? environment? */
fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", last_time.tv_sec, user_name,
runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
fprintf(io_logfile, "%s\n", user_cwd);
fprintf(io_logfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
user_args ? user_args : "");
fclose(io_logfile);
return TRUE;
}
void
sudoers_io_close(int exit_status, int error)
{
#ifdef HAVE_ZLIB
if (def_compress_transcript) {
gzclose(io_outfile.g);
gzclose(io_timfile.g);
} else
#endif
{
fclose(io_outfile.f);
fclose(io_timfile.f);
}
}
int
sudoers_io_version(int verbose)
{
struct sudo_conv_message msg;
struct sudo_conv_reply repl;
char *str;
easprintf(&str, "Sudoers I/O plugin version %s\n", PACKAGE_VERSION);
/* Call conversation function */
memset(&msg, 0, sizeof(msg));
msg.msg_type = SUDO_CONV_INFO_MSG;
msg.msg = str;
memset(&repl, 0, sizeof(repl));
io_conv(1, &msg, &repl);
return TRUE;
}
int
sudoers_io_log_output(const char *buf, unsigned int len)
{
struct timeval now, tv;
sigset_t omask;
gettimeofday(&now, NULL);
sigprocmask(SIG_BLOCK, &ttyblock, &omask);
#ifdef HAVE_ZLIB
if (def_compress_transcript)
gzwrite(io_outfile.g, buf, len);
else
#endif
fwrite(buf, 1, len, io_outfile.f);
timersub(&now, &last_time, &tv);
#ifdef HAVE_ZLIB
if (def_compress_transcript)
gzprintf(io_timfile.g, "%f %d\n",
tv.tv_sec + ((double)tv.tv_usec / 1000000), len);
else
#endif
fprintf(io_timfile.f, "%f %d\n",
tv.tv_sec + ((double)tv.tv_usec / 1000000), len);
last_time.tv_sec = now.tv_sec;
last_time.tv_usec = now.tv_usec;
sigprocmask(SIG_SETMASK, &omask, NULL);
return TRUE;
}

View File

@@ -60,7 +60,9 @@
/*
* Prototypes
*/
#if 0
static void runas_setup(void);
#endif
static void runas_setgroups(void);
static void restore_groups(void);

View File

@@ -1300,14 +1300,12 @@ struct policy_plugin sudoers_policy = {
sudoers_policy_invalidate
};
#ifdef notyet
struct io_plugin sudoers_io = {
SUDO_IO_PLUGIN,
SUDO_API_VERSION,
io_open,
io_close,
io_version,
io_log_input,
io_log_output
sudoers_io_open,
sudoers_io_close,
sudoers_io_version,
NULL,
sudoers_io_log_output
};
#endif

View File

@@ -297,6 +297,13 @@ YY_DECL;
/* atobool.c */
int atobool(const char *str);
/* iolog.c */
int sudoers_io_open(unsigned int version, sudo_conv_t conversation,
char * const settings[], char * const user_info[], char * const user_env[]);
void sudoers_io_close(int exit_status, int error);
int sudoers_io_version(int verbose);
int sudoers_io_log_output(const char *buf, unsigned int len);
/* Only provide extern declarations outside of sudo.c. */
#ifndef _SUDO_MAIN
extern struct sudo_user sudo_user;