2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 09:57:41 +00:00

Add config file support for logsrvd

This commit is contained in:
Todd C. Miller 2019-10-24 20:04:31 -06:00
parent 240d589136
commit a808dd45c2
8 changed files with 349 additions and 19 deletions

View File

@ -50,6 +50,7 @@ doc/visudo.mdoc.in
examples/Makefile.in
examples/pam.conf
examples/sudo.conf
examples/sudo_logsrvd.conf
examples/sudoers
examples/syslog.conf
include/Makefile.in
@ -236,6 +237,7 @@ logsrvd/log_server.proto
logsrvd/iolog_writer.c
logsrvd/logsrvd.c
logsrvd/logsrvd.h
logsrvd/logsrvd_conf.c
logsrvd/protobuf-c/protobuf-c.c
logsrvd/protobuf-c/protobuf-c.h
logsrvd/sendlog.c

View File

@ -0,0 +1,49 @@
#
# sudo logsrv configuration
#
# The top-level directory to use when constructing the path name for the
# I/O log directory. The session sequence number, if any, is stored here.
iolog_dir = /var/log/sudo-io
# The path name, relative to iolog_dir, in which to store I/O logs.
# Note that iolog_file may contain directory components.
iolog_file = %{seq}
# If set, I/O log data is flushed to disk after each write instead of
# buffering it. This makes it possible to view the logs in real-time
# as the program is executing but may significantly reduce the effectiveness
# of I/O log compression.
iolog_flush = true
# If set, I/O logs will be compressed using zlib. Enabling compression can
# make it harder to view the logs in real-time as the program is executing.
iolog_compress = false
# The group name to look up when setting the group-ID on new I/O log files
# and directories. If iolog_group is not set, the primary group-ID of the
# user specified by iolog_user is used. If neither iolog_group nor iolog_user
# are set, I/O log files and directories are created with group-ID 0.
#iolog_group = wheel
# The user name to look up when setting the user and group-IDs on new I/O
# log files and directories. If iolog_group is set, it will be used instead
# of the user's primary group-ID. By default, I/O log files and directories
# are created with user and group-ID 0.
#iolog_user = root
# The file mode to use when creating I/O log files. Mode bits for read and
# write permissions for owner, group or other are honored, everything else
# is ignored. The file permissions will always include the owner read and
# write bits, even if they are not present in the specified mode. When
# creating I/O log directories, search (execute) bits are added to match
# the read and write bits specified by iolog_mode.
iolog_mode = 0600
# The maximum sequence number that will be substituted for the %{seq}
# escape in the I/O log file (see the iolog_dir description below for
# more information). While the value substituted for %{seq} is in
# base 36, maxseq itself should be expressed in decimal. Values larger
# than 2176782336 (which corresponds to the base 36 sequence number
# ZZZZZZ) will be silently truncated to 2176782336.
maxseq = 2176782336

View File

@ -108,8 +108,8 @@ SHELL = @SHELL@
PROGS = logsrvd sendlog
LOGSRVD_OBJS = iolog_util.o iolog_writer.o logsrvd.o log_server.pb-c.o \
protobuf-c.o
LOGSRVD_OBJS = iolog_util.o iolog_writer.o logsrvd.o logsrvd_conf.o \
log_server.pb-c.o protobuf-c.o
SENDLOG_OBJS = sendlog.o iolog_util.o log_server.pb-c.o protobuf-c.o
@ -272,6 +272,24 @@ logsrvd.i: $(srcdir)/logsrvd.c $(devdir)/log_server.pb-c.h \
$(CC) -E -o $@ $(CPPFLAGS) $<
logsrvd.plog: logsrvd.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/logsrvd.c --i-file $< --output-file $@
logsrvd_conf.o: $(srcdir)/logsrvd_conf.c $(devdir)/log_server.pb-c.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrvd.h \
$(srcdir)/protobuf-c/protobuf-c.h $(sudoers_srcdir)/iolog.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/logsrvd_conf.c
logsrvd_conf.i: $(srcdir)/logsrvd_conf.c $(devdir)/log_server.pb-c.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(srcdir)/logsrvd.h \
$(srcdir)/protobuf-c/protobuf-c.h $(sudoers_srcdir)/iolog.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(CC) -E -o $@ $(CPPFLAGS) $<
logsrvd_conf.plog: logsrvd_conf.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/logsrvd_conf.c --i-file $< --output-file $@
protobuf-c.o: $(srcdir)/protobuf-c/protobuf-c.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/protobuf-c/protobuf-c.c
protobuf-c.i: $(srcdir)/protobuf-c/protobuf-c.c

View File

@ -231,7 +231,8 @@ iolog_details_fill(struct iolog_details *details, ExecMessage *msg)
/*
* Create I/O log path
* Set iolog_dir and iolog_dir_fd in the closure
* Sets iolog_dir and iolog_dir_fd in the closure
* XXX - use iolog_dir and iolog_file code from sudoers/iolog.c
*/
static bool
create_iolog_dir(struct iolog_details *details, struct connection_closure *closure)

