diff --git a/MANIFEST b/MANIFEST index 14e921997..acf328c52 100644 --- a/MANIFEST +++ b/MANIFEST @@ -71,6 +71,7 @@ include/sudo_dso.h include/sudo_event.h include/sudo_fatal.h include/sudo_gettext.h +include/sudo_iolog.h include/sudo_lbuf.h include/sudo_plugin.h include/sudo_queue.h @@ -81,6 +82,13 @@ init.d/aix.sh.in init.d/hpux.sh.in init.d/sudo.conf.in install-sh +lib/iolog/Makefile.in +lib/iolog/iolog_fileio.c +lib/iolog/iolog_path.c +lib/iolog/iolog_util.c +lib/iolog/regress/iolog_path/check_iolog_path.c +lib/iolog/regress/iolog_path/data +lib/iolog/regress/iolog_util/check_iolog_util.c lib/util/Makefile.in lib/util/aix.c lib/util/arc4random.c @@ -331,11 +339,7 @@ plugins/sudoers/insults.h plugins/sudoers/interfaces.c plugins/sudoers/interfaces.h plugins/sudoers/iolog.c -plugins/sudoers/iolog.h -plugins/sudoers/iolog_files.h -plugins/sudoers/iolog_path.c -plugins/sudoers/iolog_util.c -plugins/sudoers/iolog_util.h +plugins/sudoers/iolog_path_escapes.c plugins/sudoers/ldap.c plugins/sudoers/ldap_conf.c plugins/sudoers/ldap_util.c @@ -503,10 +507,7 @@ plugins/sudoers/regress/cvtsudoers/test9.out.ok plugins/sudoers/regress/cvtsudoers/test9.sh plugins/sudoers/regress/env_match/check_env_pattern.c plugins/sudoers/regress/env_match/data -plugins/sudoers/regress/iolog_path/check_iolog_path.c -plugins/sudoers/regress/iolog_path/data plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c -plugins/sudoers/regress/iolog_util/check_iolog_util.c plugins/sudoers/regress/logging/check_wrap.c plugins/sudoers/regress/logging/check_wrap.in plugins/sudoers/regress/logging/check_wrap.out.ok diff --git a/Makefile.in b/Makefile.in index 0dc345bf6..bba8bb950 100644 --- a/Makefile.in +++ b/Makefile.in @@ -49,8 +49,8 @@ sudoers_gid = @SUDOERS_GID@ sudoers_mode = @SUDOERS_MODE@ shlib_mode = @SHLIB_MODE@ -SUBDIRS = lib/util @ZLIB_SRC@ logsrvd plugins/group_file plugins/sudoers \ - plugins/system_group src include doc examples +SUBDIRS = lib/util lib/iolog @ZLIB_SRC@ logsrvd plugins/group_file \ + plugins/sudoers plugins/system_group src include doc examples SAMPLES = plugins/sample @@ -188,12 +188,13 @@ siglist.c signame.c: depend: siglist.c signame.c $(top_srcdir)/mkdep.pl --builddir=`pwd` --srcdir=$(top_srcdir) \ - lib/util/Makefile.in lib/zlib/Makefile.in logsrvd/Makefile.in \ - plugins/group_file/Makefile.in plugins/sample/Makefile.in \ - plugins/sudoers/Makefile.in plugins/system_group/Makefile.in \ - src/Makefile.in && \ + lib/util/Makefile.in lib/zlib/Makefile.in lib/iolog/Makefile.in \ + logsrvd/Makefile.in plugins/group_file/Makefile.in \ + plugins/sample/Makefile.in plugins/sudoers/Makefile.in \ + plugins/system_group/Makefile.in src/Makefile.in && \ $(top_builddir)/config.status --file $(top_builddir)/lib/util/Makefile \ --file $(top_builddir)/lib/zlib/Makefile \ + --file $(top_builddir)/lib/iolog/Makefile \ --file $(top_builddir)/logsrvd/Makefile \ --file $(top_builddir)/plugins/sample/Makefile \ --file $(top_builddir)/plugins/group_file/Makefile \ diff --git a/configure b/configure index d72141b50..103af57d0 100755 --- a/configure +++ b/configure @@ -27067,7 +27067,7 @@ elif test X"$TMPFILES_D" != X""; then ac_config_files="$ac_config_files init.d/sudo.conf" fi -ac_config_files="$ac_config_files Makefile doc/Makefile examples/Makefile include/Makefile lib/util/Makefile lib/util/util.exp logsrvd/Makefile src/sudo_usage.h src/Makefile plugins/sample/Makefile plugins/group_file/Makefile plugins/system_group/Makefile plugins/sudoers/Makefile plugins/sudoers/sudoers" +ac_config_files="$ac_config_files Makefile doc/Makefile examples/Makefile include/Makefile lib/iolog/Makefile lib/util/Makefile lib/util/util.exp logsrvd/Makefile src/sudo_usage.h src/Makefile plugins/sample/Makefile plugins/group_file/Makefile plugins/system_group/Makefile plugins/sudoers/Makefile plugins/sudoers/sudoers" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -28065,6 +28065,7 @@ do "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;; "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; + "lib/iolog/Makefile") CONFIG_FILES="$CONFIG_FILES lib/iolog/Makefile" ;; "lib/util/Makefile") CONFIG_FILES="$CONFIG_FILES lib/util/Makefile" ;; "lib/util/util.exp") CONFIG_FILES="$CONFIG_FILES lib/util/util.exp" ;; "logsrvd/Makefile") CONFIG_FILES="$CONFIG_FILES logsrvd/Makefile" ;; diff --git a/configure.ac b/configure.ac index 7480fd20a..715be4c99 100644 --- a/configure.ac +++ b/configure.ac @@ -4491,7 +4491,7 @@ if test X"$INIT_SCRIPT" != X""; then elif test X"$TMPFILES_D" != X""; then AC_CONFIG_FILES([init.d/sudo.conf]) fi -AC_CONFIG_FILES([Makefile doc/Makefile examples/Makefile include/Makefile lib/util/Makefile lib/util/util.exp logsrvd/Makefile src/sudo_usage.h src/Makefile plugins/sample/Makefile plugins/group_file/Makefile plugins/system_group/Makefile plugins/sudoers/Makefile plugins/sudoers/sudoers]) +AC_CONFIG_FILES([Makefile doc/Makefile examples/Makefile include/Makefile lib/iolog/Makefile lib/util/Makefile lib/util/util.exp logsrvd/Makefile src/sudo_usage.h src/Makefile plugins/sample/Makefile plugins/group_file/Makefile plugins/system_group/Makefile plugins/sudoers/Makefile plugins/sudoers/sudoers]) AC_OUTPUT dnl diff --git a/include/sudo_iolog.h b/include/sudo_iolog.h new file mode 100644 index 000000000..3f7c50c0c --- /dev/null +++ b/include/sudo_iolog.h @@ -0,0 +1,135 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2019 Todd C. Miller + * + * 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. + */ + +#ifndef SUDO_IOLOG_H +#define SUDO_IOLOG_H + +#ifdef HAVE_ZLIB_H +# include /* for gzFile */ +#endif + +/* Default maximum session ID */ +#define SESSID_MAX 2176782336U + +/* + * I/O log event types as stored as the first field in the timing file. + * Changing existing values will result in incompatible I/O log files. + */ +#define IO_EVENT_STDIN 0 +#define IO_EVENT_STDOUT 1 +#define IO_EVENT_STDERR 2 +#define IO_EVENT_TTYIN 3 +#define IO_EVENT_TTYOUT 4 +#define IO_EVENT_WINSIZE 5 +#define IO_EVENT_TTYOUT_1_8_7 6 +#define IO_EVENT_SUSPEND 7 +#define IO_EVENT_COUNT 8 + +/* + * Indexes into iolog_files[] array. + * These must match the IO_EVENT_ defines above. + * TODO: eliminate use of IOFD_* and IO_EVENT_* as indexes in favor of + * a struct containing iolog_file *s for each (and names too?). + */ +#define IOFD_STDIN 0 +#define IOFD_STDOUT 1 +#define IOFD_STDERR 2 +#define IOFD_TTYIN 3 +#define IOFD_TTYOUT 4 +#define IOFD_TIMING 5 +#define IOFD_MAX 6 + +/* + * Info present in the I/O log file + */ +struct iolog_info { + char *cwd; + char *user; + char *runas_user; + char *runas_group; + char *tty; + char *cmd; + time_t tstamp; + int lines; + int cols; +}; + +struct timing_closure { + struct timespec delay; + const char *decimal; + struct iolog_file *iol; + int event; + union { + struct { + int lines; + int cols; + } winsize; + size_t nbytes; + int signo; + } u; +}; + +struct iolog_file { + bool enabled; + bool compressed; + union { + FILE *f; +#ifdef HAVE_ZLIB_H + gzFile g; +#endif + void *v; + } fd; +}; + +struct iolog_path_escape { + const char *name; + size_t (*copy_fn)(char *, size_t, char *); +}; + +/* iolog_path.c */ +char *expand_iolog_path(const char *prefix, const char *dir, const char *file, char **slashp, const struct iolog_path_escape *escapes); + +/* iolog_util.c */ +bool parse_timing(const char *line, struct timing_closure *timing); +char *parse_delay(const char *cp, struct timespec *delay, const char *decimal_point); +struct iolog_info *parse_logfile(const char *logfile); +void free_iolog_info(struct iolog_info *li); +void adjust_delay(struct timespec *delay, struct timespec *max_delay, double scale_factor); + +/* iolog_fileio.c */ +struct passwd; +struct group; +bool iolog_close(struct iolog_file *iol, const char **errstr); +bool iolog_eof(struct iolog_file *iol); +bool iolog_nextid(char *iolog_dir, char sessid[7]); +bool iolog_open(struct iolog_file *iol, char *pathbuf, const char *mode); +bool iolog_set_compress(const char *str); +bool iolog_set_flush(const char *str); +bool iolog_set_group(const struct group *gr); +bool iolog_set_maxseq(const char *maxval); +bool iolog_set_mode(mode_t mode); +bool iolog_set_user(const struct passwd *pw); +bool iolog_write_info_file(const char *parent, struct iolog_info *log_info, char * const argv[]); +char *iolog_gets(struct iolog_file *iol, char *buf, size_t nbytes, const char **errsttr); +const char *iolog_fd_to_name(int iofd); +off_t iolog_seek(struct iolog_file *iol, off_t offset, int whence); +size_t mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize); +ssize_t iolog_read(struct iolog_file *iol, void *buf, size_t nbytes, const char **errstr); +ssize_t iolog_write(struct iolog_file *iol, const void *buf, size_t len, const char **errstr); + +#endif /* SUDO_IOLOG_H */ diff --git a/include/sudo_util.h b/include/sudo_util.h index 5cb967052..4434c61c2 100644 --- a/include/sudo_util.h +++ b/include/sudo_util.h @@ -25,6 +25,13 @@ # include "compat/stdbool.h" #endif /* HAVE_STDBOOL_H */ +#ifdef __TANDEM +# define ROOT_UID 65535 +#else +# define ROOT_UID 0 +#endif +#define ROOT_GID 0 + #ifndef TIME_T_MAX # if SIZEOF_TIME_T == 8 # define TIME_T_MAX LLONG_MAX diff --git a/lib/iolog/Makefile.in b/lib/iolog/Makefile.in new file mode 100644 index 000000000..a5deb68dc --- /dev/null +++ b/lib/iolog/Makefile.in @@ -0,0 +1,256 @@ +# +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2011-2019 Todd C. Miller +# +# 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. +# +# @configure_input@ +# + +#### Start of system configuration section. #### + +srcdir = @srcdir@ +devdir = @devdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +incdir = $(top_srcdir)/include +cross_compiling = @CROSS_COMPILING@ + +# Where to install things... +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +sbindir = @sbindir@ +sysconfdir = @sysconfdir@ +libexecdir = @libexecdir@ +datarootdir = @datarootdir@ +localstatedir = @localstatedir@ + +# Compiler & tools to use +CC = @CC@ +LIBTOOL = @LIBTOOL@ +SED = @SED@ +AWK = @AWK@ + +# Libraries +LT_LIBS = $(top_builddir)/lib/util/libsudo_util.la +LIBS = @LIBS@ @ZLIB@ $(LT_LIBS) + +# C preprocessor flags +CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(srcdir) -I$(top_srcdir) @CPPFLAGS@ + +# Usually -O and/or -g +CFLAGS = @CFLAGS@ + +# Flags to pass to the link stage +LDFLAGS = @LDFLAGS@ + +# Flags to pass to libtool +LTFLAGS = @LT_STATIC@ + +# Address sanitizer flags +ASAN_CFLAGS = @ASAN_CFLAGS@ +ASAN_LDFLAGS = @ASAN_LDFLAGS@ + +# PIE flags +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ + +# Stack smashing protection flags +SSP_CFLAGS = @SSP_CFLAGS@ +SSP_LDFLAGS = @SSP_LDFLAGS@ + +# cppcheck options, usually set in the top-level Makefile +CPPCHECK_OPTS = -q --force --enable=warning,performance,portability --suppress=constStatement --error-exitcode=1 --inline-suppr -Dva_copy=va_copy -U__cplusplus -UQUAD_MAX -UQUAD_MIN -UUQUAD_MAX -U_POSIX_HOST_NAME_MAX -U_POSIX_PATH_MAX -U__NBBY -DNSIG=64 + +# splint options, usually set in the top-level Makefile +SPLINT_OPTS = -D__restrict= -checks + +# PVS-studio options +PVS_CFG = $(top_srcdir)/PVS-Studio.cfg +PVS_IGNORE = 'V707,V011,V002,V536' +PVS_LOG_OPTS = -a 'GA:1,2' -e -t errorfile -d $(PVS_IGNORE) + +# Regression tests +TEST_PROGS = check_iolog_path check_iolog_util +TEST_LIBS = @LIBS@ +TEST_LDFLAGS = @LDFLAGS@ + +# Set to non-empty for development mode +DEVEL = @DEVEL@ + +#### End of system configuration section. #### + +SHELL = @SHELL@ + +LIBIOLOG_OBJS = iolog_fileio.lo iolog_path.lo iolog_util.lo + +IOBJS = $(LIBIOLOG_OBJS:.lo=.i) + +POBJS = $(IOBJS:.i=.plog) + +CHECK_IOLOG_PATH_OBJS = check_iolog_path.lo iolog_path.lo + +CHECK_IOLOG_UTIL_OBJS = check_iolog_util.lo iolog_util.lo + +all: libsudo_iolog.la + +pvs-log-files: $(POBJS) + +pvs-studio: $(POBJS) + plog-converter $(PVS_LOG_OPTS) $(POBJS) + +Makefile: $(srcdir)/Makefile.in + cd $(top_builddir) && ./config.status --file lib/iolog/Makefile + +.SUFFIXES: .c .h .i .lo .plog + +.c.lo: + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $< + +.c.i: + $(CC) -E -o $@ $(CPPFLAGS) $< + +.i.plog: + ifile=$<; rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $${ifile%i}c --i-file $< --output-file $@ + +libsudo_iolog.la: $(LIBIOLOG_OBJS) + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LIBIOLOG_OBJS) $(LT_LIBS) @ZLIB@ + +check_iolog_path: $(CHECK_IOLOG_PATH_OBJS) libsudo_iolog.la + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PATH_OBJS) libsudo_iolog.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) + +check_iolog_util: $(CHECK_IOLOG_UTIL_OBJS) libsudo_iolog.la + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_UTIL_OBJS) libsudo_iolog.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) + +pre-install: + +install: + +install-binaries: + +install-includes: + +install-doc: + +install-plugin: + +uninstall: + +splint: + splint $(SPLINT_OPTS) -I$(incdir) -I$(top_builddir) -I$(top_srcdir) $(srcdir)/*.c + +cppcheck: + cppcheck $(CPPCHECK_OPTS) -I$(incdir) -I$(top_builddir) -I$(top_srcdir) $(srcdir)/*.c + +pvs-log-files: $(POBJS) + +check: $(TEST_PROGS) + @if test X"$(cross_compiling)" != X"yes"; then \ + LC_ALL=C; export LC_ALL; \ + unset LANG || LANG=; \ + rval=0; \ + ./check_iolog_path $(srcdir)/regress/iolog_path/data || rval=`expr $$rval + $$?`; \ + ./check_iolog_util || rval=`expr $$rval + $$?`; \ + exit $$rval; \ + fi + +clean: + -$(LIBTOOL) $(LTFLAGS) --mode=clean rm -f $(TEST_PROGS) *.lo *.o \ + *.la *.a *.i *.plog stamp-* core *.core core.* regress/*/*.out \ + regress/*/*.err + +mostlyclean: clean + +distclean: clean + -rm -rf Makefile .libs + +clobber: distclean + +realclean: distclean + rm -f TAGS tags + +cleandir: realclean + +# Autogenerated dependencies, do not modify +check_iolog_path.lo: $(srcdir)/regress/iolog_path/check_iolog_path.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_path/check_iolog_path.c +check_iolog_path.i: $(srcdir)/regress/iolog_path/check_iolog_path.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +check_iolog_path.plog: check_iolog_path.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_path/check_iolog_path.c --i-file $< --output-file $@ +check_iolog_util.lo: $(srcdir)/regress/iolog_util/check_iolog_util.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_util/check_iolog_util.c +check_iolog_util.i: $(srcdir)/regress/iolog_util/check_iolog_util.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +check_iolog_util.plog: check_iolog_util.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_util/check_iolog_util.c --i-file $< --output-file $@ +iolog_fileio.lo: $(srcdir)/iolog_fileio.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_fileio.c +iolog_fileio.i: $(srcdir)/iolog_fileio.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_fileio.plog: iolog_fileio.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_fileio.c --i-file $< --output-file $@ +iolog_path.lo: $(srcdir)/iolog_path.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_path.c +iolog_path.i: $(srcdir)/iolog_path.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_path.plog: iolog_path.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_path.c --i-file $< --output-file $@ +iolog_util.lo: $(srcdir)/iolog_util.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_util.c +iolog_util.i: $(srcdir)/iolog_util.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_util.plog: iolog_util.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_util.c --i-file $< --output-file $@ diff --git a/lib/iolog/iolog_fileio.c b/lib/iolog/iolog_fileio.c new file mode 100644 index 000000000..6ce5b8d0d --- /dev/null +++ b/lib/iolog/iolog_fileio.c @@ -0,0 +1,897 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2019 Todd C. Miller + * + * 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. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include + +#include +#include +#include +#include +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#include +#include +#include +#include +#include +#include + +#include "sudo_gettext.h" /* must be included before sudo_compat.h */ +#include "sudo_compat.h" +#include "sudo_conf.h" +#include "sudo_debug.h" +#include "sudo_event.h" +#include "sudo_queue.h" +#include "sudo_util.h" +#include "sudo_fatal.h" +#include "sudo_iolog.h" +#include "pathnames.h" + +static unsigned char const gzip_magic[2] = {0x1f, 0x8b}; +static unsigned int sessid_max = SESSID_MAX; +static mode_t iolog_filemode = S_IRUSR|S_IWUSR; +static mode_t iolog_dirmode = S_IRWXU; +static uid_t iolog_uid = ROOT_UID; +static gid_t iolog_gid = ROOT_GID; +static bool iolog_gid_set; +static bool iolog_compress; +static bool iolog_flush; + +/* + * Set effective user and group-IDs to iolog_uid and iolog_gid. + * If restore flag is set, swap them back. + */ +static bool +io_swapids(bool restore) +{ +#ifdef HAVE_SETEUID + static uid_t user_euid = (uid_t)-1; + static gid_t user_egid = (gid_t)-1; + debug_decl(io_swapids, SUDO_DEBUG_UTIL) + + if (user_euid == (uid_t)-1) + user_euid = geteuid(); + if (user_egid == (gid_t)-1) + user_euid = getegid(); + + if (restore) { + if (seteuid(user_euid) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to restore effective uid to %d", __func__, + (int)user_euid); + sudo_warn("seteuid() %d -> %d", (int)iolog_uid, (int)user_euid); + debug_return_bool(false); + } + if (setegid(user_egid) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to restore effective gid to %d", __func__, + (int)user_egid); + sudo_warn("setegid() %d -> %d", (int)iolog_gid, (int)user_egid); + debug_return_bool(false); + } + } else { + /* Fail silently if the user has insufficient privileges. */ + if (setegid(iolog_gid) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to set effective gid to %d", __func__, + (int)iolog_gid); + debug_return_bool(false); + } + if (seteuid(iolog_uid) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to set effective uid to %d", __func__, + (int)iolog_uid); + debug_return_bool(false); + } + } + debug_return_bool(true); +#else + return false; +#endif +} + +/* + * Create directory and any parent directories as needed. + */ +static bool +io_mkdirs(char *path) +{ + mode_t omask; + struct stat sb; + bool ok, uid_changed = false; + debug_decl(io_mkdirs, SUDO_DEBUG_UTIL) + + /* umask must not be more restrictive than the file modes. */ + omask = umask(ACCESSPERMS & ~(iolog_filemode|iolog_dirmode)); + + ok = stat(path, &sb) == 0; + if (!ok && errno == EACCES) { + /* Try again as the I/O log owner (for NFS). */ + if (io_swapids(false)) { + ok = stat(path, &sb) == 0; + if (!io_swapids(true)) + ok = false; + } + } + if (ok) { + if (S_ISDIR(sb.st_mode)) { + if (sb.st_uid != iolog_uid || sb.st_gid != iolog_gid) { + if (chown(path, iolog_uid, iolog_gid) != 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to chown %d:%d %s", __func__, + (int)iolog_uid, (int)iolog_gid, path); + } + } + if ((sb.st_mode & ALLPERMS) != iolog_dirmode) { + if (chmod(path, iolog_dirmode) != 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to chmod 0%o %s", __func__, + (int)iolog_dirmode, path); + } + } + } else { + sudo_warnx(U_("%s exists but is not a directory (0%o)"), + path, (unsigned int) sb.st_mode); + ok = false; + } + goto done; + } + + ok = sudo_mkdir_parents(path, iolog_uid, iolog_gid, iolog_dirmode, true); + if (!ok && errno == EACCES) { + /* Try again as the I/O log owner (for NFS). */ + uid_changed = io_swapids(false); + if (uid_changed) + ok = sudo_mkdir_parents(path, -1, -1, iolog_dirmode, false); + } + if (ok) { + /* Create final path component. */ + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "mkdir %s, mode 0%o", path, (unsigned int) iolog_dirmode); + ok = mkdir(path, iolog_dirmode) == 0 || errno == EEXIST; + if (!ok) { + if (errno == EACCES && !uid_changed) { + /* Try again as the I/O log owner (for NFS). */ + uid_changed = io_swapids(false); + if (uid_changed) + ok = mkdir(path, iolog_dirmode) == 0 || errno == EEXIST; + } + if (!ok) + sudo_warn(U_("unable to mkdir %s"), path); + } else { + if (chown(path, iolog_uid, iolog_gid) != 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to chown %d:%d %s", __func__, + (int)iolog_uid, (int)iolog_gid, path); + } + } + } + if (uid_changed) { + if (!io_swapids(true)) + ok = false; + } +done: + umask(omask); + debug_return_bool(ok); +} + +/* + * Create temporary directory and any parent directories as needed. + */ +static bool +io_mkdtemp(char *path) +{ + bool ok, uid_changed = false; + debug_decl(io_mkdtemp, SUDO_DEBUG_UTIL) + + ok = sudo_mkdir_parents(path, iolog_uid, iolog_gid, iolog_dirmode, true); + if (!ok && errno == EACCES) { + /* Try again as the I/O log owner (for NFS). */ + uid_changed = io_swapids(false); + if (uid_changed) + ok = sudo_mkdir_parents(path, -1, -1, iolog_dirmode, false); + } + if (ok) { + /* Create final path component. */ + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + "mkdtemp %s", path); + /* We cannot retry mkdtemp() so always open as iolog user */ + if (!uid_changed) + uid_changed = io_swapids(false); + if (mkdtemp(path) == NULL) { + sudo_warn(U_("unable to mkdir %s"), path); + ok = false; + } else { + if (chmod(path, iolog_dirmode) != 0) { + sudo_warn(U_("unable to change mode of %s to 0%o"), + path, (unsigned int)iolog_dirmode); + } + } + } + + if (uid_changed) { + if (!io_swapids(true)) + ok = false; + } + debug_return_bool(ok); +} + +/* + * Set max sequence number (aka session ID) + */ +bool +iolog_set_maxseq(const char *maxval) +{ + const char *errstr; + unsigned int value; + debug_decl(iolog_set_maxseq, SUDO_DEBUG_UTIL) + + value = sudo_strtonum(maxval, 0, SESSID_MAX, &errstr); + if (errstr != NULL) { + if (errno != ERANGE) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "bad maxseq: %s: %s", maxval, errstr); + debug_return_bool(false); + } + /* Out of range, clamp to SESSID_MAX as documented. */ + value = SESSID_MAX; + } + sessid_max = value; + debug_return_bool(true); +} + +/* + * Set iolog_uid (and iolog_gid if iolog_group not specified). + */ +bool +iolog_set_user(const struct passwd *pw) +{ + debug_decl(iolog_set_user, SUDO_DEBUG_UTIL) + + if (pw != NULL) { + iolog_uid = pw->pw_uid; + if (!iolog_gid_set) + iolog_gid = pw->pw_gid; + } else { + /* Reset to default. */ + iolog_uid = ROOT_UID; + if (!iolog_gid_set) + iolog_gid = ROOT_GID; + } + + debug_return_bool(true); +} + +/* + * Set iolog_gid. + */ +bool +iolog_set_group(const struct group *gr) +{ + debug_decl(iolog_set_group, SUDO_DEBUG_UTIL) + + if (gr != NULL) { + iolog_gid = gr->gr_gid; + iolog_gid_set = true; + } else { + /* Reset to default. */ + iolog_gid = ROOT_GID; + iolog_gid_set = false; + } + + debug_return_bool(true); +} + +/* + * Set iolog_filemode and iolog_dirmode. + */ +bool +iolog_set_mode(mode_t mode) +{ + debug_decl(iolog_set_mode, SUDO_DEBUG_UTIL) + + /* I/O log files must be readable and writable by owner. */ + iolog_filemode = S_IRUSR|S_IWUSR; + + /* Add in group and other read/write if specified. */ + iolog_filemode |= mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + + /* For directory mode, add execute bits as needed. */ + iolog_dirmode = iolog_filemode | S_IXUSR; + if (iolog_dirmode & (S_IRGRP|S_IWGRP)) + iolog_dirmode |= S_IXGRP; + if (iolog_dirmode & (S_IROTH|S_IWOTH)) + iolog_dirmode |= S_IXOTH; + + debug_return_bool(true); +} + +/* + * Set iolog_compress + */ +bool +iolog_set_compress(const char *str) +{ + int result; + debug_decl(iolog_set_compress, SUDO_DEBUG_UTIL) + + if ((result = sudo_strtobool(str)) == -1) + debug_return_bool(false); + + iolog_compress = result; + debug_return_bool(true); +} + +/* + * Set iolog_flush + */ +bool +iolog_set_flush(const char *str) +{ + int result; + debug_decl(iolog_set_flush, SUDO_DEBUG_UTIL) + + if ((result = sudo_strtobool(str)) == -1) + debug_return_bool(false); + + iolog_flush = result; + debug_return_bool(true); +} + +/* + * Wrapper for open(2) that sets umask and retries as iolog_uid/iolog_gid + * if open(2) returns EACCES. + */ +static int +io_open(const char *path, int flags) +{ + int fd; + mode_t omask = S_IRWXG|S_IRWXO; + debug_decl(io_open, SUDO_DEBUG_UTIL) + + if (ISSET(flags, O_CREAT)) { + /* umask must not be more restrictive than the file modes. */ + omask = umask(ACCESSPERMS & ~(iolog_filemode|iolog_dirmode)); + } + fd = open(path, flags, iolog_filemode); + if (fd == -1 && errno == EACCES) { + /* Try again as the I/O log owner (for NFS). */ + if (io_swapids(false)) { + fd = open(path, flags, iolog_filemode); + if (!io_swapids(true)) { + /* io_swapids() warns on error. */ + if (fd != -1) { + close(fd); + fd = -1; + } + } + } + } + if (ISSET(flags, O_CREAT)) + umask(omask); + debug_return_int(fd); +} + +/* + * Read the on-disk sequence number, set sessid to the next + * number, and update the on-disk copy. + * Uses file locking to avoid sequence number collisions. + */ +bool +iolog_nextid(char *iolog_dir, char sessid[7]) +{ + char buf[32], *ep; + int i, len, fd = -1; + unsigned long id = 0; + ssize_t nread; + bool ret = false; + char pathbuf[PATH_MAX]; + static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + debug_decl(iolog_nextid, SUDO_DEBUG_UTIL) + + /* + * Create I/O log directory if it doesn't already exist. + */ + if (!io_mkdirs(iolog_dir)) + goto done; + + /* + * Open sequence file + */ + len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir); + if (len < 0 || len >= ssizeof(pathbuf)) { + errno = ENAMETOOLONG; + goto done; + } + fd = io_open(pathbuf, O_RDWR|O_CREAT); + if (fd == -1) { + goto done; + } + sudo_lock_file(fd, SUDO_LOCK); + if (fchown(fd, iolog_uid, iolog_gid) != 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to fchown %d:%d %s", __func__, + (int)iolog_uid, (int)iolog_gid, pathbuf); + } + + /* Read current seq number (base 36). */ + if (id == 0) { + nread = read(fd, buf, sizeof(buf) - 1); + if (nread != 0) { + if (nread == -1) { + goto done; + } + if (buf[nread - 1] == '\n') + nread--; + buf[nread] = '\0'; + id = strtoul(buf, &ep, 36); + if (ep == buf || *ep != '\0' || id >= sessid_max) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s: bad sequence number: %s", pathbuf, buf); + id = 0; + } + } + } + id++; + + /* + * Convert id to a string and stash in sessid. + * Note that that least significant digits go at the end of the string. + */ + for (i = 5; i >= 0; i--) { + buf[i] = b36char[id % 36]; + id /= 36; + } + buf[6] = '\n'; + + /* Stash id for logging purposes. */ + memcpy(sessid, buf, 6); + sessid[6] = '\0'; + + /* Rewind and overwrite old seq file, including the NUL byte. */ +#ifdef HAVE_PWRITE + if (pwrite(fd, buf, 7, 0) != 7) { +#else + if (lseek(fd, 0, SEEK_SET) == -1 || write(fd, buf, 7) != 7) { +#endif + goto done; + } + ret = true; + +done: + if (fd != -1) + close(fd); + debug_return_bool(ret); +} + +/* + * Copy iolog_path to pathbuf and create the directory and any intermediate + * directories. If iolog_path ends in 'XXXXXX', use mkdtemp(). + * Returns SIZE_MAX on error. + */ +size_t +mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize) +{ + size_t len; + bool ok; + debug_decl(mkdir_iopath, SUDO_DEBUG_UTIL) + + len = strlcpy(pathbuf, iolog_path, pathsize); + if (len >= pathsize) { + errno = ENAMETOOLONG; + debug_return_size_t((size_t)-1); + } + + /* + * Create path and intermediate subdirs as needed. + * If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp(). + * Sets iolog_gid (if it is not already set) as a side effect. + */ + if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0) + ok = io_mkdtemp(pathbuf); + else + ok = io_mkdirs(pathbuf); + + debug_return_size_t(ok ? len : (size_t)-1); +} + +/* + * Append suffix to pathbuf after len chars and open the resulting file. + * Note that the size of pathbuf is assumed to be PATH_MAX. + * Stores the open file handle which has the close-on-exec flag set. + * XXX - move enabled logic into caller? + */ +bool +iolog_open(struct iolog_file *iol, char *path, const char *mode) +{ + int flags; + unsigned char magic[2]; + debug_decl(iolog_open, SUDO_DEBUG_UTIL) + + if (mode[0] == 'r') { + flags = mode[1] == '+' ? O_RDWR : O_RDONLY; + } else if (mode[0] == 'w') { + flags = O_CREAT|O_TRUNC; + flags |= mode[1] == '+' ? O_RDWR : O_WRONLY; + } else { + sudo_debug_printf(SUDO_DEBUG_ERROR, + "%s: invalid I/O mode %s", __func__, mode); + debug_return_bool(false); + } + + iol->compressed = false; + if (iol->enabled) { + int fd = io_open(path, flags); + if (fd != -1) { + if (*mode == 'w') { + if (fchown(fd, iolog_uid, iolog_gid) != 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to fchown %d:%d %s", __func__, + (int)iolog_uid, (int)iolog_gid, path); + } + iol->compressed = iolog_compress; + } else { + /* check for gzip magic number */ + if (read(fd, magic, sizeof(magic)) == ssizeof(magic)) { + if (magic[0] == gzip_magic[0] && magic[1] == gzip_magic[1]) + iol->compressed = true; + } + (void)lseek(fd, 0, SEEK_SET); + } + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); +#ifdef HAVE_ZLIB_H + if (iol->compressed) + iol->fd.g = gzdopen(fd, mode); + else +#endif + iol->fd.f = fdopen(fd, mode); + if (iol->fd.v == NULL) { + int save_errno = errno; + close(fd); + errno = save_errno; + fd = -1; + } + } + if (fd == -1) { + iol->enabled = false; + debug_return_bool(false); + } + } else { + if (*mode == 'w') { + /* Remove old log file in case we recycled sequence numbers. */ + (void)unlink(path); + } + } + debug_return_bool(true); +} + +#ifdef HAVE_ZLIB_H +static const char * +gzstrerror(gzFile file) +{ + int errnum; + + return gzerror(file, &errnum); +} +#endif /* HAVE_ZLIB_H */ + +/* + * Close an I/O log. + */ +bool +iolog_close(struct iolog_file *iol, const char **errstr) +{ + bool ret = true; + debug_decl(iolog_close, SUDO_DEBUG_UTIL) + +#ifdef HAVE_ZLIB_H + if (iol->compressed) { + if (gzclose(iol->fd.g) != Z_OK) { + ret = false; + if (errstr != NULL) + *errstr = gzstrerror(iol->fd.g); + } + } else +#endif + if (fclose(iol->fd.f) != 0) { + ret = false; + if (errstr != NULL) + *errstr = strerror(errno); + } + + debug_return_bool(ret); +} + +/* + * I/O log wrapper for fseek/gzseek. + */ +off_t +iolog_seek(struct iolog_file *iol, off_t offset, int whence) +{ + off_t ret; + //debug_decl(iolog_seek, SUDO_DEBUG_UTIL) + +#ifdef HAVE_ZLIB_H + if (iol->compressed) + ret = gzseek(iol->fd.g, offset, whence); + else +#endif +#ifdef HAVE_FSEEKO + ret = fseeko(iol->fd.f, offset, whence); +#else + ret = fseek(iol->fd.f, offset, whence); +#endif + + //debug_return_off_t(ret); + return ret; +} + +/* + * Read from a (possibly compressed) I/O log file. + */ +ssize_t +iolog_read(struct iolog_file *iol, void *buf, size_t nbytes, + const char **errstr) +{ + ssize_t nread; + debug_decl(iolog_read, SUDO_DEBUG_UTIL) + + if (nbytes > UINT_MAX) { + errno = EINVAL; + if (errstr != NULL) + *errstr = strerror(errno); + debug_return_ssize_t(-1); + } + +#ifdef HAVE_ZLIB_H + if (iol->compressed) { + if ((nread = gzread(iol->fd.g, buf, nbytes)) == -1) { + if (errstr != NULL) + *errstr = gzstrerror(iol->fd.g); + } + } else +#endif + { + nread = (ssize_t)fread(buf, 1, nbytes, iol->fd.f); + if (nread == 0 && ferror(iol->fd.f)) { + nread = -1; + if (errstr != NULL) + *errstr = strerror(errno); + } + } + debug_return_ssize_t(nread); +} + +/* + * Write to an I/O log, optionally compressing. + */ +ssize_t +iolog_write(struct iolog_file *iol, const void *buf, size_t len, + const char **errstr) +{ + ssize_t ret; + debug_decl(iolog_write, SUDO_DEBUG_UTIL) + + if (len > UINT_MAX) { + errno = EINVAL; + if (errstr != NULL) + *errstr = strerror(errno); + debug_return_ssize_t(-1); + } + +#ifdef HAVE_ZLIB_H + if (iol->compressed) { + ret = gzwrite(iol->fd.g, (const voidp)buf, len); + if (ret == 0) { + ret = -1; + if (errstr != NULL) + *errstr = gzstrerror(iol->fd.g); + goto done; + } + if (iolog_flush) { + if (gzflush(iol->fd.g, Z_SYNC_FLUSH) != Z_OK) { + ret = -1; + if (errstr != NULL) + *errstr = gzstrerror(iol->fd.g); + goto done; + } + } + } else +#endif + { + ret = fwrite(buf, 1, len, iol->fd.f); + if (ret == 0) { + ret = -1; + if (errstr != NULL) + *errstr = strerror(errno); + goto done; + } + if (iolog_flush) { + if (fflush(iol->fd.f) != 0) { + ret = -1; + if (errstr != NULL) + *errstr = strerror(errno); + goto done; + } + } + } + +done: + debug_return_ssize_t(ret); +} + +/* + * Returns true if at end of I/O log file, else false. + */ +bool +iolog_eof(struct iolog_file *iol) +{ + bool ret; + debug_decl(iolog_eof, SUDO_DEBUG_UTIL) + +#ifdef HAVE_ZLIB_H + if (iol->compressed) + ret = gzeof(iol->fd.g) == 1; + else +#endif + ret = feof(iol->fd.f) == 1; + debug_return_int(ret); +} + +/* + * Like gets() but for struct iolog_file. + */ +char * +iolog_gets(struct iolog_file *iol, char *buf, size_t nbytes, + const char **errstr) +{ + char *str; + debug_decl(iolog_gets, SUDO_DEBUG_UTIL) + + if (nbytes > UINT_MAX) { + errno = EINVAL; + if (errstr != NULL) + *errstr = strerror(errno); + debug_return_str(NULL); + } + +#ifdef HAVE_ZLIB_H + if (iol->compressed) { + if ((str = gzgets(iol->fd.g, buf, nbytes)) == NULL) { + if (errstr != NULL) + *errstr = gzstrerror(iol->fd.g); + } + } else +#endif + { + if ((str = fgets(buf, nbytes, iol->fd.f)) == NULL) { + if (errstr != NULL) + *errstr = strerror(errno); + } + } + debug_return_str(str); +} + +/* + * Write the I/O log file that contains the user and command info. + * This file is not compressed. + */ +bool +iolog_write_info_file(const char *parent, struct iolog_info *log_info, + char * const argv[]) +{ + char path[PATH_MAX]; + char * const *av; + FILE *fp; + int error, len, fd; + debug_decl(iolog_info_write_log, SUDO_DEBUG_UTIL) + + len = snprintf(path, sizeof(path), "%s/log", parent); + if (len < 0 || len >= ssizeof(path)) { + errno = ENAMETOOLONG; + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "%s/log", parent); + debug_return_bool(false); + } + fd = io_open(path, O_CREAT|O_TRUNC|O_WRONLY); + if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "unable to open %s", path); + if (fd != -1) + close(fd); + debug_return_bool(false); + } + if (fchown(fd, iolog_uid, iolog_gid) != 0) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to fchown %d:%d %s", __func__, + (int)iolog_uid, (int)iolog_gid, path); + } + + fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n", + (long long)log_info->tstamp, + log_info->user ? log_info->user : "unknown", + log_info->runas_user ? log_info->runas_user : RUNAS_DEFAULT, + log_info->runas_group ? log_info->runas_group : "", + log_info->tty ? log_info->tty : "unknown", + log_info->lines, log_info->cols, + log_info->cwd ? log_info->cwd : "unknown"); + fputs(log_info->cmd ? log_info->cmd : "unknown", fp); + for (av = argv + 1; *av != NULL; av++) { + fputc(' ', fp); + fputs(*av, fp); + } + fputc('\n', fp); + fflush(fp); + if ((error = ferror(fp))) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "unable to write to I/O log file %s", path); + } + fclose(fp); + + debug_return_bool(!error); +} + +/* + * Map IOFD_* -> name. + */ +const char * +iolog_fd_to_name(int iofd) +{ + const char *ret = NULL; + debug_decl(iolog_fd_to_name, SUDO_DEBUG_UTIL) + + switch (iofd) { + case IOFD_STDIN: + ret = "stdin"; + break; + case IOFD_STDOUT: + ret = "stdout"; + break; + case IOFD_STDERR: + ret = "stderr"; + break; + case IOFD_TTYIN: + ret = "ttyin"; + break; + case IOFD_TTYOUT: + ret = "ttyout"; + break; + case IOFD_TIMING: + ret = "timing"; + break; + default: + sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: unexpected iofd %d", + __func__, iofd); + break; + } + debug_return_const_str(ret); +} diff --git a/plugins/sudoers/iolog_path.c b/lib/iolog/iolog_path.c similarity index 56% rename from plugins/sudoers/iolog_path.c rename to lib/iolog/iolog_path.c index 5a5d75ae0..40db2b067 100644 --- a/plugins/sudoers/iolog_path.c +++ b/lib/iolog/iolog_path.c @@ -26,6 +26,11 @@ #include #include #include +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif /* HAVE_STDBOOL_H */ #ifdef HAVE_STRING_H # include #endif /* HAVE_STRING_H */ @@ -34,137 +39,35 @@ #endif /* HAVE_STRINGS_H */ #include #include +#include #include +#include -#include "sudoers.h" +#include "sudo_gettext.h" /* must be included before sudo_compat.h */ -struct path_escape { - const char *name; - size_t (*copy_fn)(char *, size_t, char *); -}; - -static size_t -fill_seq(char *str, size_t strsize, char *logdir) -{ -#ifdef SUDOERS_NO_SEQ - debug_decl(fill_seq, SUDOERS_DEBUG_UTIL) - debug_return_size_t(strlcpy(str, "%{seq}", strsize)); -#else - static char sessid[7]; - int len; - debug_decl(fill_seq, SUDOERS_DEBUG_UTIL) - - if (sessid[0] == '\0') { - if (!io_nextid(logdir, def_iolog_dir, sessid)) - debug_return_size_t((size_t)-1); - } - - /* Path is of the form /var/log/sudo-io/00/00/01. */ - len = snprintf(str, strsize, "%c%c/%c%c/%c%c", sessid[0], - sessid[1], sessid[2], sessid[3], sessid[4], sessid[5]); - if (len < 0) - debug_return_size_t(strsize); /* handle non-standard snprintf() */ - debug_return_size_t(len); -#endif /* SUDOERS_NO_SEQ */ -} - -static size_t -fill_user(char *str, size_t strsize, char *unused) -{ - debug_decl(fill_user, SUDOERS_DEBUG_UTIL) - debug_return_size_t(strlcpy(str, user_name, strsize)); -} - -static size_t -fill_group(char *str, size_t strsize, char *unused) -{ - struct group *grp; - size_t len; - debug_decl(fill_group, SUDOERS_DEBUG_UTIL) - - if ((grp = sudo_getgrgid(user_gid)) != NULL) { - len = strlcpy(str, grp->gr_name, strsize); - sudo_gr_delref(grp); - } else { - len = strlen(str); - len = snprintf(str + len, strsize - len, "#%u", - (unsigned int) user_gid); - } - debug_return_size_t(len); -} - -static size_t -fill_runas_user(char *str, size_t strsize, char *unused) -{ - debug_decl(fill_runas_user, SUDOERS_DEBUG_UTIL) - debug_return_size_t(strlcpy(str, runas_pw->pw_name, strsize)); -} - -static size_t -fill_runas_group(char *str, size_t strsize, char *unused) -{ - struct group *grp; - size_t len; - debug_decl(fill_runas_group, SUDOERS_DEBUG_UTIL) - - if (runas_gr != NULL) { - len = strlcpy(str, runas_gr->gr_name, strsize); - } else { - if ((grp = sudo_getgrgid(runas_pw->pw_gid)) != NULL) { - len = strlcpy(str, grp->gr_name, strsize); - sudo_gr_delref(grp); - } else { - len = strlen(str); - len = snprintf(str + len, strsize - len, "#%u", - (unsigned int) runas_pw->pw_gid); - } - } - debug_return_size_t(len); -} - -static size_t -fill_hostname(char *str, size_t strsize, char *unused) -{ - debug_decl(fill_hostname, SUDOERS_DEBUG_UTIL) - debug_return_size_t(strlcpy(str, user_shost, strsize)); -} - -static size_t -fill_command(char *str, size_t strsize, char *unused) -{ - debug_decl(fill_command, SUDOERS_DEBUG_UTIL) - debug_return_size_t(strlcpy(str, user_base, strsize)); -} - -/* Note: "seq" must be first in the list. */ -static struct path_escape io_path_escapes[] = { - { "seq", fill_seq }, - { "user", fill_user }, - { "group", fill_group }, - { "runas_user", fill_runas_user }, - { "runas_group", fill_runas_group }, - { "hostname", fill_hostname }, - { "command", fill_command }, - { NULL, NULL } -}; +#include "sudo_compat.h" +#include "sudo_fatal.h" +#include "sudo_debug.h" +#include "sudo_util.h" +#include "sudo_iolog.h" /* * Concatenate dir + file, expanding any escape sequences. * Returns the concatenated path and sets slashp point to * the path separator between the expanded dir and file. + * XXX - simplify by only expanding one thing and removing prefix */ char * expand_iolog_path(const char *prefix, const char *dir, const char *file, - char **slashp) + char **slashp, const struct iolog_path_escape *escapes) { size_t len, prelen = 0; char *dst, *dst0, *path, *pathend, tmpbuf[PATH_MAX]; char *slash = NULL; const char *endbrace, *src = dir; - struct path_escape *escapes = NULL; - int pass, oldlocale; + int pass; bool strfit; - debug_decl(expand_iolog_path, SUDOERS_DEBUG_UTIL) + debug_decl(expand_iolog_path, SUDO_DEBUG_UTIL) /* Expanded path must be <= PATH_MAX */ if (prefix != NULL) @@ -194,7 +97,7 @@ expand_iolog_path(const char *prefix, const char *dir, const char *file, switch (pass) { case 0: src = dir; - escapes = io_path_escapes + 1; /* skip "%{seq}" */ + escapes++; /* skip "%{seq}" */ break; case 1: /* Trim trailing slashes from dir component. */ @@ -207,7 +110,7 @@ expand_iolog_path(const char *prefix, const char *dir, const char *file, continue; case 2: src = file; - escapes = io_path_escapes; + escapes--; /* restore "%{seq}" */ break; } dst0 = dst; @@ -216,7 +119,7 @@ expand_iolog_path(const char *prefix, const char *dir, const char *file, if (src[1] == '{') { endbrace = strchr(src + 2, '}'); if (endbrace != NULL) { - struct path_escape *esc; + const struct iolog_path_escape *esc; len = (size_t)(endbrace - src - 2); for (esc = escapes; esc->name != NULL; esc++) { if (strncmp(src + 2, esc->name, len) == 0 && @@ -238,7 +141,7 @@ expand_iolog_path(const char *prefix, const char *dir, const char *file, src++; } else { /* May need strftime() */ - strfit = 1; + strfit = true; } } /* Need at least 2 chars, including the NUL terminator. */ @@ -257,16 +160,10 @@ expand_iolog_path(const char *prefix, const char *dir, const char *file, if ((timeptr = localtime(&now)) == NULL) goto bad; - /* Use sudoers locale for strftime() */ - sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); - /* We only call strftime() on the current part of the buffer. */ tmpbuf[sizeof(tmpbuf) - 1] = '\0'; len = strftime(tmpbuf, sizeof(tmpbuf), dst0, timeptr); - /* Restore old locale. */ - sudoers_setlocale(oldlocale, NULL); - if (len == 0 || tmpbuf[sizeof(tmpbuf) - 1] != '\0') goto bad; /* strftime() failed, buf too small? */ diff --git a/plugins/sudoers/iolog_util.c b/lib/iolog/iolog_util.c similarity index 99% rename from plugins/sudoers/iolog_util.c rename to lib/iolog/iolog_util.c index 068c1e4d6..43e248d30 100644 --- a/plugins/sudoers/iolog_util.c +++ b/lib/iolog/iolog_util.c @@ -26,6 +26,11 @@ #include #include #include +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif /* HAVE_STDBOOL_H */ #if defined(HAVE_STDINT_H) # include #elif defined(HAVE_INTTYPES_H) @@ -44,11 +49,6 @@ #include #include #include -#ifdef HAVE_STDBOOL_H -# include -#else -# include "compat/stdbool.h" -#endif /* HAVE_STDBOOL_H */ #include "sudo_gettext.h" /* must be included before sudo_compat.h */ @@ -56,7 +56,7 @@ #include "sudo_fatal.h" #include "sudo_debug.h" #include "sudo_util.h" -#include "iolog_util.h" +#include "sudo_iolog.h" static int timing_event_adj; @@ -311,8 +311,8 @@ parse_timing(const char *line, struct timing_closure *timing) char *cp, *ep; debug_decl(parse_timing, SUDO_DEBUG_UTIL) - /* Clear fd. */ - timing->fd.v = NULL; + /* Clear iolog descriptor. */ + timing->iol = NULL; /* Parse event type. */ ulval = strtoul(line, &ep, 10); diff --git a/plugins/sudoers/regress/iolog_path/check_iolog_path.c b/lib/iolog/regress/iolog_path/check_iolog_path.c similarity index 59% rename from plugins/sudoers/regress/iolog_path/check_iolog_path.c rename to lib/iolog/regress/iolog_path/check_iolog_path.c index 817f9ac2b..148c21943 100644 --- a/plugins/sudoers/regress/iolog_path/check_iolog_path.c +++ b/lib/iolog/regress/iolog_path/check_iolog_path.c @@ -27,19 +27,25 @@ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ -#include -#include #include +#include #define SUDO_ERROR_WRAP 0 -#include "sudoers.h" -#include "def_data.c" +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" +#include "sudo_iolog.h" -struct sudo_user sudo_user; -struct passwd *list_pw; - -static char sessid[7]; +static struct iolog_escape_data { + char sessid[7]; + char *user; + char *group; + char *runas_user; + char *runas_group; + char *host; + char *command; +} escape_data; __dso_public int main(int argc, char *argv[]); @@ -50,6 +56,80 @@ usage(void) exit(1); } +static void +reset_escape_data(struct iolog_escape_data *data) +{ + free(data->user); + free(data->group); + free(data->runas_user); + free(data->runas_group); + free(data->host); + free(data->command); + memset(data, 0, sizeof(*data)); +} + +static size_t +fill_seq(char *str, size_t strsize, char *logdir) +{ + int len; + + /* Path is of the form /var/log/sudo-io/00/00/01. */ + len = snprintf(str, strsize, "%c%c/%c%c/%c%c", escape_data.sessid[0], + escape_data.sessid[1], escape_data.sessid[2], escape_data.sessid[3], + escape_data.sessid[4], escape_data.sessid[5]); + if (len < 0) + return strsize; /* handle non-standard snprintf() */ + return len; +} + +static size_t +fill_user(char *str, size_t strsize, char *unused) +{ + return strlcpy(str, escape_data.user, strsize); +} + +static size_t +fill_group(char *str, size_t strsize, char *unused) +{ + return strlcpy(str, escape_data.group, strsize); +} + +static size_t +fill_runas_user(char *str, size_t strsize, char *unused) +{ + return strlcpy(str, escape_data.runas_user, strsize); +} + +static size_t +fill_runas_group(char *str, size_t strsize, char *unused) +{ + return strlcpy(str, escape_data.runas_group, strsize); +} + +static size_t +fill_hostname(char *str, size_t strsize, char *unused) +{ + return strlcpy(str, escape_data.host, strsize); +} + +static size_t +fill_command(char *str, size_t strsize, char *unused) +{ + return strlcpy(str, escape_data.command, strsize); +} + +/* Note: "seq" must be first in the list. */ +static struct iolog_path_escape path_escapes[] = { + { "seq", fill_seq }, + { "user", fill_user }, + { "group", fill_group }, + { "runas_user", fill_runas_user }, + { "runas_group", fill_runas_group }, + { "hostname", fill_hostname }, + { "command", fill_command }, + { NULL, NULL } +}; + static int do_check(char *dir_in, char *file_in, char *tdir_out, char *tfile_out) { @@ -70,7 +150,7 @@ do_check(char *dir_in, char *file_in, char *tdir_out, char *tfile_out) strftime(dir_out, sizeof(dir_out), tdir_out, timeptr); strftime(file_out, sizeof(file_out), tfile_out, timeptr); - path = expand_iolog_path(NULL, dir_in, file_in, &slash); + path = expand_iolog_path(NULL, dir_in, file_in, &slash, path_escapes); if (path == NULL) sudo_fatalx("unable to expand I/O log path"); *slash = '\0'; @@ -92,13 +172,11 @@ do_check(char *dir_in, char *file_in, char *tdir_out, char *tfile_out) int main(int argc, char *argv[]) { - struct passwd pw, rpw; size_t len; FILE *fp; char line[2048]; char *file_in = NULL, *file_out = NULL; char *dir_in = NULL, *dir_out = NULL; - const char *errstr; int state = 0; int errors = 0; int tests = 0; @@ -112,11 +190,6 @@ main(int argc, char *argv[]) if (fp == NULL) sudo_fatalx("unable to open %s", argv[1]); - memset(&pw, 0, sizeof(pw)); - memset(&rpw, 0, sizeof(rpw)); - sudo_user.pw = &pw; - sudo_user._runas_pw = &rpw; - /* * Input consists of 12 lines: * sequence number @@ -138,37 +211,31 @@ main(int argc, char *argv[]) switch (state) { case 0: - strlcpy(sessid, line, sizeof(sessid)); + strlcpy(escape_data.sessid, line, sizeof(escape_data.sessid)); break; case 1: - if (user_name != NULL) - free(user_name); - user_name = strdup(line); + if ((escape_data.user = strdup(line)) == NULL) + sudo_fatal(NULL); break; case 2: - user_gid = (gid_t)sudo_strtoid(line, &errstr); - if (errstr != NULL) - sudo_fatalx("group ID %s: %s", line, errstr); + if ((escape_data.group = strdup(line)) == NULL) + sudo_fatal(NULL); break; case 3: - if (runas_pw->pw_name != NULL) - free(runas_pw->pw_name); - runas_pw->pw_name = strdup(line); + if ((escape_data.runas_user = strdup(line)) == NULL) + sudo_fatal(NULL); break; case 4: - runas_pw->pw_gid = (gid_t)sudo_strtoid(line, &errstr); - if (errstr != NULL) - sudo_fatalx("group ID %s: %s", line, errstr); + if ((escape_data.runas_group = strdup(line)) == NULL) + sudo_fatal(NULL); break; case 5: - if (user_shost != NULL) - free(user_shost); - user_shost = strdup(line); + if ((escape_data.host = strdup(line)) == NULL) + sudo_fatal(NULL); break; case 6: - if (user_base != NULL) - free(user_base); - user_base = strdup(line); + if ((escape_data.command = strdup(line)) == NULL) + sudo_fatal(NULL); break; case 7: if (dir_in != NULL) @@ -193,6 +260,7 @@ main(int argc, char *argv[]) case 11: errors += do_check(dir_in, file_in, dir_out, file_out); tests++; + reset_escape_data(&escape_data); break; default: sudo_fatalx("internal error, invalid state %d", state); @@ -208,10 +276,3 @@ main(int argc, char *argv[]) exit(errors); } - -bool -io_nextid(char *iolog_dir, char *fallback, char id[7]) -{ - memcpy(id, sessid, sizeof(sessid)); - return true; -} diff --git a/plugins/sudoers/regress/iolog_path/data b/lib/iolog/regress/iolog_path/data similarity index 88% rename from plugins/sudoers/regress/iolog_path/data rename to lib/iolog/regress/iolog_path/data index dcc3942b1..fa4c5b502 100644 --- a/plugins/sudoers/regress/iolog_path/data +++ b/lib/iolog/regress/iolog_path/data @@ -1,8 +1,8 @@ 000001 nobody -1 +nogroup +root root -0 somehost id /var/log/sudo-io @@ -12,9 +12,9 @@ id 000001 nobody -1 +nogroup root -0 +wheel somehost id /var/log/sudo-io @@ -24,9 +24,9 @@ id 000001 nobody -1 +nogroup root -0 +wheel somehost id /var/log/sudo-io @@ -36,9 +36,9 @@ id 000001 nobody -1 +nogroup root -0 +wheel somehost id /var/log/sudo-io/%{user} @@ -48,9 +48,9 @@ id 000001 nobody -1 +nogroup root -0 +wheel somehost su /var/log/sudo-io/%{user}/%{runas_user} @@ -60,9 +60,9 @@ su_%Y%m%s_%H%M 000001 nobody -1 +nogroup root -0 +wheel somehost su /var/log/sudo-io/ @@ -72,9 +72,9 @@ nobody/root/su_%Y%m%s_%H%M 000001 nobody -1 +nogroup root -0 +wheel somehost su /var/log/sudo-io/%d%m%Y @@ -84,9 +84,9 @@ nobody/root/su 000001 nobody -1 +nogroup root -0 +wheel somehost su //////// diff --git a/plugins/sudoers/regress/iolog_util/check_iolog_util.c b/lib/iolog/regress/iolog_util/check_iolog_util.c similarity index 96% rename from plugins/sudoers/regress/iolog_util/check_iolog_util.c rename to lib/iolog/regress/iolog_util/check_iolog_util.c index fb3fd3370..6021fa8d9 100644 --- a/plugins/sudoers/regress/iolog_util/check_iolog_util.c +++ b/lib/iolog/regress/iolog_util/check_iolog_util.c @@ -35,7 +35,7 @@ #include "sudo_compat.h" #include "sudo_util.h" #include "sudo_fatal.h" -#include "iolog_util.h" +#include "sudo_iolog.h" __dso_public int main(int argc, char *argv[]); @@ -137,14 +137,14 @@ main(int argc, char *argv[]) { int tests = 0, errors = 0; - initprogname(argc > 0 ? argv[0] : "check_iolog_reader"); + initprogname(argc > 0 ? argv[0] : "check_iolog_util"); test_parse_delay(&tests, &errors); test_adjust_delay(&tests, &errors); if (tests != 0) { - printf("check_iolog_reader: %d test%s run, %d errors, %d%% success rate\n", + printf("iolog_util: %d test%s run, %d errors, %d%% success rate\n", tests, tests == 1 ? "" : "s", errors, (tests - errors) * 100 / tests); } diff --git a/lib/util/sudo_conf.c b/lib/util/sudo_conf.c index ce1f5e56c..0538a1b3b 100644 --- a/lib/util/sudo_conf.c +++ b/lib/util/sudo_conf.c @@ -56,12 +56,6 @@ #include "sudo_debug.h" #include "sudo_util.h" -#ifdef __TANDEM -# define ROOT_UID 65535 -#else -# define ROOT_UID 0 -#endif - struct sudo_conf_table { const char *name; unsigned int namelen; diff --git a/logsrvd/Makefile.in b/logsrvd/Makefile.in index a0bacf74d..dd780a907 100644 --- a/logsrvd/Makefile.in +++ b/logsrvd/Makefile.in @@ -24,7 +24,6 @@ srcdir = @srcdir@ devdir = @devdir@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -sudoers_srcdir = @top_srcdir@/plugins/sudoers incdir = $(top_srcdir)/include rundir = @rundir@ cross_compiling = @CROSS_COMPILING@ @@ -40,7 +39,7 @@ INSTALL_OWNER = -o $(install_uid) -g $(install_gid) INSTALL_BACKUP = @INSTALL_BACKUP@ # Libraries -LT_LIBS = $(top_builddir)/lib/util/libsudo_util.la +LT_LIBS = $(top_builddir)/lib/iolog/libsudo_iolog.la LIBS = $(LT_LIBS) # C preprocessor defines @@ -49,7 +48,7 @@ CPPDEFS = -D_PATH_SUDO_LOGSRVD_CONF=\"$(sysconfdir)/sudo_logsrvd.conf\" \ # C preprocessor flags CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(devdir) -I$(srcdir) \ - -I$(sudoers_srcdir) -I$(top_srcdir) $(CPPDEFS) @CPPFLAGS@ + -I$(top_srcdir) $(CPPDEFS) @CPPFLAGS@ # Usually -O and/or -g CFLAGS = @CFLAGS@ @@ -108,10 +107,10 @@ SHELL = @SHELL@ PROGS = logsrvd sendlog -LOGSRVD_OBJS = iolog_util.o iolog_writer.o logsrvd.o logsrvd_conf.o \ - log_server.pb-c.o protobuf-c.o +LOGSRVD_OBJS = 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 +SENDLOG_OBJS = sendlog.o log_server.pb-c.o protobuf-c.o IOBJS = $(LOGSRVD_OBJS:.o=.i) $(SENDLOG_OBJS:.o=.i) @@ -144,7 +143,7 @@ logsrvd: $(LOGSRVD_OBJS) $(LT_LIBS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LOGSRVD_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) sendlog: $(SENDLOG_OBJS) $(LT_LIBS) - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(SENDLOG_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -lz + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(SENDLOG_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) GENERATED = log_server.pb-c.h log_server.pb-c.c @@ -212,37 +211,19 @@ realclean: distclean cleandir: realclean # Autogenerated dependencies, do not modify -iolog_util.o: plugins/sudoers/iolog_util.c $(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 \ - $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ - $(top_builddir)/config.h - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) plugins/sudoers/iolog_util.c -iolog_util.i: plugins/sudoers/iolog_util.c $(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 \ - $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ - $(top_builddir)/config.h - $(CC) -E -o $@ $(CPPFLAGS) $< -iolog_util.plog: iolog_util.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file plugins/sudoers/iolog_util.c --i-file $< --output-file $@ iolog_writer.o: $(srcdir)/iolog_writer.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_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ - $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ - $(top_builddir)/config.h + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/logsrvd.h \ + $(srcdir)/protobuf-c/protobuf-c.h $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_writer.c iolog_writer.i: $(srcdir)/iolog_writer.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_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ - $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ - $(top_builddir)/config.h + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/logsrvd.h \ + $(srcdir)/protobuf-c/protobuf-c.h $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< iolog_writer.plog: iolog_writer.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_writer.c --i-file $< --output-file $@ @@ -258,34 +239,36 @@ logsrvd.o: $(srcdir)/logsrvd.c $(devdir)/log_server.pb-c.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_rand.h $(incdir)/sudo_util.h \ - $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ - $(top_builddir)/config.h $(top_builddir)/pathnames.h + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h $(incdir)/sudo_rand.h \ + $(incdir)/sudo_util.h $(srcdir)/logsrvd.h \ + $(srcdir)/protobuf-c/protobuf-c.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/logsrvd.c logsrvd.i: $(srcdir)/logsrvd.c $(devdir)/log_server.pb-c.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_rand.h $(incdir)/sudo_util.h \ - $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ - $(top_builddir)/config.h $(top_builddir)/pathnames.h + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h $(incdir)/sudo_rand.h \ + $(incdir)/sudo_util.h $(srcdir)/logsrvd.h \ + $(srcdir)/protobuf-c/protobuf-c.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.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 \ + $(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.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 \ + $(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< logsrvd_conf.plog: logsrvd_conf.i @@ -301,9 +284,8 @@ sendlog.o: $(srcdir)/sendlog.c $(devdir)/log_server.pb-c.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(srcdir)/protobuf-c/protobuf-c.h $(srcdir)/sendlog.h \ - $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sendlog.c sendlog.i: $(srcdir)/sendlog.c $(devdir)/log_server.pb-c.h \ @@ -311,9 +293,8 @@ sendlog.i: $(srcdir)/sendlog.c $(devdir)/log_server.pb-c.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(srcdir)/protobuf-c/protobuf-c.h $(srcdir)/sendlog.h \ - $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< sendlog.plog: sendlog.i diff --git a/logsrvd/iolog_writer.c b/logsrvd/iolog_writer.c index 7914cb362..54f6eadf6 100644 --- a/logsrvd/iolog_writer.c +++ b/logsrvd/iolog_writer.c @@ -42,20 +42,9 @@ #include "sudo_debug.h" #include "sudo_util.h" #include "sudo_fatal.h" -#include "iolog_util.h" +#include "sudo_iolog.h" #include "logsrvd.h" -/* I/O log file names relative to iolog_dir. */ -static const char *iolog_names[] = { - "stdin", /* IOFD_STDIN */ - "stdout", /* IOFD_STDOUT */ - "stderr", /* IOFD_STDERR */ - "ttyin", /* IOFD_TTYIN */ - "ttyout", /* IOFD_TTYOUT */ - "timing", /* IOFD_TIMING */ - NULL /* IOFD_MAX */ -}; - static inline bool has_numval(InfoMessage *info) { @@ -284,13 +273,13 @@ create_iolog_dir(struct iolog_details *details, struct connection_closure *closu goto bad; } - /* Make a copy of iolog_dir for error messages. */ if ((closure->iolog_dir = strdup(path)) == NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, "strdup"); goto bad; } +#if 0 /* We use iolog_dir_fd in calls to openat(2) */ closure->iolog_dir_fd = open(closure->iolog_dir, O_RDONLY); if (closure->iolog_dir_fd == -1) { @@ -298,6 +287,7 @@ create_iolog_dir(struct iolog_details *details, struct connection_closure *closu "%s", closure->iolog_dir); goto bad; } +#endif debug_return_bool(true); bad: @@ -349,9 +339,11 @@ iolog_details_write(struct iolog_details *details, struct connection_closure *cl } static bool -iolog_open(int iofd, struct connection_closure *closure) +iolog_create(int iofd, struct connection_closure *closure) { - debug_decl(iolog_open, SUDO_DEBUG_UTIL) + char path[PATH_MAX]; + int len; + debug_decl(iolog_create, SUDO_DEBUG_UTIL) if (iofd < 0 || iofd >= IOFD_MAX) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, @@ -359,24 +351,34 @@ iolog_open(int iofd, struct connection_closure *closure) debug_return_bool(false); } - closure->io_fds[iofd] = openat(closure->iolog_dir_fd, - iolog_names[iofd], O_CREAT|O_EXCL|O_WRONLY, 0600); - debug_return_bool(closure->io_fds[iofd] != -1); + len = snprintf(path, sizeof(path), "%s/%s", closure->iolog_dir, + iolog_fd_to_name(iofd)); + if (len < 0 || len >= ssizeof(path)) { + errno = ENAMETOOLONG; + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, + "%s/%s", closure->iolog_dir, iolog_fd_to_name(iofd)); + debug_return_bool(false); + } + + closure->iolog_files[iofd].enabled = true; + debug_return_bool(iolog_open(&closure->iolog_files[iofd], path, "w")); } void -iolog_close(struct connection_closure *closure) +iolog_close_all(struct connection_closure *closure) { + const char *errstr; int i; debug_decl(iolog_close, SUDO_DEBUG_UTIL) for (i = 0; i < IOFD_MAX; i++) { - if (closure->io_fds[i] == -1) + if (!closure->iolog_files[i].enabled) continue; - close(closure->io_fds[i]); + if (!iolog_close(&closure->iolog_files[i], &errstr)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "error closing iofd %d: %s", i, errstr); + } } - if (closure->iolog_dir_fd != -1) - close(closure->iolog_dir_fd); debug_return; } @@ -385,13 +387,8 @@ bool iolog_init(ExecMessage *msg, struct connection_closure *closure) { struct iolog_details details; - int i; debug_decl(iolog_init, SUDO_DEBUG_UTIL) - /* Init io_fds in closure. */ - for (i = 0; i < IOFD_MAX; i++) - closure->io_fds[i] = -1; - /* Fill in iolog_details */ if (!iolog_details_fill(&details, msg)) debug_return_bool(false); @@ -404,11 +401,14 @@ iolog_init(ExecMessage *msg, struct connection_closure *closure) if (!iolog_details_write(&details, closure)) debug_return_bool(false); - /* Create timing, stdout, stderr and ttyout files for sudoreplay. */ - if (!iolog_open(IOFD_TIMING, closure) || - !iolog_open(IOFD_STDOUT, closure) || - !iolog_open(IOFD_STDERR, closure) || - !iolog_open(IOFD_TTYOUT, closure)) + /* + * Create timing, stdout, stderr and ttyout files for sudoreplay. + * Others will be created on demand. + */ + if (!iolog_create(IOFD_TIMING, closure) || + !iolog_create(IOFD_STDOUT, closure) || + !iolog_create(IOFD_STDERR, closure) || + !iolog_create(IOFD_TTYOUT, closure)) debug_return_bool(false); /* Ready to log I/O buffers. */ @@ -420,18 +420,19 @@ iolog_init(ExecMessage *msg, struct connection_closure *closure) * Return 0 on success, 1 on EOF and -1 on error. */ static int -read_timing_record(FILE *fp, struct timing_closure *timing) +read_timing_record(struct iolog_file *iol, struct timing_closure *timing) { char line[LINE_MAX]; + const char *errstr; debug_decl(read_timing_record, SUDO_DEBUG_UTIL) /* Read next record from timing file. */ - if (fgets(line, sizeof(line), fp) == NULL) { + if (iolog_gets(iol, line, sizeof(line), &errstr) == NULL) { /* EOF or error reading timing file, we are done. */ - if (feof(fp)) + if (iolog_eof(iol)) debug_return_int(1); /* EOF */ - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "error reading timing file"); + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "error reading timing file: %s", errstr); debug_return_int(-1); } @@ -446,80 +447,65 @@ read_timing_record(FILE *fp, struct timing_closure *timing) debug_return_int(0); } +/* XXX - compressed I/O logs cannot be restarted, must re-write them */ bool iolog_restart(RestartMessage *msg, struct connection_closure *closure) { struct timespec target; struct timing_closure timing; - FILE *fp = NULL; - int i, fd; - off_t length; + off_t pos; + int i; debug_decl(iolog_init, SUDO_DEBUG_UTIL) target.tv_sec = msg->resume_point->tv_sec; target.tv_nsec = msg->resume_point->tv_nsec; - /* We use iolog_dir_fd in calls to openat(2) */ if ((closure->iolog_dir = strdup(msg->log_id)) == NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, "strdup"); goto bad; } - closure->iolog_dir_fd = open(closure->iolog_dir, O_RDONLY); - if (closure->iolog_dir_fd == -1) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "%s", closure->iolog_dir); - goto bad; - } /* Open existing I/O log files. */ for (i = 0; i < IOFD_MAX; i++) { - closure->io_fds[i] = openat(closure->iolog_dir_fd, iolog_names[i], - O_RDWR, 0600); + char path[PATH_MAX]; + int len = snprintf(path, sizeof(path), "%s/%s", closure->iolog_dir, + iolog_fd_to_name(i)); + if (len < 0 || len >= ssizeof(path)) { + errno = ENAMETOOLONG; + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: %s/%s", __func__, closure->iolog_dir, iolog_fd_to_name(i)); + goto bad; + } + closure->iolog_files[i].enabled = true; + (void)iolog_open(&closure->iolog_files[i], path, "r+"); } - - /* Dup IOFD_TIMING to a stream for easier processing. */ - if (closure->io_fds[IOFD_TIMING] == -1) + if (!closure->iolog_files[IOFD_TIMING].enabled) goto bad; - if ((fd = dup(closure->io_fds[IOFD_TIMING])) == -1) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "dup"); - goto bad; - } - if ((fp = fdopen(fd, "r")) == NULL) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "fdopen"); - goto bad; - } /* Parse timing file until we reach the target point. */ /* XXX - split up */ for (;;) { - if (read_timing_record(fp, &timing) != 0) + if (read_timing_record(&closure->iolog_files[IOFD_TIMING], &timing) != 0) goto bad; sudo_timespecadd(&timing.delay, &closure->elapsed_time, &closure->elapsed_time); if (timing.event < IOFD_TIMING) { - if (closure->io_fds[timing.event] == -1) { + if (!closure->iolog_files[timing.event].enabled) { /* Missing log file. */ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "iofd %d referenced but not open", timing.event); goto bad; } - length = lseek(closure->io_fds[timing.event], timing.u.nbytes, - SEEK_CUR); - if (length == -1) { + pos = iolog_seek(&closure->iolog_files[timing.event], + timing.u.nbytes, SEEK_CUR); + if (pos == -1) { sudo_debug_printf( SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "lseek(%d, %lld, SEEK_CUR", closure->io_fds[timing.event], + "seek(%d, %lld, SEEK_CUR", timing.event, (long long)timing.u.nbytes); goto bad; } - if (ftruncate(closure->io_fds[timing.event], length) == -1) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "ftruncate(%d, %lld)", closure->io_fds[IOFD_TIMING], length); - goto bad; - } } if (sudo_timespeccmp(&closure->elapsed_time, &target, >=)) { if (sudo_timespeccmp(&closure->elapsed_time, &target, ==)) @@ -534,47 +520,19 @@ iolog_restart(RestartMessage *msg, struct connection_closure *closure) goto bad; } } - /* Update timing file position after determining resume point. */ -#ifdef HAVE_FSEEKO - length = ftello(fp); -#else - length = ftell(fp); -#endif - fclose(fp); - if (lseek(closure->io_fds[IOFD_TIMING], length, SEEK_SET) == -1) { + /* Must seek or flush before switching from read -> write. */ + if (iolog_seek(&closure->iolog_files[IOFD_TIMING], 0, SEEK_CUR) == -1) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "lseek(%d, %lld, SEEK_SET", closure->io_fds[IOFD_TIMING], length); - goto bad; - } - if (ftruncate(closure->io_fds[IOFD_TIMING], length) == -1) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, - "ftruncate(%d, %lld)", closure->io_fds[IOFD_TIMING], length); + "lseek(IOFD_TIMING, 0, SEEK_CUR)"); goto bad; } /* Ready to log I/O buffers. */ debug_return_bool(true); bad: - if (fp != NULL) - fclose(fp); debug_return_bool(false); } -static bool -iolog_write(int iofd, void *buf, size_t len, struct connection_closure *closure) -{ - debug_decl(iolog_write, SUDO_DEBUG_UTIL) - size_t nread; - - if (iofd < 0 || iofd >= IOFD_MAX) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "invalid iofd %d", iofd); - debug_return_bool(false); - } - nread = write(closure->io_fds[iofd], buf, len); - debug_return_bool(nread == len); -} - /* * Add given delta to elapsed time. * We cannot use timespecadd here since delta is not struct timespec. @@ -598,13 +556,14 @@ update_elapsed_time(TimeSpec *delta, struct timespec *elapsed) int store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure) { + const char *errstr; char tbuf[1024]; int len; debug_decl(store_iobuf, SUDO_DEBUG_UTIL) /* Open log file as needed. */ - if (closure->io_fds[iofd] == -1) { - if (!iolog_open(iofd, closure)) + if (!closure->iolog_files[iofd].enabled) { + if (!iolog_create(iofd, closure)) debug_return_int(-1); } @@ -620,12 +579,22 @@ store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure) } /* Write to specified I/O log file. */ - if (!iolog_write(iofd, msg->data.data, msg->data.len, closure)) + if (!iolog_write(&closure->iolog_files[iofd], msg->data.data, + msg->data.len, &errstr)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "unable to write to %s/%s: %s", closure->iolog_dir, + iolog_fd_to_name(iofd), errstr); debug_return_int(-1); + } /* Write timing data. */ - if (!iolog_write(IOFD_TIMING, tbuf, len, closure)) + if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf, + len, &errstr)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "unable to write to %s/%s: %s", closure->iolog_dir, + iolog_fd_to_name(IOFD_TIMING), errstr); debug_return_int(-1); + } update_elapsed_time(msg->delay, &closure->elapsed_time); @@ -635,6 +604,7 @@ store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure) int store_suspend(CommandSuspend *msg, struct connection_closure *closure) { + const char *errstr; char tbuf[1024]; int len; debug_decl(store_suspend, SUDO_DEBUG_UTIL) @@ -650,8 +620,13 @@ store_suspend(CommandSuspend *msg, struct connection_closure *closure) } /* Write timing data. */ - if (!iolog_write(IOFD_TIMING, tbuf, len, closure)) + if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf, + len, &errstr)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "unable to write to %s/%s: %s", closure->iolog_dir, + iolog_fd_to_name(IOFD_TIMING), errstr); debug_return_int(-1); + } update_elapsed_time(msg->delay, &closure->elapsed_time); @@ -661,6 +636,7 @@ store_suspend(CommandSuspend *msg, struct connection_closure *closure) int store_winsize(ChangeWindowSize *msg, struct connection_closure *closure) { + const char *errstr; char tbuf[1024]; int len; debug_decl(store_winsize, SUDO_DEBUG_UTIL) @@ -676,8 +652,13 @@ store_winsize(ChangeWindowSize *msg, struct connection_closure *closure) } /* Write timing data. */ - if (!iolog_write(IOFD_TIMING, tbuf, len, closure)) + if (!iolog_write(&closure->iolog_files[IOFD_TIMING], tbuf, + len, &errstr)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "unable to write to %s/%s: %s", closure->iolog_dir, + iolog_fd_to_name(IOFD_TIMING), errstr); debug_return_int(-1); + } update_elapsed_time(msg->delay, &closure->elapsed_time); diff --git a/logsrvd/logsrvd.c b/logsrvd/logsrvd.c index f46ec6f76..03a1a460e 100644 --- a/logsrvd/logsrvd.c +++ b/logsrvd/logsrvd.c @@ -50,6 +50,7 @@ #include "sudo_util.h" #include "sudo_rand.h" #include "sudo_fatal.h" +#include "sudo_iolog.h" #include "pathnames.h" #include "logsrvd.h" @@ -84,7 +85,7 @@ connection_closure_free(struct connection_closure *closure) TAILQ_REMOVE(&connections, closure, entries); close(closure->sock); - iolog_close(closure); + iolog_close_all(closure); sudo_ev_free(closure->commit_ev); sudo_ev_free(closure->read_ev); sudo_ev_free(closure->write_ev); diff --git a/logsrvd/logsrvd.h b/logsrvd/logsrvd.h index ef83b016c..5b15121b9 100644 --- a/logsrvd/logsrvd.h +++ b/logsrvd/logsrvd.h @@ -14,6 +14,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef SUDO_LOGSRVD_H +#define SUDO_LOGSRVD_H + #if PROTOBUF_C_VERSION_NUMBER < 1003000 # error protobuf-c version 1.30 or higher required #endif @@ -28,18 +31,6 @@ /* Shutdown timeout (in seconds) in case client connections time out. */ #define SHUTDOWN_TIMEO 10 -/* - * Indexes into io_fds[] and iolog_names[] - * The first five must match the IO_EVENT_ defines in iolog.h. - */ -#define IOFD_STDIN 0 -#define IOFD_STDOUT 1 -#define IOFD_STDERR 2 -#define IOFD_TTYIN 3 -#define IOFD_TTYOUT 4 -#define IOFD_TIMING 5 -#define IOFD_MAX 6 - /* * I/O log details from the ExecMessage */ @@ -93,19 +84,19 @@ struct connection_closure { struct sudo_event *read_ev; struct sudo_event *write_ev; char *iolog_dir; + struct iolog_file iolog_files[IOFD_MAX]; int iolog_dir_fd; - int io_fds[IOFD_MAX]; int sock; enum connection_status state; }; -/* iolog.c */ +/* iolog_writer.c */ bool iolog_init(ExecMessage *msg, struct connection_closure *closure); bool iolog_restart(RestartMessage *msg, struct connection_closure *closure); 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); +void iolog_close_all(struct connection_closure *closure); /* logsrvd_conf.c */ bool logsrvd_conf_iolog_compress(void); @@ -117,3 +108,5 @@ 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); + +#endif /* SUDO_LOGSRVD_H */ diff --git a/logsrvd/logsrvd_conf.c b/logsrvd/logsrvd_conf.c index c5b76cbc0..4c1e849ef 100644 --- a/logsrvd/logsrvd_conf.c +++ b/logsrvd/logsrvd_conf.c @@ -39,9 +39,9 @@ #include "sudo_debug.h" #include "sudo_util.h" #include "sudo_fatal.h" +#include "sudo_iolog.h" #include "pathnames.h" #include "logsrvd.h" -#include "iolog.h" enum config_type { CONF_BOOL, diff --git a/logsrvd/sendlog.c b/logsrvd/sendlog.c index 90136627e..ec2f5b754 100644 --- a/logsrvd/sendlog.c +++ b/logsrvd/sendlog.c @@ -54,22 +54,11 @@ #include "sudo_util.h" #include "sudo_event.h" #include "sudo_fatal.h" -#include "iolog_util.h" +#include "sudo_iolog.h" #include "sendlog.h" -static gzFile io_fds[IOFD_MAX]; - -/* I/O log file names relative to the iolog dir. */ -/* XXX - duplicated with server */ -static const char *iolog_names[] = { - "stdin", /* IOFD_STDIN */ - "stdout", /* IOFD_STDOUT */ - "stderr", /* IOFD_STDERR */ - "ttyin", /* IOFD_TTYIN */ - "ttyout", /* IOFD_TTYOUT */ - "timing", /* IOFD_TIMING */ - NULL /* IOFD_MAX */ -}; +static struct iolog_file iolog_files[IOFD_MAX]; +static char *iolog_path; static void usage(void) @@ -151,8 +140,6 @@ client_closure_free(struct client_closure *closure) free(closure->read_buf.data); free(closure->write_buf.data); free(closure->buf); - // XXX - avoid use after free - //free(closure); } debug_return; @@ -165,26 +152,23 @@ client_closure_free(struct client_closure *closure) int read_timing_record(struct timing_closure *timing) { - const char *errstr; char line[LINE_MAX]; - int errnum; + const char *errstr; debug_decl(read_timing_record, SUDO_DEBUG_UTIL) /* Read next record from timing file. */ - if (gzgets(io_fds[IOFD_TIMING], line, sizeof(line)) == NULL) { + if (iolog_gets(&iolog_files[IOFD_TIMING], line, sizeof(line), &errstr) == NULL) { /* EOF or error reading timing file, we are done. */ - if (gzeof(io_fds[IOFD_TIMING])) - debug_return_int(1); /* EOF */ - if ((errstr = gzerror(io_fds[IOFD_TIMING], &errnum)) == NULL) - errstr = strerror(errno); - sudo_warnx("error reading timing file: %s", errstr); + if (iolog_eof(&iolog_files[IOFD_TIMING])) + debug_return_int(1); + sudo_warnx(U_("error reading timing file: %s"), errstr); debug_return_int(-1); } /* Parse timing file record. */ line[strcspn(line, "\n")] = '\0'; if (!parse_timing(line, timing)) { - sudo_warnx("invalid timing file line: %s", line); + sudo_warnx(U_("invalid timing file line: %s"), line); debug_return_int(-1); } @@ -198,11 +182,13 @@ static bool read_io_buf(struct client_closure *closure) { struct timing_closure *timing = &closure->timing; + const char *errstr = NULL; size_t nread; debug_decl(read_io_buf, SUDO_DEBUG_UTIL) - if (io_fds[timing->event] == NULL) { - sudo_warnx("%s file not open", iolog_names[timing->event]); + if (!iolog_files[timing->event].enabled) { + errno = ENOENT; + sudo_warn("%s/%s", iolog_path, iolog_fd_to_name(timing->event)); debug_return_bool(false); } @@ -219,15 +205,11 @@ read_io_buf(struct client_closure *closure) } } - nread = gzread(io_fds[timing->event], closure->buf, timing->u.nbytes); + nread = iolog_read(&iolog_files[timing->event], closure->buf, + timing->u.nbytes, &errstr); if (nread != timing->u.nbytes) { - int errnum; - const char *errstr; - - if ((errstr = gzerror(io_fds[timing->event], &errnum)) == NULL) - errstr = strerror(errno); - sudo_warnx("unable to read %s file: %s", iolog_names[timing->event], - errstr); + sudo_warnx(U_("unable to read %s/%s: %s"), iolog_path, + iolog_fd_to_name(timing->event), errstr); debug_return_bool(false); } debug_return_bool(true); @@ -742,7 +724,7 @@ handle_server_hello(ServerHello *msg, struct client_closure *closure) /* Sanity check ServerHello message. */ if (msg->server_id == NULL || msg->server_id[0] == '\0') { - sudo_warnx("invalid ServerHello"); + sudo_warnx("%s", U_("invalid ServerHello")); debug_return_bool(false); } @@ -1034,23 +1016,25 @@ bad: * The timing file must always exist. */ bool -iolog_open_all(const char *iolog_path) +iolog_open_all(char *path, size_t dir_len) { - char fname[PATH_MAX]; - int i, len; + size_t file_len; + int i; debug_decl(iolog_open_all, SUDO_DEBUG_UTIL) - for (i = 0; iolog_names[i] != NULL; i++) { - len = snprintf(fname, sizeof(fname), "%s/%s", iolog_path, - iolog_names[i]); - if (len < 0 || len >= ssizeof(fname)) { - errno = ENAMETOOLONG; - sudo_warn("%s/%s", iolog_path, iolog_names[i]); + for (i = 0; i < IOFD_MAX; i++) { + path[dir_len] = '\0'; + file_len = strlen(iolog_fd_to_name(i)); + if (dir_len + 1 + file_len + 1 >= PATH_MAX) { + errno = ENAMETOOLONG; + sudo_warn("%s/%s", path, iolog_fd_to_name(i)); + debug_return_bool(false); } - io_fds[i] = gzopen(fname, "r"); - if (io_fds[i] == NULL && i == IOFD_TIMING) { - /* The timing file is not optional. */ - sudo_warn("unable to open %s/%s", iolog_path, iolog_names[i]); + path[dir_len++] = '/'; + memcpy(path + dir_len, iolog_fd_to_name(i), file_len + 1); + iolog_files[i].enabled = true; + if (!iolog_open(&iolog_files[i], path, "r")) { + sudo_warn(U_("unable to open %s"), path); debug_return_bool(false); } } @@ -1104,9 +1088,9 @@ main(int argc, char *argv[]) const char *host = "localhost"; const char *port = DEFAULT_PORT_STR; struct timespec restart = { 0, 0 }; - char fname[PATH_MAX], *iolog_path; + char pathbuf[PATH_MAX]; const char *iolog_id = NULL; - int ch, sock; + int ch, len, sock; debug_decl_vars(main, SUDO_DEBUG_MAIN) initprogname(argc > 0 ? argv[0] : "sendlog"); @@ -1158,12 +1142,18 @@ main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); /* Parse I/O info log file. */ - snprintf(fname, sizeof(fname), "%s/log", iolog_path); - if ((log_info = parse_logfile(fname)) == NULL) + len = snprintf(pathbuf, sizeof(pathbuf), "%s/log", iolog_path); + if (len < 0 || len >= ssizeof(pathbuf)) { + errno = ENAMETOOLONG; + sudo_warn("%s/log", iolog_path); + goto bad; + } + len -= 4; + if ((log_info = parse_logfile(pathbuf)) == NULL) goto bad; /* Open the I/O log files. */ - if (!iolog_open_all(iolog_path)) + if (!iolog_open_all(pathbuf, len)) goto bad; /* Connect to server, setup events. */ diff --git a/logsrvd/sendlog.h b/logsrvd/sendlog.h index 0880accf9..9d0549c92 100644 --- a/logsrvd/sendlog.h +++ b/logsrvd/sendlog.h @@ -14,25 +14,15 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef SUDO_SENDLOG_H +#define SUDO_SENDLOG_H + #if PROTOBUF_C_VERSION_NUMBER < 1003000 # error protobuf-c version 1.30 or higher required #endif #define DEFAULT_PORT_STR "30344" -/* - * Indexes into io_fds[] and iolog_names[] - * The first five must match the IO_EVENT_ defines in iolog.h. - * XXX - needed? - */ -#define IOFD_STDIN 0 -#define IOFD_STDOUT 1 -#define IOFD_STDERR 2 -#define IOFD_TTYIN 3 -#define IOFD_TTYOUT 4 -#define IOFD_TIMING 5 -#define IOFD_MAX 6 - enum client_state { ERROR, RECV_HELLO, @@ -67,3 +57,5 @@ struct client_closure { size_t bufsize; /* XXX */ enum client_state state; }; + +#endif /* SUDO_SENDLOG_H */ diff --git a/plugins/sample/sample_plugin.c b/plugins/sample/sample_plugin.c index 97bc49d54..bbf2098de 100644 --- a/plugins/sample/sample_plugin.c +++ b/plugins/sample/sample_plugin.c @@ -59,12 +59,6 @@ * caching the validate and invalidate functions are NULL. */ -#ifdef __TANDEM -# define ROOT_UID 65535 -#else -# define ROOT_UID 0 -#endif - static struct plugin_state { char **envp; char * const *settings; diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index b082f29a3..2703ce94a 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -52,11 +52,12 @@ INSTALL_OWNER = -o $(install_uid) -g $(install_gid) INSTALL_BACKUP = @INSTALL_BACKUP@ # Libraries -LT_LIBS = $(top_builddir)/lib/util/libsudo_util.la -LIBS = $(LT_LIBS) +LIBUTIL = $(top_builddir)/lib/util/libsudo_util.la +LIBIOLOG = $(top_builddir)/lib/iolog/libsudo_iolog.la +LIBS = $(LIBUTIL) NET_LIBS = @NET_LIBS@ -SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS) @ZLIB@ -REPLAY_LIBS = @REPLAY_LIBS@ @ZLIB@ +SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(NET_LIBS) $(LIBIOLOG) +REPLAY_LIBS = @REPLAY_LIBS@ $(LIBIOLOG) VISUDO_LIBS = $(NET_LIBS) CVTSUDOERS_LIBS = $(NET_LIBS) TESTSUDOERS_LIBS = $(NET_LIBS) @@ -148,8 +149,8 @@ SHELL = @SHELL@ PROGS = sudoers.la visudo sudoreplay cvtsudoers testsudoers TEST_PROGS = check_addr check_base64 check_digest check_env_pattern check_fill \ - check_gentime check_hexchar check_iolog_path check_iolog_plugin \ - check_iolog_util check_wrap check_starttime @SUDOERS_TEST_PROGS@ + check_gentime check_hexchar check_iolog_plugin check_wrap \ + check_starttime @SUDOERS_TEST_PROGS@ AUTH_OBJS = sudo_auth.lo @AUTH_OBJS@ @@ -165,7 +166,7 @@ LIBPARSESUDOERS_IOBJS = $(LIBPARSESUDOERS_OBJS:.lo=.i) passwd.i SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo \ env_pattern.lo file.lo find_path.lo fmtsudoers.lo gc.lo \ goodpath.lo group_plugin.lo interfaces.lo iolog.lo \ - iolog_path.lo locale.lo logging.lo logwrap.lo \ + iolog_path_escapes.lo locale.lo logging.lo logwrap.lo \ parse.lo policy.lo prompt.lo set_perms.lo starttime.lo \ sudo_nss.lo sudoers.lo timestamp.lo @SUDOERS_OBJS@ @@ -183,7 +184,7 @@ CVTSUDOERS_OBJS = cvtsudoers.o cvtsudoers_json.o cvtsudoers_ldif.o \ CVTSUDOERS_IOBJS = cvtsudoers.i cvtsudoers_json.i cvtsudoers_ldif.i \ cvtsudoers_pwutil.i strlist.i -REPLAY_OBJS = getdate.o sudoreplay.o iolog_util.o +REPLAY_OBJS = getdate.o sudoreplay.o REPLAY_IOBJS = $(REPLAY_OBJS:.o=.i) @@ -213,16 +214,9 @@ CHECK_GENTIME_OBJS = check_gentime.o gentime.lo gmtoff.lo sudoers_debug.lo CHECK_HEXCHAR_OBJS = check_hexchar.o hexchar.lo sudoers_debug.lo -CHECK_IOLOG_PATH_OBJS = check_iolog_path.o iolog_path.lo locale.lo \ - pwutil.lo pwutil_impl.lo redblack.lo sudoers_debug.lo - -CHECK_IOLOG_PLUGIN_OBJS = check_iolog_plugin.o iolog.lo iolog_path.lo \ - iolog_util.o locale.lo pwutil.lo \ +CHECK_IOLOG_PLUGIN_OBJS = check_iolog_plugin.o iolog.lo locale.lo pwutil.lo \ pwutil_impl.lo redblack.lo sudoers_debug.lo -CHECK_IOLOG_UTIL_OBJS = check_iolog_util.o iolog_util.o locale.lo \ - sudoers_debug.lo - CHECK_SYMBOLS_OBJS = check_symbols.o CHECK_STARTTIME_OBJS = check_starttime.o starttime.lo sudoers_debug.lo @@ -272,7 +266,7 @@ Makefile: $(srcdir)/Makefile.in libparsesudoers.la: $(LIBPARSESUDOERS_OBJS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LIBPARSESUDOERS_OBJS) -no-install -sudoers.la: $(SUDOERS_OBJS) $(LT_LIBS) libparsesudoers.la @LT_LDDEP@ +sudoers.la: $(SUDOERS_OBJS) $(LIBUTIL) $(LIBIOLOG) libparsesudoers.la @LT_LDDEP@ case "$(LT_LDFLAGS)" in \ *-no-install*) \ $(LIBTOOL) $(LTFLAGS) @LT_STATIC@ --mode=link $(CC) $(LDFLAGS) $(LT_LDFLAGS) -o $@ $(SUDOERS_OBJS) libparsesudoers.la $(SUDOERS_LIBS) -module;; \ @@ -280,59 +274,53 @@ sudoers.la: $(SUDOERS_OBJS) $(LT_LIBS) libparsesudoers.la @LT_LDDEP@ $(LIBTOOL) $(LTFLAGS) @LT_STATIC@ --mode=link $(CC) $(LDFLAGS) $(ASAN_LDFLAGS) $(SSP_LDFLAGS) $(LT_LDFLAGS) -o $@ $(SUDOERS_OBJS) libparsesudoers.la $(SUDOERS_LIBS) -module -avoid-version -rpath $(plugindir) -shrext .so;; \ esac -visudo: libparsesudoers.la $(VISUDO_OBJS) $(LT_LIBS) +visudo: libparsesudoers.la $(VISUDO_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(VISUDO_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) libparsesudoers.la $(LIBS) $(VISUDO_LIBS) -cvtsudoers: libparsesudoers.la $(CVTSUDOERS_OBJS) $(LT_LIBS) +cvtsudoers: libparsesudoers.la $(CVTSUDOERS_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CVTSUDOERS_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) libparsesudoers.la $(LIBS) $(CVTSUDOERS_LIBS) -sudoreplay: timestr.lo $(REPLAY_OBJS) $(LT_LIBS) - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) timestr.lo $(LIBS) $(REPLAY_LIBS) +sudoreplay: timestr.lo $(REPLAY_OBJS) $(LIBUTIL) $(LIBIOLOG) + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) timestr.lo $(REPLAY_LIBS) -testsudoers: libparsesudoers.la $(TEST_OBJS) $(LT_LIBS) +testsudoers: libparsesudoers.la $(TEST_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) libparsesudoers.la $(LIBS) $(TESTSUDOERS_LIBS) -tsdump: $(TSDUMP_OBJS) $(LT_LIBS) +tsdump: $(TSDUMP_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(TSDUMP_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_addr: $(CHECK_ADDR_OBJS) $(LT_LIBS) +check_addr: $(CHECK_ADDR_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_ADDR_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) $(NET_LIBS) -check_base64: $(CHECK_BASE64_OBJS) $(LT_LIBS) +check_base64: $(CHECK_BASE64_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_BASE64_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_digest: $(CHECK_DIGEST_OBJS) $(LT_LIBS) +check_digest: $(CHECK_DIGEST_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_DIGEST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_env_pattern: $(CHECK_ENV_MATCH_OBJS) $(LT_LIBS) +check_env_pattern: $(CHECK_ENV_MATCH_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_ENV_MATCH_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_fill: $(CHECK_FILL_OBJS) $(LT_LIBS) +check_fill: $(CHECK_FILL_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_FILL_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_gentime: $(CHECK_GENTIME_OBJS) $(LT_LIBS) +check_gentime: $(CHECK_GENTIME_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_GENTIME_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_hexchar: $(CHECK_HEXCHAR_OBJS) $(LT_LIBS) +check_hexchar: $(CHECK_HEXCHAR_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_HEXCHAR_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) -check_iolog_path: $(CHECK_IOLOG_PATH_OBJS) $(LT_LIBS) - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PATH_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) +check_iolog_plugin: $(CHECK_IOLOG_PLUGIN_OBJS) $(LIBUTIL) $(LIBIOLOG) + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PLUGIN_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBIOLOG) -check_iolog_plugin: $(CHECK_IOLOG_PLUGIN_OBJS) $(LT_LIBS) - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PLUGIN_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) @ZLIB@ - -check_iolog_util: $(CHECK_IOLOG_UTIL_OBJS) $(LT_LIBS) - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_UTIL_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) @ZLIB@ - -check_starttime: $(CHECK_STARTTIME_OBJS) $(LT_LIBS) +check_starttime: $(CHECK_STARTTIME_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_STARTTIME_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) # We need to link check_symbols with -lpthread on HP-UX since LDAP uses threads -check_symbols: $(CHECK_SYMBOLS_OBJS) $(LT_LIBS) +check_symbols: $(CHECK_SYMBOLS_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_SYMBOLS_OBJS) $(CHECK_SYMBOLS_LDFLAGS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) @SUDO_LIBS@ -check_wrap: $(CHECK_WRAP_OBJS) $(LT_LIBS) +check_wrap: $(CHECK_WRAP_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_WRAP_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) GENERATED = gram.h gram.c toke.c def_data.c def_data.h getdate.c @@ -472,9 +460,7 @@ check: $(TEST_PROGS) visudo testsudoers cvtsudoers ./check_fill || rval=`expr $$rval + $$?`; \ ./check_gentime || rval=`expr $$rval + $$?`; \ ./check_hexchar || rval=`expr $$rval + $$?`; \ - ./check_iolog_path $(srcdir)/regress/iolog_path/data || rval=`expr $$rval + $$?`; \ ./check_iolog_plugin $(srcdir)/regress/iolog_plugin/iolog || rval=`expr $$rval + $$?`; \ - ./check_iolog_util || rval=`expr $$rval + $$?`; \ ./check_starttime || rval=`expr $$rval + $$?`; \ if test -f check_symbols; then \ ./check_symbols .libs/sudoers.so $(shlib_exp) || rval=`expr $$rval + $$?`; \ @@ -933,72 +919,32 @@ check_hexchar.i: $(srcdir)/regress/parser/check_hexchar.c \ $(CC) -E -o $@ $(CPPFLAGS) $< check_hexchar.plog: check_hexchar.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/parser/check_hexchar.c --i-file $< --output-file $@ -check_iolog_path.o: $(srcdir)/regress/iolog_path/check_iolog_path.c \ - $(devdir)/def_data.c $(devdir)/def_data.h \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ - $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h \ - $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ - $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ - $(top_builddir)/config.h $(top_builddir)/pathnames.h - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_path/check_iolog_path.c -check_iolog_path.i: $(srcdir)/regress/iolog_path/check_iolog_path.c \ - $(devdir)/def_data.c $(devdir)/def_data.h \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ - $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h \ - $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ - $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ - $(top_builddir)/config.h $(top_builddir)/pathnames.h - $(CC) -E -o $@ $(CPPFLAGS) $< -check_iolog_path.plog: check_iolog_path.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_path/check_iolog_path.c --i-file $< --output-file $@ check_iolog_plugin.o: $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c \ $(devdir)/def_data.c $(devdir)/def_data.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h \ - $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ - $(srcdir)/logging.h $(srcdir)/parse.h \ - $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ - $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h + $(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ + $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c check_iolog_plugin.i: $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c \ $(devdir)/def_data.c $(devdir)/def_data.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h \ - $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ - $(srcdir)/logging.h $(srcdir)/parse.h \ - $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ - $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h + $(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ + $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< check_iolog_plugin.plog: check_iolog_plugin.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c --i-file $< --output-file $@ -check_iolog_util.o: $(srcdir)/regress/iolog_util/check_iolog_util.c \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ - $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ - $(top_builddir)/config.h - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_util/check_iolog_util.c -check_iolog_util.i: $(srcdir)/regress/iolog_util/check_iolog_util.c \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ - $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ - $(top_builddir)/config.h - $(CC) -E -o $@ $(CPPFLAGS) $< -check_iolog_util.plog: check_iolog_util.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_util/check_iolog_util.c --i-file $< --output-file $@ check_starttime.o: $(srcdir)/regress/starttime/check_starttime.c \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ @@ -1550,8 +1496,8 @@ interfaces.plog: interfaces.i iolog.lo: $(srcdir)/iolog.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/defaults.h $(srcdir)/iolog.h $(srcdir)/iolog_files.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/defaults.h \ $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h @@ -1559,50 +1505,38 @@ iolog.lo: $(srcdir)/iolog.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ iolog.i: $(srcdir)/iolog.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/defaults.h $(srcdir)/iolog.h $(srcdir)/iolog_files.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/defaults.h \ $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< iolog.plog: iolog.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog.c --i-file $< --output-file $@ -iolog_path.lo: $(srcdir)/iolog_path.c $(devdir)/def_data.h \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ - $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h $(srcdir)/logging.h \ - $(srcdir)/parse.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ - $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h - $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_path.c -iolog_path.i: $(srcdir)/iolog_path.c $(devdir)/def_data.h \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ - $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ - $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h $(srcdir)/logging.h \ - $(srcdir)/parse.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ - $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h +iolog_path_escapes.lo: $(srcdir)/iolog_path_escapes.c $(devdir)/def_data.h \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ + $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_path_escapes.c +iolog_path_escapes.i: $(srcdir)/iolog_path_escapes.c $(devdir)/def_data.h \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ + $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< -iolog_path.plog: iolog_path.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_path.c --i-file $< --output-file $@ -iolog_util.o: $(srcdir)/iolog_util.c $(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)/iolog.h \ - $(srcdir)/iolog_util.h $(top_builddir)/config.h - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_util.c -iolog_util.i: $(srcdir)/iolog_util.c $(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)/iolog.h \ - $(srcdir)/iolog_util.h $(top_builddir)/config.h - $(CC) -E -o $@ $(CPPFLAGS) $< -iolog_util.plog: iolog_util.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_util.c --i-file $< --output-file $@ +iolog_path_escapes.plog: iolog_path_escapes.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_path_escapes.c --i-file $< --output-file $@ kerb5.lo: $(authdir)/kerb5.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ @@ -2365,22 +2299,22 @@ sudoers.lo: $(srcdir)/sudoers.c $(devdir)/def_data.h \ $(incdir)/compat/getaddrinfo.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/auth/sudo_auth.h $(srcdir)/defaults.h \ - $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ - $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/auth/sudo_auth.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sudoers.c sudoers.i: $(srcdir)/sudoers.c $(devdir)/def_data.h \ $(incdir)/compat/getaddrinfo.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/auth/sudo_auth.h $(srcdir)/defaults.h \ - $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ - $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/auth/sudo_auth.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< sudoers.plog: sudoers.i @@ -2411,21 +2345,19 @@ sudoreplay.o: $(srcdir)/sudoreplay.c $(incdir)/compat/getopt.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/iolog_files.h $(srcdir)/iolog_util.h \ - $(srcdir)/logging.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h + $(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/logging.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sudoreplay.c sudoreplay.i: $(srcdir)/sudoreplay.c $(incdir)/compat/getopt.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/iolog_files.h $(srcdir)/iolog_util.h \ - $(srcdir)/logging.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h + $(incdir)/sudo_gettext.h $(incdir)/sudo_iolog.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/logging.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< sudoreplay.plog: sudoreplay.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sudoreplay.c --i-file $< --output-file $@ diff --git a/plugins/sudoers/def_data.c b/plugins/sudoers/def_data.c index 539cd30ee..9d1bb86f5 100644 --- a/plugins/sudoers/def_data.c +++ b/plugins/sudoers/def_data.c @@ -402,8 +402,8 @@ struct sudo_defs_types sudo_defs_table[] = { N_("Perform PAM account validation management"), NULL, }, { - "maxseq", T_UINT, - N_("Maximum I/O log sequence number: %u"), + "maxseq", T_STR, + N_("Maximum I/O log sequence number: %s"), NULL, }, { "use_netgroups", T_FLAG, diff --git a/plugins/sudoers/def_data.h b/plugins/sudoers/def_data.h index 16cafc975..d8393bbbd 100644 --- a/plugins/sudoers/def_data.h +++ b/plugins/sudoers/def_data.h @@ -181,7 +181,7 @@ #define I_PAM_ACCT_MGMT 90 #define def_pam_acct_mgmt (sudo_defs_table[I_PAM_ACCT_MGMT].sd_un.flag) #define I_MAXSEQ 91 -#define def_maxseq (sudo_defs_table[I_MAXSEQ].sd_un.uival) +#define def_maxseq (sudo_defs_table[I_MAXSEQ].sd_un.str) #define I_USE_NETGROUPS 92 #define def_use_netgroups (sudo_defs_table[I_USE_NETGROUPS].sd_un.flag) #define I_SUDOEDIT_CHECKDIR 93 diff --git a/plugins/sudoers/def_data.in b/plugins/sudoers/def_data.in index 3c71e9848..cfd5ebb7c 100644 --- a/plugins/sudoers/def_data.in +++ b/plugins/sudoers/def_data.in @@ -287,8 +287,8 @@ pam_acct_mgmt T_FLAG "Perform PAM account validation management" maxseq - T_UINT - "Maximum I/O log sequence number: %u" + T_STR + "Maximum I/O log sequence number: %s" use_netgroups T_FLAG "Enable sudoers netgroup support" diff --git a/plugins/sudoers/iolog.c b/plugins/sudoers/iolog.c index cae253817..27be548c7 100644 --- a/plugins/sudoers/iolog.c +++ b/plugins/sudoers/iolog.c @@ -42,8 +42,7 @@ #include #include "sudoers.h" -#include "iolog.h" -#include "iolog_files.h" +#include "sudo_iolog.h" /* XXX - separate sudoers.h and iolog.h? */ #undef runas_pw @@ -62,206 +61,29 @@ struct iolog_details { bool ignore_iolog_errors; }; +static struct iolog_file iolog_files[] = { + { false }, /* IOFD_STDIN */ + { false }, /* IOFD_STDOUT */ + { false }, /* IOFD_STDERR */ + { false }, /* IOFD_TTYIN */ + { false }, /* IOFD_TTYOUT */ + { true, }, /* IOFD_TIMING */ +}; + static struct iolog_details iolog_details; -static bool iolog_compress = false; static bool warned = false; static struct timespec last_time; -static unsigned int sessid_max = SESSID_MAX; -static mode_t iolog_filemode = S_IRUSR|S_IWUSR; -static mode_t iolog_dirmode = S_IRWXU; -static bool iolog_gid_set; - -/* shared with set_perms.c */ -uid_t iolog_uid = ROOT_UID; -gid_t iolog_gid = ROOT_GID; /* sudoers_io is declared at the end of this file. */ extern __dso_public struct io_plugin sudoers_io; -/* - * Create directory and any parent directories as needed. - */ -static bool -io_mkdirs(char *path) -{ - struct stat sb; - bool ok, uid_changed = false; - debug_decl(io_mkdirs, SUDOERS_DEBUG_UTIL) - - ok = stat(path, &sb) == 0; - if (!ok && errno == EACCES) { - /* Try again as the I/O log owner (for NFS). */ - if (set_perms(PERM_IOLOG)) { - ok = stat(path, &sb) == 0; - if (!restore_perms()) - ok = false; - } - } - if (ok) { - if (S_ISDIR(sb.st_mode)) { - if (sb.st_uid != iolog_uid || sb.st_gid != iolog_gid) { - if (chown(path, iolog_uid, iolog_gid) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to chown %d:%d %s", __func__, - (int)iolog_uid, (int)iolog_gid, path); - } - } - if ((sb.st_mode & ALLPERMS) != iolog_dirmode) { - if (chmod(path, iolog_dirmode) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to chmod 0%o %s", __func__, - (int)iolog_dirmode, path); - } - } - } else { - sudo_warnx(U_("%s exists but is not a directory (0%o)"), - path, (unsigned int) sb.st_mode); - ok = false; - } - goto done; - } - - ok = sudo_mkdir_parents(path, iolog_uid, iolog_gid, iolog_dirmode, true); - if (!ok && errno == EACCES) { - /* Try again as the I/O log owner (for NFS). */ - uid_changed = set_perms(PERM_IOLOG); - ok = sudo_mkdir_parents(path, -1, -1, iolog_dirmode, false); - } - if (ok) { - /* Create final path component. */ - sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, - "mkdir %s, mode 0%o", path, (unsigned int) iolog_dirmode); - ok = mkdir(path, iolog_dirmode) == 0 || errno == EEXIST; - if (!ok) { - if (errno == EACCES && !uid_changed) { - /* Try again as the I/O log owner (for NFS). */ - uid_changed = set_perms(PERM_IOLOG); - ok = mkdir(path, iolog_dirmode) == 0 || errno == EEXIST; - } - if (!ok) - sudo_warn(U_("unable to mkdir %s"), path); - } else { - if (chown(path, iolog_uid, iolog_gid) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to chown %d:%d %s", __func__, - (int)iolog_uid, (int)iolog_gid, path); - } - } - } - if (uid_changed) { - if (!restore_perms()) - ok = false; - } -done: - debug_return_bool(ok); -} - -/* - * Create temporary directory and any parent directories as needed. - */ -static bool -io_mkdtemp(char *path) -{ - bool ok, uid_changed = false; - debug_decl(io_mkdtemp, SUDOERS_DEBUG_UTIL) - - ok = sudo_mkdir_parents(path, iolog_uid, iolog_gid, iolog_dirmode, true); - if (!ok && errno == EACCES) { - /* Try again as the I/O log owner (for NFS). */ - uid_changed = set_perms(PERM_IOLOG); - ok = sudo_mkdir_parents(path, -1, -1, iolog_dirmode, false); - } - if (ok) { - /* Create final path component. */ - sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, - "mkdtemp %s", path); - /* We cannot retry mkdtemp() so always use PERM_IOLOG */ - if (!uid_changed) - uid_changed = set_perms(PERM_IOLOG); - if (mkdtemp(path) == NULL) { - sudo_warn(U_("unable to mkdir %s"), path); - ok = false; - } else { - if (chmod(path, iolog_dirmode) != 0) { - sudo_warn(U_("unable to change mode of %s to 0%o"), - path, (unsigned int)iolog_dirmode); - } - } - } - - if (uid_changed) { - if (!restore_perms()) - ok = false; - } - debug_return_bool(ok); -} - -/* - * Set max session ID (aka sequence number) - */ -static bool -io_set_max_sessid(const char *maxval) -{ - const char *errstr; - unsigned int value; - debug_decl(io_set_max_sessid, SUDOERS_DEBUG_UTIL) - - value = sudo_strtonum(maxval, 0, SESSID_MAX, &errstr); - if (errstr != NULL) { - if (errno != ERANGE) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "bad maxseq: %s: %s", maxval, errstr); - debug_return_bool(false); - } - /* Out of range, clamp to SESSID_MAX as documented. */ - value = SESSID_MAX; - } - sessid_max = value; - debug_return_bool(true); -} - /* * Sudoers callback for maxseq Defaults setting. */ bool cb_maxseq(const union sudo_defs_val *sd_un) { - debug_decl(cb_maxseq, SUDOERS_DEBUG_UTIL) - - /* Clamp value to SESSID_MAX as documented. */ - sessid_max = sd_un->uival < SESSID_MAX ? sd_un->uival : SESSID_MAX; - debug_return_bool(true); -} - -/* - * Look up I/O log user-ID from user name. Sets iolog_uid. - * Also sets iolog_gid if iolog_group not specified. - */ -static bool -iolog_set_user(const char *name) -{ - struct passwd *pw; - debug_decl(iolog_set_user, SUDOERS_DEBUG_UTIL) - - if (name != NULL) { - pw = sudo_getpwnam(name); - if (pw != NULL) { - iolog_uid = pw->pw_uid; - if (!iolog_gid_set) - iolog_gid = pw->pw_gid; - sudo_pw_delref(pw); - } else { - log_warningx(SLOG_SEND_MAIL, - N_("unknown user: %s"), name); - } - } else { - /* Reset to default. */ - iolog_uid = ROOT_UID; - if (!iolog_gid_set) - iolog_gid = ROOT_GID; - } - - debug_return_bool(true); + return iolog_set_maxseq(sd_un->str); } /* @@ -270,36 +92,23 @@ iolog_set_user(const char *name) bool cb_iolog_user(const union sudo_defs_val *sd_un) { - return iolog_set_user(sd_un->str); -} - -/* - * Look up I/O log group-ID from group name. - * Sets iolog_gid. - */ -static bool -iolog_set_group(const char *name) -{ - struct group *gr; - debug_decl(iolog_set_group, SUDOERS_DEBUG_UTIL) + const char *name = sd_un->str; + struct passwd *pw = NULL; + bool ret; + debug_decl(cb_iolog_user, SUDOERS_DEBUG_UTIL) + /* NULL name means reset to default. */ if (name != NULL) { - gr = sudo_getgrnam(name); - if (gr != NULL) { - iolog_gid = gr->gr_gid; - iolog_gid_set = true; - sudo_gr_delref(gr); - } else { - log_warningx(SLOG_SEND_MAIL, - N_("unknown group: %s"), name); + if ((pw = sudo_getpwnam(name)) == NULL) { + log_warningx(SLOG_SEND_MAIL, N_("unknown user: %s"), name); + debug_return_bool(false); } - } else { - /* Reset to default. */ - iolog_gid = ROOT_GID; - iolog_gid_set = false; } + ret = iolog_set_user(pw); + if (pw != NULL) + sudo_pw_delref(pw); - debug_return_bool(true); + debug_return_bool(ret); } /* @@ -308,31 +117,23 @@ iolog_set_group(const char *name) bool cb_iolog_group(const union sudo_defs_val *sd_un) { - return iolog_set_group(sd_un->str); -} + const char *name = sd_un->str; + struct group *gr = NULL; + bool ret; + debug_decl(cb_iolog_group, SUDOERS_DEBUG_UTIL) -/* - * Set iolog_filemode and iolog_dirmode. - */ -static bool -iolog_set_mode(mode_t mode) -{ - debug_decl(iolog_set_mode, SUDOERS_DEBUG_UTIL) + /* NULL name means reset to default. */ + if (name != NULL) { + if ((gr = sudo_getgrnam(name)) == NULL) { + log_warningx(SLOG_SEND_MAIL, N_("unknown group: %s"), name); + debug_return_bool(false); + } + } + ret = iolog_set_group(gr); + if (gr != NULL) + sudo_gr_delref(gr); - /* I/O log files must be readable and writable by owner. */ - iolog_filemode = S_IRUSR|S_IWUSR; - - /* Add in group and other read/write if specified. */ - iolog_filemode |= mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); - - /* For directory mode, add execute bits as needed. */ - iolog_dirmode = iolog_filemode | S_IXUSR; - if (iolog_dirmode & (S_IRGRP|S_IWGRP)) - iolog_dirmode |= S_IXGRP; - if (iolog_dirmode & (S_IROTH|S_IWOTH)) - iolog_dirmode |= S_IXOTH; - - debug_return_bool(true); + debug_return_bool(ret); } /* @@ -344,248 +145,6 @@ cb_iolog_mode(const union sudo_defs_val *sd_un) return iolog_set_mode(sd_un->mode); } -/* - * Wrapper for open(2) that retries with PERM_IOLOG if open(2) - * returns EACCES. - */ -static int -io_open(const char *path, int flags, mode_t perm) -{ - int fd; - debug_decl(io_open, SUDOERS_DEBUG_UTIL) - - fd = open(path, flags, perm); - if (fd == -1 && errno == EACCES) { - /* Try again as the I/O log owner (for NFS). */ - if (set_perms(PERM_IOLOG)) { - fd = open(path, flags, perm); - if (!restore_perms()) { - /* restore_perms() warns on error. */ - if (fd != -1) { - close(fd); - fd = -1; - } - } - } - } - debug_return_int(fd); -} - -/* - * Read the on-disk sequence number, set sessid to the next - * number, and update the on-disk copy. - * Uses file locking to avoid sequence number collisions. - */ -bool -io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7]) -{ - struct stat sb; - char buf[32], *ep; - int i, len, fd = -1; - unsigned long id = 0; - mode_t omask; - ssize_t nread; - bool ret = false; - char pathbuf[PATH_MAX]; - static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - debug_decl(io_nextid, SUDOERS_DEBUG_UTIL) - - /* umask must not be more restrictive than the file modes. */ - omask = umask(ACCESSPERMS & ~(iolog_filemode|iolog_dirmode)); - - /* - * Create I/O log directory if it doesn't already exist. - */ - if (!io_mkdirs(iolog_dir)) - goto done; - - /* - * Open sequence file - */ - len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir); - if (len < 0 || len >= ssizeof(pathbuf)) { - errno = ENAMETOOLONG; - log_warning(SLOG_SEND_MAIL, "%s/seq", pathbuf); - goto done; - } - fd = io_open(pathbuf, O_RDWR|O_CREAT, iolog_filemode); - if (fd == -1) { - log_warning(SLOG_SEND_MAIL, N_("unable to open %s"), pathbuf); - goto done; - } - sudo_lock_file(fd, SUDO_LOCK); - if (fchown(fd, iolog_uid, iolog_gid) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to fchown %d:%d %s", __func__, - (int)iolog_uid, (int)iolog_gid, pathbuf); - } - - /* - * If there is no seq file in iolog_dir and a fallback dir was - * specified, look for seq in the fallback dir. This is to work - * around a bug in sudo 1.8.5 and older where iolog_dir was not - * expanded before the sequence number was updated. - */ - if (iolog_dir_fallback != NULL && fstat(fd, &sb) == 0 && sb.st_size == 0) { - char fallback[PATH_MAX]; - - len = snprintf(fallback, sizeof(fallback), "%s/seq", - iolog_dir_fallback); - if (len > 0 && len < ssizeof(fallback)) { - int fd2 = io_open(fallback, O_RDWR|O_CREAT, iolog_filemode); - if (fd2 != -1) { - if (fchown(fd2, iolog_uid, iolog_gid) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to fchown %d:%d %s", __func__, - (int)iolog_uid, (int)iolog_gid, fallback); - } - nread = read(fd2, buf, sizeof(buf) - 1); - if (nread > 0) { - if (buf[nread - 1] == '\n') - nread--; - buf[nread] = '\0'; - id = strtoul(buf, &ep, 36); - if (ep == buf || *ep != '\0' || id >= sessid_max) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "%s: bad sequence number: %s", fallback, buf); - id = 0; - } - } - close(fd2); - } - } - } - - /* Read current seq number (base 36). */ - if (id == 0) { - nread = read(fd, buf, sizeof(buf) - 1); - if (nread != 0) { - if (nread == -1) { - log_warning(SLOG_SEND_MAIL, N_("unable to read %s"), pathbuf); - goto done; - } - if (buf[nread - 1] == '\n') - nread--; - buf[nread] = '\0'; - id = strtoul(buf, &ep, 36); - if (ep == buf || *ep != '\0' || id >= sessid_max) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "%s: bad sequence number: %s", pathbuf, buf); - id = 0; - } - } - } - id++; - - /* - * Convert id to a string and stash in sessid. - * Note that that least significant digits go at the end of the string. - */ - for (i = 5; i >= 0; i--) { - buf[i] = b36char[id % 36]; - id /= 36; - } - buf[6] = '\n'; - - /* Stash id for logging purposes. */ - memcpy(sessid, buf, 6); - sessid[6] = '\0'; - - /* Rewind and overwrite old seq file, including the NUL byte. */ -#ifdef HAVE_PWRITE - if (pwrite(fd, buf, 7, 0) != 7) { -#else - if (lseek(fd, 0, SEEK_SET) == -1 || write(fd, buf, 7) != 7) { -#endif - log_warning(SLOG_SEND_MAIL, N_("unable to write to %s"), pathbuf); - warned = true; - goto done; - } - ret = true; - -done: - umask(omask); - if (fd != -1) - close(fd); - debug_return_bool(ret); -} - -/* - * Copy iolog_path to pathbuf and create the directory and any intermediate - * directories. If iolog_path ends in 'XXXXXX', use mkdtemp(). - * Returns SIZE_MAX on error. - */ -static size_t -mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize) -{ - size_t len; - bool ok; - debug_decl(mkdir_iopath, SUDOERS_DEBUG_UTIL) - - len = strlcpy(pathbuf, iolog_path, pathsize); - if (len >= pathsize) { - errno = ENAMETOOLONG; - log_warning(SLOG_SEND_MAIL, "%s", iolog_path); - debug_return_size_t((size_t)-1); - } - - /* - * Create path and intermediate subdirs as needed. - * If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp(). - * Sets iolog_gid (if it is not already set) as a side effect. - */ - if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0) - ok = io_mkdtemp(pathbuf); - else - ok = io_mkdirs(pathbuf); - - debug_return_size_t(ok ? len : (size_t)-1); -} - -/* - * Append suffix to pathbuf after len chars and open the resulting file. - * Note that the size of pathbuf is assumed to be PATH_MAX. - * Uses zlib if docompress is true. - * Stores the open file handle which has the close-on-exec flag set. - */ -static bool -open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress) -{ - debug_decl(open_io_fd, SUDOERS_DEBUG_UTIL) - - pathbuf[len] = '\0'; - strlcat(pathbuf, iol->suffix, PATH_MAX); - if (iol->enabled) { - int fd = io_open(pathbuf, O_CREAT|O_TRUNC|O_WRONLY, iolog_filemode); - if (fd != -1) { - if (fchown(fd, iolog_uid, iolog_gid) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to fchown %d:%d %s", __func__, - (int)iolog_uid, (int)iolog_gid, pathbuf); - } - (void)fcntl(fd, F_SETFD, FD_CLOEXEC); -#ifdef HAVE_ZLIB_H - if (docompress) - iol->fd.g = gzdopen(fd, "w"); - else -#endif - iol->fd.f = fdopen(fd, "w"); - if (iol->fd.v == NULL) { - close(fd); - fd = -1; - } - } - if (fd == -1) { - log_warning(SLOG_SEND_MAIL, N_("unable to create %s"), pathbuf); - debug_return_bool(false); - } - } else { - /* Remove old log file if we recycled sequence numbers. */ - unlink(pathbuf); - } - debug_return_bool(true); -} - /* * Pull out I/O log related data from user_info and command_info arrays. * Returns true if I/O logging is enabled, else false. @@ -666,52 +225,81 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[], } if (strncmp(*cur, "iolog_stdin=", sizeof("iolog_stdin=") - 1) == 0) { if (sudo_strtobool(*cur + sizeof("iolog_stdin=") - 1) == true) - io_log_files[IOFD_STDIN].enabled = true; + iolog_files[IOFD_STDIN].enabled = true; continue; } if (strncmp(*cur, "iolog_stdout=", sizeof("iolog_stdout=") - 1) == 0) { if (sudo_strtobool(*cur + sizeof("iolog_stdout=") - 1) == true) - io_log_files[IOFD_STDOUT].enabled = true; + iolog_files[IOFD_STDOUT].enabled = true; continue; } if (strncmp(*cur, "iolog_stderr=", sizeof("iolog_stderr=") - 1) == 0) { if (sudo_strtobool(*cur + sizeof("iolog_stderr=") - 1) == true) - io_log_files[IOFD_STDERR].enabled = true; + iolog_files[IOFD_STDERR].enabled = true; continue; } if (strncmp(*cur, "iolog_ttyin=", sizeof("iolog_ttyin=") - 1) == 0) { if (sudo_strtobool(*cur + sizeof("iolog_ttyin=") - 1) == true) - io_log_files[IOFD_TTYIN].enabled = true; + iolog_files[IOFD_TTYIN].enabled = true; continue; } if (strncmp(*cur, "iolog_ttyout=", sizeof("iolog_ttyout=") - 1) == 0) { if (sudo_strtobool(*cur + sizeof("iolog_ttyout=") - 1) == true) - io_log_files[IOFD_TTYOUT].enabled = true; + iolog_files[IOFD_TTYOUT].enabled = true; continue; } if (strncmp(*cur, "iolog_compress=", sizeof("iolog_compress=") - 1) == 0) { - if (sudo_strtobool(*cur + sizeof("iolog_compress=") - 1) == true) - iolog_compress = true; /* must be global */ + if (!iolog_set_compress(*cur + sizeof("iolog_compress=") - 1)) { + sudo_debug_printf(SUDO_DEBUG_WARN, + "%s: unable to parse %s", __func__, *cur); + } + continue; + } + if (strncmp(*cur, "iolog_flush=", sizeof("iolog_flush=") - 1) == 0) { + if (!iolog_set_flush(*cur + sizeof("iolog_flush=") - 1)) { + sudo_debug_printf(SUDO_DEBUG_WARN, + "%s: unable to parse %s", __func__, *cur); + } continue; } if (strncmp(*cur, "iolog_mode=", sizeof("iolog_mode=") - 1) == 0) { mode_t mode = sudo_strtomode(*cur + sizeof("iolog_mode=") - 1, &errstr); - if (errstr == NULL) + if (errstr == NULL) { iolog_set_mode(mode); + } else { + sudo_debug_printf(SUDO_DEBUG_WARN, + "%s: unable to parse %s", __func__, *cur); + } continue; } if (strncmp(*cur, "iolog_group=", sizeof("iolog_group=") - 1) == 0) { - iolog_set_group(*cur + sizeof("iolog_group=") - 1); + struct group *gr = + sudo_getgrnam(*cur + sizeof("iolog_group=") - 1); + if (gr == NULL) { + sudo_debug_printf(SUDO_DEBUG_WARN, "%s: unknown group %s", + __func__, *cur + sizeof("iolog_group=") - 1); + } else { + iolog_set_group(gr); + sudo_gr_delref(gr); + } continue; } if (strncmp(*cur, "iolog_user=", sizeof("iolog_user=") - 1) == 0) { - iolog_set_user(*cur + sizeof("iolog_user=") - 1); + struct passwd *pw = + sudo_getpwnam(*cur + sizeof("iolog_user=") - 1); + if (pw == NULL) { + sudo_debug_printf(SUDO_DEBUG_WARN, "%s: unknown user %s", + __func__, *cur + sizeof("iolog_user=") - 1); + } else { + iolog_set_user(pw); + sudo_pw_delref(pw); + } continue; } break; case 'm': if (strncmp(*cur, "maxseq=", sizeof("maxseq=") - 1) == 0) { - io_set_max_sessid(*cur + sizeof("maxseq=") - 1); + iolog_set_maxseq(*cur + sizeof("maxseq=") - 1); continue; } break; @@ -774,9 +362,9 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[], } } debug_return_bool( - io_log_files[IOFD_STDIN].enabled || io_log_files[IOFD_STDOUT].enabled || - io_log_files[IOFD_STDERR].enabled || io_log_files[IOFD_TTYIN].enabled || - io_log_files[IOFD_TTYOUT].enabled); + iolog_files[IOFD_STDIN].enabled || iolog_files[IOFD_STDOUT].enabled || + iolog_files[IOFD_STDERR].enabled || iolog_files[IOFD_TTYIN].enabled || + iolog_files[IOFD_TTYOUT].enabled); } /* @@ -787,97 +375,28 @@ static bool write_info_log(char *pathbuf, size_t len, struct iolog_details *details, char * const argv[]) { - time_t now; - char * const *av; - FILE *fp; - int fd; - bool ret = true; + struct iolog_info iolog_info; debug_decl(write_info_log, SUDOERS_DEBUG_UTIL) + /* XXX - just use iolog_info in the first place? */ + time(&iolog_info.tstamp); + iolog_info.user = (char *)details->user; + iolog_info.runas_user = details->runas_pw->pw_name; + iolog_info.runas_group = details->runas_gr ? details->runas_gr->gr_name: NULL; + iolog_info.tty = (char *)details->tty; + iolog_info.cwd = (char *)details->cwd; + iolog_info.cmd = (char *)details->command; + iolog_info.lines = details->lines; + iolog_info.cols = details->cols; pathbuf[len] = '\0'; - strlcat(pathbuf, "/log", PATH_MAX); - fd = io_open(pathbuf, O_CREAT|O_TRUNC|O_WRONLY, iolog_filemode); - if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) { - log_warning(SLOG_SEND_MAIL, N_("unable to create %s"), pathbuf); - debug_return_bool(false); - } - if (fchown(fd, iolog_uid, iolog_gid) != 0) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to fchown %d:%d %s", __func__, - (int)iolog_uid, (int)iolog_gid, pathbuf); - } - fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n%s", (long long)time(&now), - details->user ? details->user : "unknown", details->runas_pw->pw_name, - details->runas_gr ? details->runas_gr->gr_name : "", - details->tty ? details->tty : "unknown", details->lines, details->cols, - details->cwd ? details->cwd : "unknown", - details->command ? details->command : "unknown"); - for (av = argv + 1; *av != NULL; av++) { - fputc(' ', fp); - fputs(*av, fp); - } - fputc('\n', fp); - fflush(fp); - if (ferror(fp)) { + if (!iolog_write_info_file(pathbuf, &iolog_info, argv)) { log_warning(SLOG_SEND_MAIL, N_("unable to write to I/O log file: %s"), strerror(errno)); warned = true; - ret = false; + debug_return_bool(false); } - fclose(fp); - debug_return_bool(ret); -} - -#ifdef HAVE_ZLIB_H -static const char * -gzstrerror(gzFile file) -{ - int errnum; - - return gzerror(file, &errnum); -} -#endif /* HAVE_ZLIB_H */ - -/* - * Write to an I/O log, compressing if iolog_compress is enabled. - * If def_iolog_flush is true, flush the buffer immediately. - */ -static const char * -iolog_write(union io_fd ifd, const void *buf, unsigned int len) -{ - const char *errstr = NULL; - debug_decl(iolog_write, SUDOERS_DEBUG_PLUGIN) - -#ifdef HAVE_ZLIB_H - if (iolog_compress) { - if (gzwrite(ifd.g, (const voidp)buf, len) != (int)len) { - errstr = gzstrerror(ifd.g); - goto done; - } - if (def_iolog_flush) { - if (gzflush(ifd.g, Z_SYNC_FLUSH) != Z_OK) { - errstr = gzstrerror(ifd.g); - goto done; - } - } - } else -#endif - { - if (fwrite(buf, 1, len, ifd.f) != len) { - errstr = strerror(errno); - goto done; - } - if (def_iolog_flush) { - if (fflush(ifd.f) != 0) { - errstr = strerror(errno); - goto done; - } - } - } - -done: - debug_return_const_str(errstr); + debug_return_bool(true); } static int @@ -892,7 +411,6 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, char * const *cur; const char *cp, *plugin_path = NULL; size_t len; - mode_t omask; int i, ret = -1; debug_decl(sudoers_io_open, SUDOERS_DEBUG_PLUGIN) @@ -919,9 +437,6 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, } } - /* umask must not be more restrictive than the file modes. */ - omask = umask(ACCESSPERMS & ~(iolog_filemode|iolog_dirmode)); - if (!sudoers_debug_register(plugin_path, &debug_files)) { ret = -1; goto done; @@ -944,7 +459,8 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, goto done; } memcpy(tofree, _PATH_SUDO_IO_LOGDIR, sizeof(_PATH_SUDO_IO_LOGDIR)); - if (!io_nextid(tofree, NULL, sessid)) { + if (!iolog_nextid(tofree, sessid)) { + log_warning(SLOG_SEND_MAIL, N_("unable to update sequence file")); ret = false; goto done; } @@ -959,8 +475,10 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, * intermediate subdirs. Calls mkdtemp() if iolog_path ends in XXXXXX. */ len = mkdir_iopath(iolog_details.iolog_path, pathbuf, sizeof(pathbuf)); - if (len >= sizeof(pathbuf)) + if (len >= sizeof(pathbuf)) { + log_warning(SLOG_SEND_MAIL, "%s", iolog_details.iolog_path); goto done; + } /* Write log file with user and command details. */ if (!write_info_log(pathbuf, len, &iolog_details, argv)) @@ -968,22 +486,31 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, /* Create the timing and I/O log files. */ for (i = 0; i < IOFD_MAX; i++) { - if (!open_io_fd(pathbuf, len, &io_log_files[i], iolog_compress)) + pathbuf[len] = '/'; + pathbuf[len + 1] = '\0'; + if (strlcat(pathbuf, iolog_fd_to_name(i), sizeof(pathbuf)) >= sizeof(pathbuf)) { + errno = ENAMETOOLONG; + log_warning(SLOG_SEND_MAIL, N_("unable to create %s"), pathbuf); goto done; + } + if (!iolog_open(&iolog_files[i], pathbuf, "w")) { + log_warning(SLOG_SEND_MAIL, N_("unable to create %s"), pathbuf); + goto done; + } } /* * Clear I/O log function pointers for disabled log functions. */ - if (!io_log_files[IOFD_STDIN].enabled) + if (!iolog_files[IOFD_STDIN].enabled) sudoers_io.log_stdin = NULL; - if (!io_log_files[IOFD_STDOUT].enabled) + if (!iolog_files[IOFD_STDOUT].enabled) sudoers_io.log_stdout = NULL; - if (!io_log_files[IOFD_STDERR].enabled) + if (!iolog_files[IOFD_STDERR].enabled) sudoers_io.log_stderr = NULL; - if (!io_log_files[IOFD_TTYIN].enabled) + if (!iolog_files[IOFD_TTYIN].enabled) sudoers_io.log_ttyin = NULL; - if (!io_log_files[IOFD_TTYOUT].enabled) + if (!iolog_files[IOFD_TTYOUT].enabled) sudoers_io.log_ttyout = NULL; if (sudo_gettime_awake(&last_time) == -1) { @@ -995,7 +522,6 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, ret = true; done: - umask(omask); free(tofree); if (iolog_details.runas_pw) sudo_pw_delref(iolog_details.runas_pw); @@ -1019,18 +545,9 @@ sudoers_io_close(int exit_status, int error) debug_decl(sudoers_io_close, SUDOERS_DEBUG_PLUGIN) for (i = 0; i < IOFD_MAX; i++) { - if (io_log_files[i].fd.v == NULL) + if (iolog_files[i].fd.v == NULL) continue; -#ifdef HAVE_ZLIB_H - if (iolog_compress) { - int errnum; - - if (gzclose(io_log_files[i].fd.g) != Z_OK) - errstr = gzerror(io_log_files[i].fd.g, &errnum); - } else -#endif - if (fclose(io_log_files[i].fd.f) != 0) - errstr = strerror(errno); + iolog_close(&iolog_files[i], &errstr); } if (errstr != NULL && !warned) { @@ -1061,7 +578,8 @@ sudoers_io_version(int verbose) * Returns 1 on success and -1 on error. */ static int -sudoers_io_log(union io_fd ifd, const char *buf, unsigned int len, int event) +sudoers_io_log(struct iolog_file *iol, const char *buf, unsigned int len, + int event) { struct timespec now, delay; char tbuf[1024]; @@ -1069,7 +587,7 @@ sudoers_io_log(union io_fd ifd, const char *buf, unsigned int len, int event) int ret = -1; debug_decl(sudoers_io_log, SUDOERS_DEBUG_PLUGIN) - if (ifd.v == NULL) { + if (!iol->enabled) { sudo_warnx(U_("%s: internal error, I/O log file for event %d not open"), __func__, event); debug_return_int(-1); @@ -1083,8 +601,7 @@ sudoers_io_log(union io_fd ifd, const char *buf, unsigned int len, int event) } /* Write I/O log file entry. */ - errstr = iolog_write(ifd, buf, len); - if (errstr != NULL) + if (iolog_write(iol, buf, len, &errstr) == -1) goto done; /* Write timing file entry. */ @@ -1096,8 +613,7 @@ sudoers_io_log(union io_fd ifd, const char *buf, unsigned int len, int event) errstr = strerror(EOVERFLOW); goto done; } - errstr = iolog_write(io_log_files[IOFD_TIMING].fd, tbuf, len); - if (errstr != NULL) + if (iolog_write(&iolog_files[IOFD_TIMING], tbuf, len, &errstr) == -1) goto done; /* Success. */ @@ -1127,41 +643,31 @@ bad: static int sudoers_io_log_stdin(const char *buf, unsigned int len) { - const union io_fd ifd = io_log_files[IOFD_STDIN].fd; - - return sudoers_io_log(ifd, buf, len, IO_EVENT_STDIN); + return sudoers_io_log(&iolog_files[IOFD_STDIN], buf, len, IO_EVENT_STDIN); } static int sudoers_io_log_stdout(const char *buf, unsigned int len) { - const union io_fd ifd = io_log_files[IOFD_STDOUT].fd; - - return sudoers_io_log(ifd, buf, len, IO_EVENT_STDOUT); + return sudoers_io_log(&iolog_files[IOFD_STDOUT], buf, len, IO_EVENT_STDOUT); } static int sudoers_io_log_stderr(const char *buf, unsigned int len) { - const union io_fd ifd = io_log_files[IOFD_STDERR].fd; - - return sudoers_io_log(ifd, buf, len, IO_EVENT_STDERR); + return sudoers_io_log(&iolog_files[IOFD_STDERR], buf, len, IO_EVENT_STDERR); } static int sudoers_io_log_ttyin(const char *buf, unsigned int len) { - const union io_fd ifd = io_log_files[IOFD_TTYIN].fd; - - return sudoers_io_log(ifd, buf, len, IO_EVENT_TTYIN); + return sudoers_io_log(&iolog_files[IOFD_TTYIN], buf, len, IO_EVENT_TTYIN); } static int sudoers_io_log_ttyout(const char *buf, unsigned int len) { - const union io_fd ifd = io_log_files[IOFD_TTYOUT].fd; - - return sudoers_io_log(ifd, buf, len, IO_EVENT_TTYOUT); + return sudoers_io_log(&iolog_files[IOFD_TTYOUT], buf, len, IO_EVENT_TTYOUT); } static int @@ -1190,8 +696,7 @@ sudoers_io_change_winsize(unsigned int lines, unsigned int cols) errstr = strerror(EOVERFLOW); goto done; } - errstr = iolog_write(io_log_files[IOFD_TIMING].fd, tbuf, len); - if (errstr != NULL) + if (iolog_write(&iolog_files[IOFD_TIMING], tbuf, len, &errstr) == -1) goto done; /* Success. */ @@ -1251,8 +756,7 @@ sudoers_io_suspend(int signo) errstr = strerror(EOVERFLOW); goto done; } - errstr = iolog_write(io_log_files[IOFD_TIMING].fd, tbuf, len); - if (errstr != NULL) + if (iolog_write(&iolog_files[IOFD_TIMING], tbuf, len, &errstr) == -1) goto done; /* Success. */ diff --git a/plugins/sudoers/iolog.h b/plugins/sudoers/iolog.h deleted file mode 100644 index 194516829..000000000 --- a/plugins/sudoers/iolog.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright (c) 2009-2019 Todd C. Miller - * - * 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. - */ - -#ifndef SUDOERS_IOLOG_H -#define SUDOERS_IOLOG_H - -#ifdef HAVE_ZLIB_H -# include /* for gzFile */ -#endif - -/* - * I/O log event types as stored as the first field in the timing file. - * Changing existing values will result in incompatible I/O log files. - */ -#define IO_EVENT_STDIN 0 -#define IO_EVENT_STDOUT 1 -#define IO_EVENT_STDERR 2 -#define IO_EVENT_TTYIN 3 -#define IO_EVENT_TTYOUT 4 -#define IO_EVENT_WINSIZE 5 -#define IO_EVENT_TTYOUT_1_8_7 6 -#define IO_EVENT_SUSPEND 7 -#define IO_EVENT_COUNT 8 - -/* Default maximum session ID */ -#define SESSID_MAX 2176782336U - -union io_fd { - FILE *f; -#ifdef HAVE_ZLIB_H - gzFile g; -#endif - void *v; -}; - -#endif /* SUDOERS_IOLOG_H */ diff --git a/plugins/sudoers/iolog_files.h b/plugins/sudoers/iolog_files.h deleted file mode 100644 index b564fa607..000000000 --- a/plugins/sudoers/iolog_files.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright (c) 2013 Todd C. Miller - * - * 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. - */ - -#ifndef SUDOERS_IOLOG_FILES_H -#define SUDOERS_IOLOG_FILES_H - -/* - * Indexes into io_log_files[] - */ -#define IOFD_STDIN 0 -#define IOFD_STDOUT 1 -#define IOFD_STDERR 2 -#define IOFD_TTYIN 3 -#define IOFD_TTYOUT 4 -#define IOFD_TIMING 5 -#define IOFD_MAX 6 - -struct io_log_file { - bool enabled; - const char *suffix; - union io_fd fd; -}; - -static struct io_log_file io_log_files[] = { - { false, "/stdin" }, /* IOFD_STDIN */ - { false, "/stdout" }, /* IOFD_STDOUT */ - { false, "/stderr" }, /* IOFD_STDERR */ - { false, "/ttyin" }, /* IOFD_TTYIN */ - { false, "/ttyout" }, /* IOFD_TTYOUT */ - { true, "/timing" }, /* IOFD_TIMING */ - { false, NULL } /* IOFD_MAX */ -}; - -#endif /* SUDOERS_IOLOG_H */ diff --git a/plugins/sudoers/iolog_path_escapes.c b/plugins/sudoers/iolog_path_escapes.c new file mode 100644 index 000000000..c061c8070 --- /dev/null +++ b/plugins/sudoers/iolog_path_escapes.c @@ -0,0 +1,148 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2011-2015 Todd C. Miller + * + * 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. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include + +#include +#include +#include +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#include +#include +#include +#include +#include + +#include "sudoers.h" +#include "sudo_iolog.h" + +static size_t +fill_seq(char *str, size_t strsize, char *logdir) +{ +#ifdef SUDOERS_NO_SEQ + debug_decl(fill_seq, SUDO_DEBUG_UTIL) + debug_return_size_t(strlcpy(str, "%{seq}", strsize)); +#else + static char sessid[7]; + int len; + debug_decl(fill_seq, SUDO_DEBUG_UTIL) + + if (sessid[0] == '\0') { + if (!iolog_nextid(logdir, sessid)) + debug_return_size_t((size_t)-1); + } + + /* Path is of the form /var/log/sudo-io/00/00/01. */ + len = snprintf(str, strsize, "%c%c/%c%c/%c%c", sessid[0], + sessid[1], sessid[2], sessid[3], sessid[4], sessid[5]); + if (len < 0) + debug_return_size_t(strsize); /* handle non-standard snprintf() */ + debug_return_size_t(len); +#endif /* SUDOERS_NO_SEQ */ +} + +static size_t +fill_user(char *str, size_t strsize, char *unused) +{ + debug_decl(fill_user, SUDO_DEBUG_UTIL) + debug_return_size_t(strlcpy(str, user_name, strsize)); +} + +static size_t +fill_group(char *str, size_t strsize, char *unused) +{ + struct group *grp; + size_t len; + debug_decl(fill_group, SUDO_DEBUG_UTIL) + + if ((grp = sudo_getgrgid(user_gid)) != NULL) { + len = strlcpy(str, grp->gr_name, strsize); + sudo_gr_delref(grp); + } else { + len = strlen(str); + len = snprintf(str + len, strsize - len, "#%u", + (unsigned int) user_gid); + } + debug_return_size_t(len); +} + +static size_t +fill_runas_user(char *str, size_t strsize, char *unused) +{ + debug_decl(fill_runas_user, SUDO_DEBUG_UTIL) + debug_return_size_t(strlcpy(str, runas_pw->pw_name, strsize)); +} + +static size_t +fill_runas_group(char *str, size_t strsize, char *unused) +{ + struct group *grp; + size_t len; + debug_decl(fill_runas_group, SUDO_DEBUG_UTIL) + + if (runas_gr != NULL) { + len = strlcpy(str, runas_gr->gr_name, strsize); + } else { + if ((grp = sudo_getgrgid(runas_pw->pw_gid)) != NULL) { + len = strlcpy(str, grp->gr_name, strsize); + sudo_gr_delref(grp); + } else { + len = strlen(str); + len = snprintf(str + len, strsize - len, "#%u", + (unsigned int) runas_pw->pw_gid); + } + } + debug_return_size_t(len); +} + +static size_t +fill_hostname(char *str, size_t strsize, char *unused) +{ + debug_decl(fill_hostname, SUDO_DEBUG_UTIL) + debug_return_size_t(strlcpy(str, user_shost, strsize)); +} + +static size_t +fill_command(char *str, size_t strsize, char *unused) +{ + debug_decl(fill_command, SUDO_DEBUG_UTIL) + debug_return_size_t(strlcpy(str, user_base, strsize)); +} + +/* Note: "seq" must be first in the list. */ +static const struct iolog_path_escape path_escapes[] = { + { "seq", fill_seq }, + { "user", fill_user }, + { "group", fill_group }, + { "runas_user", fill_runas_user }, + { "runas_group", fill_runas_group }, + { "hostname", fill_hostname }, + { "command", fill_command }, + { NULL, NULL } +}; +const struct iolog_path_escape *sudoers_iolog_path_escapes = path_escapes; diff --git a/plugins/sudoers/iolog_util.h b/plugins/sudoers/iolog_util.h deleted file mode 100644 index f9682be13..000000000 --- a/plugins/sudoers/iolog_util.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright (c) 2009-2019 Todd C. Miller - * - * 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. - */ - -#ifndef SUDOERS_IOLOG_READER_H -#define SUDOERS_IOLOG_READER_H - -#include "iolog.h" - -/* - * Info present in the I/O log file - */ -struct iolog_info { - char *cwd; - char *user; - char *runas_user; - char *runas_group; - char *tty; - char *cmd; - time_t tstamp; - int lines; - int cols; -}; - -struct timing_closure { - struct timespec delay; - const char *decimal; - union io_fd fd; - int event; - union { - struct { - int lines; - int cols; - } winsize; - size_t nbytes; // XXX - int signo; - } u; -}; - -/* iolog_reader.c */ -bool parse_timing(const char *line, struct timing_closure *timing); -char *parse_delay(const char *cp, struct timespec *delay, const char *decimal_point); -struct iolog_info *parse_logfile(const char *logfile); -void free_iolog_info(struct iolog_info *li); -void adjust_delay(struct timespec *delay, struct timespec *max_delay, double scale_factor); - -#endif /* SUDOERS_IOLOG_READER_H */ diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index 357ccb3a0..b56a9415f 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -550,8 +550,12 @@ sudoers_policy_exec_setup(char *argv[], char *envp[], mode_t cmnd_umask, if ((command_info[info_len++] = strdup("iolog_compress=true")) == NULL) goto oom; } - if (def_maxseq) { - if (asprintf(&command_info[info_len++], "maxseq=%u", def_maxseq) == -1) + if (def_iolog_flush) { + if ((command_info[info_len++] = strdup("iolog_flush=true")) == NULL) + goto oom; + } + if (def_maxseq != NULL) { + if (asprintf(&command_info[info_len++], "maxseq=%s", def_maxseq) == -1) goto oom; } } diff --git a/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c b/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c index 9ec02bcab..9a79c15ea 100644 --- a/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c +++ b/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2018 Todd C. Miller + * Copyright (c) 2018-2019 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -37,7 +37,7 @@ #include "sudoers.h" #include "def_data.c" /* for iolog_path.c */ #include "sudo_plugin.h" -#include "iolog_util.h" +#include "sudo_iolog.h" extern struct io_plugin sudoers_io; @@ -337,7 +337,7 @@ test_endpoints(int *ntests, int *nerrors, const char *iolog_dir, char *envp[]) int main(int argc, char *argv[], char *envp[]) { - struct passwd pw, rpw, *tpw; + struct passwd *tpw; int tests = 0, errors = 0; const char *iolog_dir; @@ -347,21 +347,20 @@ main(int argc, char *argv[], char *envp[]) usage(); iolog_dir = argv[1]; - /* Bare minimum to link. */ - memset(&pw, 0, sizeof(pw)); - memset(&rpw, 0, sizeof(rpw)); + /* Set runas user. */ if ((tpw = getpwuid(0)) == NULL) { if ((tpw = getpwnam("root")) == NULL) sudo_fatalx("unable to look up uid 0 or root"); } - rpw.pw_uid = tpw->pw_uid; - rpw.pw_gid = tpw->pw_gid; - sudo_user.pw = &pw; - sudo_user._runas_pw = &rpw; + sudo_user._runas_pw = pw_dup(tpw); + + /* Set invoking user. */ + if ((tpw = getpwuid(geteuid())) == NULL) + sudo_fatalx("unable to look up invoking user's uid"); + sudo_user.pw = pw_dup(tpw); /* Set iolog uid/gid to invoking user. */ - iolog_uid = geteuid(); - iolog_gid = getegid(); + iolog_set_user(sudo_user.pw); test_endpoints(&tests, &errors, iolog_dir, envp); diff --git a/plugins/sudoers/set_perms.c b/plugins/sudoers/set_perms.c index 528440fa2..d77e5b4e3 100644 --- a/plugins/sudoers/set_perms.c +++ b/plugins/sudoers/set_perms.c @@ -356,37 +356,6 @@ set_perms(int perm) goto bad; } break; - - case PERM_IOLOG: - state->gidlist = ostate->gidlist; - sudo_gidlist_addref(state->gidlist); - state->rgid = ostate->rgid; - state->egid = iolog_gid; - state->sgid = ostate->sgid; - state->ruid = ROOT_UID; - state->euid = iolog_uid; - state->suid = ROOT_UID; - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: gid: " - "[%d, %d, %d] -> [%d, %d, %d]", __func__, - (int)ostate->rgid, (int)ostate->egid, (int)ostate->sgid, - (int)state->rgid, (int)state->egid, (int)state->sgid); - if (GID_CHANGED && setresgid(ID(rgid), ID(egid), ID(sgid))) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setresgid(%d, %d, %d)", - (int)ID(rgid), (int)ID(egid), (int)ID(sgid)); - goto bad; - } - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: uid: " - "[%d, %d, %d] -> [%d, %d, %d]", __func__, - (int)ostate->ruid, (int)ostate->euid, (int)ostate->suid, - (int)state->ruid, (int)state->euid, (int)state->suid); - if (UID_CHANGED && setresuid(ID(ruid), ID(euid), ID(suid))) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setresuid(%d, %d, %d)", - (int)ID(ruid), (int)ID(euid), (int)ID(suid)); - goto bad; - } - break; } perm_stack_depth++; @@ -726,46 +695,6 @@ set_perms(int perm) } } break; - - case PERM_IOLOG: - state->gidlist = ostate->gidlist; - sudo_gidlist_addref(state->gidlist); - state->rgid = ostate->rgid; - state->egid = iolog_gid; - state->sgid = ostate->sgid; - state->ruid = ROOT_UID; - state->euid = iolog_uid; - state->suid = ROOT_UID; - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: gid: " - "[%d, %d, %d] -> [%d, %d, %d]", __func__, - (int)ostate->rgid, (int)ostate->egid, (int)ostate->sgid, - (int)state->rgid, (int)state->egid, (int)state->sgid); - if (GID_CHANGED && setgidx(ID_EFFECTIVE, iolog_gid)) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setgidx(ID_EFFECTIVE, %d)", (int)iolog_gid); - goto bad; - } - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: uid: " - "[%d, %d, %d] -> [%d, %d, %d]", __func__, - (int)ostate->ruid, (int)ostate->euid, (int)ostate->suid, - (int)state->ruid, (int)state->euid, (int)state->suid); - if (UID_CHANGED) { - if (ostate->ruid != ROOT_UID || ostate->suid != ROOT_UID) { - if (setuidx(ID_EFFECTIVE|ID_REAL|ID_SAVED, ROOT_UID)) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setuidx(ID_EFFECTIVE|ID_REAL|ID_SAVED, %d)", - ROOT_UID); - goto bad; - } - } - if (setuidx(ID_EFFECTIVE, timestamp_uid)) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setuidx(ID_EFFECTIVE, %d)", - (int)timestamp_uid); - goto bad; - } - } - break; } perm_stack_depth++; @@ -1129,33 +1058,6 @@ set_perms(int perm) goto bad; } break; - - case PERM_IOLOG: - state->gidlist = ostate->gidlist; - sudo_gidlist_addref(state->gidlist); - state->rgid = ostate->rgid; - state->egid = iolog_gid; - state->ruid = ROOT_UID; - state->euid = iolog_uid; - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: gid: " - "[%d, %d] -> [%d, %d]", __func__, (int)ostate->rgid, - (int)ostate->egid, (int)state->rgid, (int)state->egid); - if (GID_CHANGED && setregid(ID(rgid), ID(egid))) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setregid(%d, %d)", - (int)ID(rgid), (int)ID(egid)); - goto bad; - } - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: uid: " - "[%d, %d] -> [%d, %d]", __func__, (int)ostate->ruid, - (int)ostate->euid, (int)state->ruid, (int)state->euid); - if (UID_CHANGED && setreuid(ID(ruid), ID(euid))) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setreuid(%d, %d)", - (int)ID(ruid), (int)ID(euid)); - goto bad; - } - break; } perm_stack_depth++; @@ -1459,31 +1361,6 @@ set_perms(int perm) goto bad; } break; - - case PERM_IOLOG: - state->gidlist = ostate->gidlist; - sudo_gidlist_addref(state->gidlist); - state->rgid = ostate->rgid; - state->egid = iolog_gid; - state->ruid = ROOT_UID; - state->euid = iolog_uid; - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: gid: " - "[%d, %d] -> [%d, %d]", __func__, (int)ostate->rgid, - (int)ostate->egid, (int)state->rgid, (int)state->egid); - if (GID_CHANGED && setegid(iolog_gid)) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: setegid(%d)", (int)iolog_gid); - goto bad; - } - sudo_debug_printf(SUDO_DEBUG_INFO, "%s: PERM_IOLOG: uid: " - "[%d, %d] -> [%d, %d]", __func__, (int)ostate->ruid, - (int)ostate->euid, (int)state->ruid, (int)state->euid); - if (seteuid(timestamp_uid)) { - (void)snprintf(errbuf, sizeof(errbuf), - "PERM_IOLOG: seteuid(%d)", (int)timestamp_uid); - goto bad; - } - break; } perm_stack_depth++; @@ -1524,11 +1401,11 @@ restore_perms(void) * real and effective uids to ROOT_UID initially to be safe. */ if (seteuid(ROOT_UID)) { - sudo_warnx("seteuid() [%d] -> [%d]", (int)state->euid, ROOT_UID); + sudo_warn("seteuid() [%d] -> [%d]", (int)state->euid, ROOT_UID); goto bad; } if (setuid(ROOT_UID)) { - sudo_warnx("setuid() [%d, %d] -> [%d, %d]", (int)state->ruid, ROOT_UID, + sudo_warn("setuid() [%d, %d] -> [%d, %d]", (int)state->ruid, ROOT_UID, ROOT_UID, ROOT_UID); goto bad; } @@ -1643,7 +1520,6 @@ set_perms(int perm) case PERM_SUDOERS: case PERM_RUNAS: case PERM_TIMESTAMP: - case PERM_IOLOG: /* Unsupported since we can't set euid. */ state->ruid = ostate->ruid; state->rgid = ostate->rgid; diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index e3c052ed5..cad1355ee 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -68,6 +68,7 @@ #include "sudoers.h" #include "parse.h" #include "auth/sudo_auth.h" +#include "sudo_iolog.h" #ifndef HAVE_GETADDRINFO # include "compat/getaddrinfo.h" @@ -472,8 +473,12 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], if (ISSET(sudo_mode, (MODE_RUN | MODE_EDIT))) { if ((def_log_input || def_log_output) && def_iolog_file && def_iolog_dir) { const char prefix[] = "iolog_path="; + /* Use sudoers locale for strftime() */ + sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); iolog_path = expand_iolog_path(prefix, def_iolog_dir, - def_iolog_file, &sudo_user.iolog_file); + def_iolog_file, &sudo_user.iolog_file, + sudoers_iolog_path_escapes); + sudoers_setlocale(oldlocale, NULL); if (iolog_path == NULL) { if (!def_ignore_iolog_errors) goto done; diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index b00980aa3..906dc541a 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -237,13 +237,6 @@ struct sudo_user { # define SUDOERS_MODE 0600 #endif -#ifdef __TANDEM -# define ROOT_UID 65535 -#else -# define ROOT_UID 0 -#endif -#define ROOT_GID 0 - struct sudo_lbuf; struct passwd; struct stat; @@ -350,17 +343,14 @@ char *get_timestr(time_t, int); bool get_boottime(struct timespec *); /* iolog.c */ -bool io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7]); bool cb_maxseq(const union sudo_defs_val *sd_un); bool cb_iolog_user(const union sudo_defs_val *sd_un); bool cb_iolog_group(const union sudo_defs_val *sd_un); bool cb_iolog_mode(const union sudo_defs_val *sd_un); -extern uid_t iolog_uid; -extern gid_t iolog_gid; -/* iolog_path.c */ -char *expand_iolog_path(const char *prefix, const char *dir, const char *file, - char **slashp); +/* iolog_path_escapes.c */ +struct iolog_path_escape; +extern const struct iolog_path_escape *sudoers_iolog_path_escapes; /* env.c */ char **env_get(void); diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c index e411b8ac1..4a5b01764 100644 --- a/plugins/sudoers/sudoreplay.c +++ b/plugins/sudoers/sudoreplay.c @@ -64,8 +64,7 @@ #include "sudo_compat.h" #include "sudo_fatal.h" #include "logging.h" -#include "iolog_util.h" -#include "iolog_files.h" +#include "sudo_iolog.h" #include "sudo_queue.h" #include "sudo_plugin.h" #include "sudo_conf.h" @@ -80,6 +79,7 @@ #endif /* HAVE_GETOPT_LONG */ struct replay_closure { + const char *iolog_dir; struct sudo_event_base *evbase; struct sudo_event *delay_ev; struct sudo_event *keyboard_ev; @@ -146,6 +146,15 @@ static int terminal_lines, terminal_cols; static int ttyfd = -1; +static struct iolog_file iolog_files[] = { + { false }, /* IOFD_STDIN */ + { false }, /* IOFD_STDOUT */ + { false }, /* IOFD_STDERR */ + { false }, /* IOFD_TTYIN */ + { false }, /* IOFD_TTYOUT */ + { true, }, /* IOFD_TIMING */ +}; + static const char short_opts[] = "d:f:hlm:nRSs:V"; static struct option long_opts[] = { { "directory", required_argument, NULL, 'd' }, @@ -166,11 +175,11 @@ extern char *get_timestr(time_t, int); extern time_t get_date(char *); static int list_sessions(int, char **, const char *, const char *, const char *); -static int open_io_fd(char *path, int len, struct io_log_file *iol); static int parse_expr(struct search_node_list *, char **, bool); static void read_keyboard(int fd, int what, void *v); static void help(void) __attribute__((__noreturn__)); -static int replay_session(struct timespec *max_wait, const char *decimal, bool interactive, bool suspend_wait); +static int replay_session(const char *iolog_dir, struct timespec *max_wait, + const char *decimal, bool interactive, bool suspend_wait); static void sudoreplay_cleanup(void); static void usage(int); static void write_output(int fd, int what, void *v); @@ -238,15 +247,15 @@ main(int argc, char *argv[]) def_filter = false; for (cp = strtok_r(optarg, ",", &ep); cp; cp = strtok_r(NULL, ",", &ep)) { if (strcmp(cp, "stdin") == 0) - io_log_files[IOFD_STDIN].enabled = true; + iolog_files[IOFD_STDIN].enabled = true; else if (strcmp(cp, "stdout") == 0) - io_log_files[IOFD_STDOUT].enabled = true; + iolog_files[IOFD_STDOUT].enabled = true; else if (strcmp(cp, "stderr") == 0) - io_log_files[IOFD_STDERR].enabled = true; + iolog_files[IOFD_STDERR].enabled = true; else if (strcmp(cp, "ttyin") == 0) - io_log_files[IOFD_TTYIN].enabled = true; + iolog_files[IOFD_TTYIN].enabled = true; else if (strcmp(cp, "ttyout") == 0) - io_log_files[IOFD_TTYOUT].enabled = true; + iolog_files[IOFD_TTYOUT].enabled = true; else sudo_fatalx(U_("invalid filter option: %s"), optarg); } @@ -308,9 +317,9 @@ main(int argc, char *argv[]) /* By default we replay stdout, stderr and ttyout. */ if (def_filter) { - io_log_files[IOFD_STDOUT].enabled = true; - io_log_files[IOFD_STDERR].enabled = true; - io_log_files[IOFD_TTYOUT].enabled = true; + iolog_files[IOFD_STDOUT].enabled = true; + iolog_files[IOFD_STDERR].enabled = true; + iolog_files[IOFD_TTYOUT].enabled = true; } /* 6 digit ID in base 36, e.g. 01G712AB or free-form name */ @@ -327,15 +336,22 @@ main(int argc, char *argv[]) sudo_fatalx(U_("%s/timing: %s"), id, strerror(ENAMETOOLONG)); } else { plen = snprintf(path, sizeof(path), "%s/%s/timing", session_dir, id); - if (plen < 0 || plen >= ssizeof(path)) + if (plen < 0 || plen >= ssizeof(path)) { sudo_fatalx(U_("%s/%s/timing: %s"), session_dir, id, strerror(ENAMETOOLONG)); + } } plen -= 7; /* Open files for replay, applying replay filter for the -f flag. */ for (i = 0; i < IOFD_MAX; i++) { - if (open_io_fd(path, plen, &io_log_files[i]) == -1) + path[plen] = '/'; + path[plen + 1] = '\0'; + if (strlcat(path, iolog_fd_to_name(i), sizeof(path)) >= sizeof(path)) { + errno = ENAMETOOLONG; + sudo_fatal("%s%s", path, iolog_fd_to_name(i)); + } + if (!iolog_open(&iolog_files[i], path, "r")) sudo_fatal(U_("unable to open %s"), path); } @@ -357,8 +373,10 @@ main(int argc, char *argv[]) free_iolog_info(li); li = NULL; - /* Replay session corresponding to io_log_files[]. */ - exitcode = replay_session(max_delay, decimal, interactive, suspend_wait); + /* Replay session corresponding to iolog_files[]. */ + path[plen] = '\0'; + exitcode = replay_session(path, max_delay, decimal, interactive, + suspend_wait); restore_terminal_size(); sudo_term_restore(ttyfd, true); @@ -367,58 +385,6 @@ done: exit(exitcode); } -/* - * Call gzread() or fread() for the I/O log file in question. - * Return 0 for EOF or -1 on error. - */ -static ssize_t -io_log_read(union io_fd ifd, char *buf, size_t nbytes) -{ - ssize_t nread; - debug_decl(io_log_read, SUDO_DEBUG_UTIL) - - if (nbytes > INT_MAX) { - errno = EINVAL; - debug_return_ssize_t(-1); - } -#ifdef HAVE_ZLIB_H - nread = gzread(ifd.g, buf, nbytes); -#else - nread = (ssize_t)fread(buf, 1, nbytes, ifd.f); - if (nread == 0 && ferror(ifd.f)) - nread = -1; -#endif - debug_return_ssize_t(nread); -} - -static int -io_log_eof(union io_fd ifd) -{ - int ret; - debug_decl(io_log_eof, SUDO_DEBUG_UTIL) - -#ifdef HAVE_ZLIB_H - ret = gzeof(ifd.g); -#else - ret = feof(ifd.f); -#endif - debug_return_int(ret); -} - -static char * -io_log_gets(union io_fd ifd, char *buf, size_t nbytes) -{ - char *str; - debug_decl(io_log_gets, SUDO_DEBUG_UTIL) - -#ifdef HAVE_ZLIB_H - str = gzgets(ifd.g, buf, nbytes); -#else - str = fgets(buf, nbytes, ifd.f); -#endif - debug_return_str(str); -} - /* * List of terminals that support xterm-like resizing. * This is not an exhaustive list. @@ -753,18 +719,23 @@ restore_terminal_size(void) * Read the next record from the timing file and schedule a delay * event with the specified timeout. * Return 0 on success, 1 on EOF and -1 on error. + * XXX - duplicated in sendlog */ static int read_timing_record(struct replay_closure *closure) { struct timing_closure *timing = &closure->timing; char line[LINE_MAX]; + const char *errstr; debug_decl(read_timing_record, SUDO_DEBUG_UTIL) /* Read next record from timing file. */ - if (io_log_gets(io_log_files[IOFD_TIMING].fd, line, sizeof(line)) == NULL) { + if (iolog_gets(&iolog_files[IOFD_TIMING], line, sizeof(line), &errstr) == NULL) { /* EOF or error reading timing file, we are done. */ - debug_return_int(io_log_eof(io_log_files[IOFD_TIMING].fd) ? 1 : -1); + if (iolog_eof(&iolog_files[IOFD_TIMING])) + debug_return_int(1); + sudo_fatalx(U_("error reading timing file: %s"), errstr); + debug_return_int(-1); } /* Parse timing file record. */ @@ -773,7 +744,6 @@ read_timing_record(struct replay_closure *closure) sudo_fatalx(U_("invalid timing file line: %s"), line); /* Record number bytes to read. */ - /* XXX - remove timing->nbytes? */ if (timing->event != IO_EVENT_WINSIZE && timing->event != IO_EVENT_SUSPEND) { closure->iobuf.len = 0; @@ -828,24 +798,27 @@ fill_iobuf(struct replay_closure *closure) { const size_t space = sizeof(closure->iobuf.buf) - closure->iobuf.len; const struct timing_closure *timing = &closure->timing; + const char *errstr; debug_decl(fill_iobuf, SUDO_DEBUG_UTIL) if (closure->iobuf.toread != 0 && space != 0) { const size_t len = closure->iobuf.toread < space ? closure->iobuf.toread : space; - ssize_t nread = io_log_read(timing->fd, - closure->iobuf.buf + closure->iobuf.off, len); + ssize_t nread = iolog_read(timing->iol, + closure->iobuf.buf + closure->iobuf.off, len, &errstr); if (nread <= 0) { if (nread == 0) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "%s: premature EOF, expected %u bytes", - io_log_files[timing->event].suffix, closure->iobuf.toread); + "%s/%s: premature EOF, expected %u bytes", + closure->iolog_dir, iolog_fd_to_name(timing->event), + closure->iobuf.toread); } else { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, - "%s: read error", io_log_files[timing->event].suffix); + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s/%s: read error: %s", closure->iolog_dir, + iolog_fd_to_name(timing->event), errstr); } - sudo_warnx(U_("unable to read %s"), - io_log_files[timing->event].suffix); + sudo_warnx(U_("unable to read %s/%s: %s"), + closure->iolog_dir, iolog_fd_to_name(timing->event), errstr); debug_return_bool(false); } closure->iobuf.toread -= nread; @@ -872,28 +845,28 @@ delay_cb(int fd, int what, void *v) resize_terminal(timing->u.winsize.lines, timing->u.winsize.cols); break; case IO_EVENT_STDIN: - if (io_log_files[IOFD_STDIN].enabled) - timing->fd = io_log_files[IOFD_STDIN].fd; + if (iolog_files[IOFD_STDIN].enabled) + timing->iol = &iolog_files[IOFD_STDIN]; break; case IO_EVENT_STDOUT: - if (io_log_files[IOFD_STDOUT].enabled) - timing->fd = io_log_files[IOFD_STDOUT].fd; + if (iolog_files[IOFD_STDOUT].enabled) + timing->iol = &iolog_files[IOFD_STDOUT]; break; case IO_EVENT_STDERR: - if (io_log_files[IOFD_STDERR].enabled) - timing->fd = io_log_files[IOFD_STDERR].fd; + if (iolog_files[IOFD_STDERR].enabled) + timing->iol = &iolog_files[IOFD_STDERR]; break; case IO_EVENT_TTYIN: - if (io_log_files[IOFD_TTYIN].enabled) - timing->fd = io_log_files[IOFD_TTYIN].fd; + if (iolog_files[IOFD_TTYIN].enabled) + timing->iol = &iolog_files[IOFD_TTYIN]; break; case IO_EVENT_TTYOUT: - if (io_log_files[IOFD_TTYOUT].enabled) - timing->fd = io_log_files[IOFD_TTYOUT].fd; + if (iolog_files[IOFD_TTYOUT].enabled) + timing->iol = &iolog_files[IOFD_TTYOUT]; break; } - if (timing->fd.v != NULL) { + if (timing->iol != NULL) { /* If the stream is open, enable the write event. */ if (sudo_ev_add(closure->evbase, closure->output_ev, NULL, false) == -1) sudo_fatal(U_("unable to add event to queue")); @@ -950,8 +923,8 @@ signal_cb(int signo, int what, void *v) } static struct replay_closure * -replay_closure_alloc(struct timespec *max_delay, const char *decimal, - bool interactive, bool suspend_wait) +replay_closure_alloc(const char *iolog_dir, struct timespec *max_delay, + const char *decimal, bool interactive, bool suspend_wait) { struct replay_closure *closure; debug_decl(replay_closure_alloc, SUDO_DEBUG_UTIL) @@ -959,6 +932,7 @@ replay_closure_alloc(struct timespec *max_delay, const char *decimal, if ((closure = calloc(1, sizeof(*closure))) == NULL) debug_return_ptr(NULL); + closure->iolog_dir = iolog_dir; closure->interactive = interactive; closure->suspend_wait = suspend_wait; closure->max_delay = max_delay; @@ -1033,15 +1007,15 @@ bad: } static int -replay_session(struct timespec *max_delay, const char *decimal, - bool interactive, bool suspend_wait) +replay_session(const char *iolog_dir, struct timespec *max_delay, + const char *decimal, bool interactive, bool suspend_wait) { struct replay_closure *closure; int ret = 0; debug_decl(replay_session, SUDO_DEBUG_UTIL) /* Allocate the delay closure and read the first timing record. */ - closure = replay_closure_alloc(max_delay, decimal, interactive, + closure = replay_closure_alloc(iolog_dir, max_delay, decimal, interactive, suspend_wait); if (read_timing_record(closure) != 0) { ret = 1; @@ -1059,28 +1033,6 @@ done: debug_return_int(ret); } -static int -open_io_fd(char *path, int len, struct io_log_file *iol) -{ - debug_decl(open_io_fd, SUDO_DEBUG_UTIL) - - if (!iol->enabled) - debug_return_int(0); - - path[len] = '\0'; - strlcat(path, iol->suffix, PATH_MAX); -#ifdef HAVE_ZLIB_H - iol->fd.g = gzopen(path, "r"); -#else - iol->fd.f = fopen(path, "r"); -#endif - if (iol->fd.v == NULL) { - iol->enabled = false; - debug_return_int(-1); - } - debug_return_int(0); -} - /* * Write the I/O buffer. */ diff --git a/src/sudo.h b/src/sudo.h index ac0b323a3..d1d8663d4 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -50,12 +50,6 @@ # define NDEBUG #endif -#ifdef __TANDEM -# define ROOT_UID 65535 -#else -# define ROOT_UID 0 -#endif - /* * Various modes sudo can be in (based on arguments) in hex */