View File

@ -53,16 +53,23 @@
#include "pathnames.h"
#include "logsrvd.h"
/*
* Sudo I/O audit server.
*/
TAILQ_HEAD(connection_list, connection_closure);
static struct connection_list connections = TAILQ_HEAD_INITIALIZER(connections);
static const char server_id[] = "Sudo Audit Server 0.1";
static const char *conf_file = _PATH_SUDO_LOGSRVD_CONF;
static double random_drop;
/*
* Proof of concept audit server.
* Currently only handle a single connection at a time.
* TODO: use atomic I/O when we know the packed buffer size.
*/
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_STRUCT_IN6_ADDR
struct sockaddr_in6 sin6;
#endif
};
/*
* Free a struct connection_closure container and its contents.
@ -648,8 +655,8 @@ signal_cb(int signo, int what, void *v)
switch (signo) {
case SIGHUP:
/* TODO: reload config */
sudo_debug_printf(SUDO_DEBUG_INFO, "received SIGHUP");
logsrvd_conf_read(conf_file);
break;
case SIGINT:
case SIGTERM:
@ -903,7 +910,8 @@ daemonize(void)
static void
usage(void)
{
fprintf(stderr, "usage: %s [-n] [-R percentage]\n", getprogname());
fprintf(stderr, "usage: %s [-n] [-f conf_file] [-R percentage]\n",
getprogname());
exit(1);
}
@ -934,12 +942,16 @@ main(int argc, char *argv[])
sudo_fatalx("Protobuf-C version 1.3 or higher required");
/* XXX - getopt_long option handling */
while ((ch = getopt(argc, argv, "nR:")) != -1) {
while ((ch = getopt(argc, argv, "f:nR:")) != -1) {
switch (ch) {
case 'f':
conf_file = optarg;
break;
case 'n':
nofork = true;
break;
case 'R':
/* random connection drop probability as a percentage (debug) */
errno = 0;
random_drop = strtod(optarg, &ep);
if (*ep != '\0' || errno != 0)
@ -953,6 +965,9 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
/* Read sudo_logsrvd.conf */
logsrvd_conf_read(conf_file);
signal(SIGPIPE, SIG_IGN);
if (!nofork)
daemonize();

View File

@ -72,14 +72,6 @@ enum connection_status {
ERROR
};
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_STRUCT_IN6_ADDR
struct sockaddr_in6 sin6;
#endif
};
struct connection_buffer {
uint8_t *data; /* pre-allocated data buffer */
unsigned int size; /* currently always UINT16_MAX + 2 */
@ -114,3 +106,14 @@ int store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure);
int store_suspend(CommandSuspend *msg, struct connection_closure *closure);
int store_winsize(ChangeWindowSize *msg, struct connection_closure *closure);
void iolog_close(struct connection_closure *closure);
/* logsrvd_conf.c */
bool logsrvd_conf_iolog_compress(void);
bool logsrvd_conf_iolog_flush(void);
const char *logsrvd_conf_iolog_dir(void);
const char *logsrvd_conf_iolog_file(void);
const char *logsrvd_conf_iolog_group(void);
const char *logsrvd_conf_iolog_user(void);
mode_t logsrvd_conf_iolog_mode(void);
unsigned int logsrvd_conf_maxseq(void);
void logsrvd_conf_read(const char *path);

235
logsrvd/logsrvd_conf.c Normal file
View File

@ -0,0 +1,235 @@
/*
* Copyright (c) 2019 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/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "log_server.pb-c.h"
#include "sudo_gettext.h" /* must be included before sudo_compat.h */
#include "sudo_compat.h"
#include "sudo_debug.h"
#include "sudo_util.h"
#include "sudo_fatal.h"
#include "pathnames.h"
#include "logsrvd.h"
#include "iolog.h"
enum config_type {
CONF_BOOL,
CONF_INT,
CONF_UINT,
CONF_MODE,
CONF_STR
};
union config_value {
char *strval;
int intval;
unsigned int uintval;
mode_t modeval;
bool boolval;
};
struct logsrvd_config_table {
char *conf_str;
enum config_type conf_type;
union config_value conf_val;
};
/* Indexes into conf_table */
#define LOGSRVD_CONF_IOLOG_DIR 0
#define LOGSRVD_CONF_IOLOG_FILE 1
#define LOGSRVD_CONF_IOLOG_FLUSH 2
#define LOGSRVD_CONF_IOLOG_COMPRESS 3
#define LOGSRVD_CONF_IOLOG_USER 4
#define LOGSRVD_CONF_IOLOG_GROUP 5
#define LOGSRVD_CONF_IOLOG_MODE 6
#define LOGSRVD_CONF_MAXSEQ 7
/* XXX - use callbacks into iolog.c instead */
static struct logsrvd_config_table conf_table[] = {
{ "iolog_dir", CONF_STR, { .strval = _PATH_SUDO_IO_LOGDIR } },
{ "iolog_file", CONF_STR, { .strval = "%{seq}" } },
{ "iolog_flush", CONF_BOOL, { .boolval = true } },
{ "iolog_compress", CONF_BOOL, { .boolval = false } },
{ "iolog_user", CONF_STR, { .strval = NULL } },
{ "iolog_group", CONF_STR, { .strval = NULL } },
{ "iolog_mode", CONF_MODE, { .intval = S_IRUSR|S_IWUSR } },
{ "maxseq", CONF_UINT, { .intval = SESSID_MAX } },
{ NULL }
};
static bool
parse_value(struct logsrvd_config_table *ct, const char *val)
{
int ival;
unsigned int uval;
mode_t mode;
const char *errstr;
debug_decl(parse_value, SUDO_DEBUG_UTIL)
switch (ct->conf_type) {
case CONF_BOOL:
ival = sudo_strtobool(val);
if (ival == -1)
debug_return_bool(false);
ct->conf_val.boolval = ival;
break;
case CONF_INT:
ival = sudo_strtonum(val, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL)
debug_return_bool(false);
ct->conf_val.intval = ival;
break;
case CONF_UINT:
uval = sudo_strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
debug_return_bool(false);
ct->conf_val.uintval = uval;
break;
case CONF_MODE:
mode = sudo_strtomode(val, &errstr);
if (errstr != NULL)
debug_return_bool(false);
ct->conf_val.modeval = mode;
break;
case CONF_STR:
ct->conf_val.strval = strdup(val);
if (ct->conf_val.strval == NULL)
debug_return_bool(false);
break;
default:
debug_return_bool(false);
}
debug_return_bool(true);
}
void
logsrvd_conf_read(const char *path)
{
unsigned int lineno = 0;
size_t linesize = 0;
char *line = NULL;
FILE *fp;
debug_decl(read_config, SUDO_DEBUG_UTIL)
if ((fp = fopen(path, "r")) == NULL) {
if (errno != ENOENT)
sudo_warn("%s", path);
debug_return;
}
while (sudo_parseln(&line, &linesize, &lineno, fp, 0) != -1) {
struct logsrvd_config_table *ct;
char *ep, *val;
// XXX - warn about bogus lines
if ((ep = strchr(line, '=')) == NULL)
continue;
val = ep + 1;
while (isspace((unsigned char)*val))
val++;
while (ep > line && isspace((unsigned char)ep[-1]))
ep--;
*ep = '\0';
for (ct = conf_table; ct->conf_str != NULL; ct++) {
if (strcmp(line, ct->conf_str) == 0) {
if (!parse_value(ct, val))
sudo_warnx("invalid value for %s: %s", ct->conf_str, val);
break;
}
}
}
#if 0
/*
* TODO: iolog_dir, iolog_file, iolog_flush, iolog_compress
*/
iolog_set_user(conf_table[LOGSRVD_CONF_IOLOG_USER].conf_val.strval);
iolog_set_group(conf_table[LOGSRVD_CONF_IOLOG_GROUP].conf_val.strval);
iolog_set_mode(conf_table[LOGSRVD_CONF_IOLOG_MODE].conf_val.modeval);
/* XXX - expects a string */
iolog_set_max_sessid(conf_table[LOGSRVD_CONF_MAXSEQ].conf_val.uintval);
#endif
debug_return;
}
/* XXX - use callbacks instead */
const char *
logsrvd_conf_iolog_dir(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_DIR].conf_val.strval;
}
const char *
logsrvd_conf_iolog_file(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_FILE].conf_val.strval;
}
const char *
logsrvd_conf_iolog_user(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_USER].conf_val.strval;
}
const char *
logsrvd_conf_iolog_group(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_GROUP].conf_val.strval;
}
bool
logsrvd_conf_iolog_flush(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_FLUSH].conf_val.boolval;
}
bool
logsrvd_conf_iolog_compress(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_COMPRESS].conf_val.boolval;
}
mode_t
logsrvd_conf_iolog_mode(void)
{
return conf_table[LOGSRVD_CONF_IOLOG_MODE].conf_val.modeval;
}
unsigned int
logsrvd_conf_maxseq(void)
{
return conf_table[LOGSRVD_CONF_MAXSEQ].conf_val.uintval;
}

View File

@ -78,6 +78,13 @@
# define _PATH_CVTSUDOERS_CONF "/etc/cvtsudoers.conf"
#endif /* _PATH_CVTSUDOERS_CONF */
/*
* NOTE: _PATH_SUDO_LOGSRVD_CONF is usually overridden by the Makefile.
*/
#ifndef _PATH_SUDO_LOGSRVD_CONF
# define _PATH_SUDO_LOGSRVD_CONF "/etc/sudo_logsrvd.conf"
#endif /* _PATH_SUDO_LOGSRVD_CONF */
/*
* The following paths are controlled via the configure script.
*/