diff --git a/MANIFEST b/MANIFEST index 36110a735..2d3c0bc43 100644 --- a/MANIFEST +++ b/MANIFEST @@ -115,14 +115,28 @@ lib/fuzzstub/fuzzstub.c lib/iolog/Makefile.in lib/iolog/host_port.c lib/iolog/hostcheck.c +lib/iolog/iolog_clearerr.c +lib/iolog/iolog_close.c lib/iolog/iolog_conf.c -lib/iolog/iolog_fileio.c +lib/iolog/iolog_eof.c +lib/iolog/iolog_gets.c lib/iolog/iolog_json.c lib/iolog/iolog_json.h lib/iolog/iolog_legacy.c lib/iolog/iolog_loginfo.c +lib/iolog/iolog_mkdirs.c +lib/iolog/iolog_mkdtemp.c +lib/iolog/iolog_mkpath.c +lib/iolog/iolog_nextid.c +lib/iolog/iolog_open.c +lib/iolog/iolog_openat.c lib/iolog/iolog_path.c +lib/iolog/iolog_read.c +lib/iolog/iolog_seek.c +lib/iolog/iolog_swapids.c lib/iolog/iolog_timing.c +lib/iolog/iolog_util.c +lib/iolog/iolog_write.c lib/iolog/regress/corpus/log_json/id.json lib/iolog/regress/corpus/log_json/ls.json lib/iolog/regress/corpus/log_json/mailq.json diff --git a/include/sudo_iolog.h b/include/sudo_iolog.h index fbcc6e7a3..16e0c3a97 100644 --- a/include/sudo_iolog.h +++ b/include/sudo_iolog.h @@ -113,7 +113,6 @@ bool iolog_mkdtemp(char *path); bool iolog_mkpath(char *path); bool iolog_nextid(char *iolog_dir, char sessid[7]); bool iolog_open(struct iolog_file *iol, int dfd, int iofd, const char *mode); -bool iolog_rename(const char *from, const char *to); bool iolog_write_info_file(int dfd, struct eventlog *evlog); char *iolog_gets(struct iolog_file *iol, char *buf, size_t nbytes, const char **errsttr); const char *iolog_fd_to_name(int iofd); @@ -137,5 +136,7 @@ void iolog_set_gid(gid_t gid); void iolog_set_maxseq(unsigned int maxval); void iolog_set_mode(mode_t mode); void iolog_set_owner(uid_t uid, uid_t gid); +bool iolog_swapids(bool restore); +bool iolog_mkdirs(char *path); #endif /* SUDO_IOLOG_H */ diff --git a/lib/iolog/Makefile.in b/lib/iolog/Makefile.in index 968340a5c..ff2d62428 100644 --- a/lib/iolog/Makefile.in +++ b/lib/iolog/Makefile.in @@ -95,23 +95,27 @@ DEVEL = @DEVEL@ SHELL = @SHELL@ -LIBIOLOG_OBJS = iolog_conf.lo iolog_fileio.lo iolog_json.lo iolog_legacy.lo \ - iolog_loginfo.lo iolog_path.lo iolog_timing.lo host_port.lo \ - hostcheck.lo +LIBIOLOG_OBJS = host_port.lo hostcheck.lo iolog_clearerr.lo iolog_close.lo \ + iolog_conf.lo iolog_eof.lo iolog_gets.lo iolog_json.lo \ + iolog_legacy.lo iolog_loginfo.lo iolog_mkdirs.lo \ + iolog_mkdtemp.lo iolog_mkpath.lo iolog_nextid.lo \ + iolog_open.lo iolog_openat.lo iolog_path.lo iolog_read.lo \ + iolog_seek.lo iolog_swapids.lo iolog_timing.lo iolog_util.lo \ + iolog_write.lo IOBJS = $(LIBIOLOG_OBJS:.lo=.i) POBJS = $(IOBJS:.i=.plog) -CHECK_IOLOG_MKPATH_OBJS = check_iolog_mkpath.lo iolog_conf.lo iolog_fileio.lo +CHECK_IOLOG_MKPATH_OBJS = check_iolog_mkpath.lo -CHECK_IOLOG_PATH_OBJS = check_iolog_path.lo iolog_path.lo +CHECK_IOLOG_PATH_OBJS = check_iolog_path.lo -CHECK_IOLOG_TIMING_OBJS = check_iolog_timing.lo iolog_json.lo iolog_timing.lo +CHECK_IOLOG_TIMING_OBJS = check_iolog_timing.lo -CHECK_IOLOG_JSON_OBJS = check_iolog_json.lo iolog_json.lo +CHECK_IOLOG_JSON_OBJS = check_iolog_json.lo -HOST_PORT_TEST_OBJS = host_port_test.lo host_port.lo +HOST_PORT_TEST_OBJS = host_port_test.lo FUZZ_IOLOG_JSON_OBJS = fuzz_iolog_json.lo @@ -425,6 +429,30 @@ hostcheck.i: $(srcdir)/hostcheck.c $(incdir)/compat/stdbool.h \ $(CC) -E -o $@ $(CPPFLAGS) $< hostcheck.plog: hostcheck.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/hostcheck.c --i-file $< --output-file $@ +iolog_clearerr.lo: $(srcdir)/iolog_clearerr.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_clearerr.c +iolog_clearerr.i: $(srcdir)/iolog_clearerr.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_clearerr.plog: iolog_clearerr.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_clearerr.c --i-file $< --output-file $@ +iolog_close.lo: $(srcdir)/iolog_close.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_close.c +iolog_close.i: $(srcdir)/iolog_close.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_close.plog: iolog_close.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_close.c --i-file $< --output-file $@ iolog_conf.lo: $(srcdir)/iolog_conf.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ @@ -439,22 +467,30 @@ iolog_conf.i: $(srcdir)/iolog_conf.c $(incdir)/compat/stdbool.h \ $(CC) -E -o $@ $(CPPFLAGS) $< iolog_conf.plog: iolog_conf.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_conf.c --i-file $< --output-file $@ -iolog_fileio.lo: $(srcdir)/iolog_fileio.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_plugin.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_fileio.c -iolog_fileio.i: $(srcdir)/iolog_fileio.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_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(top_builddir)/config.h +iolog_eof.lo: $(srcdir)/iolog_eof.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_eof.c +iolog_eof.i: $(srcdir)/iolog_eof.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.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_eof.plog: iolog_eof.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_eof.c --i-file $< --output-file $@ +iolog_gets.lo: $(srcdir)/iolog_gets.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_gets.c +iolog_gets.i: $(srcdir)/iolog_gets.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_gets.plog: iolog_gets.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_gets.c --i-file $< --output-file $@ iolog_json.lo: $(srcdir)/iolog_json.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_eventlog.h $(incdir)/sudo_fatal.h \ @@ -507,6 +543,94 @@ iolog_loginfo.i: $(srcdir)/iolog_loginfo.c $(incdir)/compat/stdbool.h \ $(CC) -E -o $@ $(CPPFLAGS) $< iolog_loginfo.plog: iolog_loginfo.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_loginfo.c --i-file $< --output-file $@ +iolog_mkdirs.lo: $(srcdir)/iolog_mkdirs.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_plugin.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_mkdirs.c +iolog_mkdirs.i: $(srcdir)/iolog_mkdirs.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_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_mkdirs.plog: iolog_mkdirs.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_mkdirs.c --i-file $< --output-file $@ +iolog_mkdtemp.lo: $(srcdir)/iolog_mkdtemp.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_plugin.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_mkdtemp.c +iolog_mkdtemp.i: $(srcdir)/iolog_mkdtemp.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_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_mkdtemp.plog: iolog_mkdtemp.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_mkdtemp.c --i-file $< --output-file $@ +iolog_mkpath.lo: $(srcdir)/iolog_mkpath.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_mkpath.c +iolog_mkpath.i: $(srcdir)/iolog_mkpath.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_mkpath.plog: iolog_mkpath.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_mkpath.c --i-file $< --output-file $@ +iolog_nextid.lo: $(srcdir)/iolog_nextid.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_plugin.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_nextid.c +iolog_nextid.i: $(srcdir)/iolog_nextid.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_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_nextid.plog: iolog_nextid.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_nextid.c --i-file $< --output-file $@ +iolog_open.lo: $(srcdir)/iolog_open.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.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_open.c +iolog_open.i: $(srcdir)/iolog_open.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_open.plog: iolog_open.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_open.c --i-file $< --output-file $@ +iolog_openat.lo: $(srcdir)/iolog_openat.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_plugin.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_openat.c +iolog_openat.i: $(srcdir)/iolog_openat.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_plugin.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_openat.plog: iolog_openat.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_openat.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 \ @@ -523,6 +647,44 @@ iolog_path.i: $(srcdir)/iolog_path.c $(incdir)/compat/stdbool.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_read.lo: $(srcdir)/iolog_read.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_read.c +iolog_read.i: $(srcdir)/iolog_read.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_read.plog: iolog_read.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_read.c --i-file $< --output-file $@ +iolog_seek.lo: $(srcdir)/iolog_seek.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_seek.c +iolog_seek.i: $(srcdir)/iolog_seek.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_seek.plog: iolog_seek.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_seek.c --i-file $< --output-file $@ +iolog_swapids.lo: $(srcdir)/iolog_swapids.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_plugin.h \ + $(incdir)/sudo_queue.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_swapids.c +iolog_swapids.i: $(srcdir)/iolog_swapids.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_plugin.h \ + $(incdir)/sudo_queue.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_swapids.plog: iolog_swapids.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_swapids.c --i-file $< --output-file $@ iolog_timing.lo: $(srcdir)/iolog_timing.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_eventlog.h $(incdir)/sudo_fatal.h \ @@ -539,3 +701,27 @@ iolog_timing.i: $(srcdir)/iolog_timing.c $(incdir)/compat/stdbool.h \ $(CC) -E -o $@ $(CPPFLAGS) $< iolog_timing.plog: iolog_timing.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_timing.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_iolog.h $(incdir)/sudo_queue.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_iolog.h $(incdir)/sudo_queue.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_write.lo: $(srcdir)/iolog_write.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_write.c +iolog_write.i: $(srcdir)/iolog_write.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_iolog.h $(incdir)/sudo_queue.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +iolog_write.plog: iolog_write.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_write.c --i-file $< --output-file $@ diff --git a/lib/iolog/iolog_clearerr.c b/lib/iolog/iolog_clearerr.c new file mode 100644 index 000000000..631e02090 --- /dev/null +++ b/lib/iolog/iolog_clearerr.c @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +void +iolog_clearerr(struct iolog_file *iol) +{ + debug_decl(iolog_eof, SUDO_DEBUG_UTIL); + +#ifdef HAVE_ZLIB_H + if (iol->compressed) + gzclearerr(iol->fd.g); + else +#endif + clearerr(iol->fd.f); + debug_return; +} diff --git a/lib/iolog/iolog_close.c b/lib/iolog/iolog_close.c new file mode 100644 index 000000000..caa68c6db --- /dev/null +++ b/lib/iolog/iolog_close.c @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.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) { + int errnum; + + /* Must check error indicator before closing. */ + if (iol->writable) { + if (gzflush(iol->fd.g, Z_SYNC_FLUSH) != Z_OK) { + ret = false; + if (errstr != NULL) { + *errstr = gzerror(iol->fd.g, &errnum); + if (errnum == Z_ERRNO) + *errstr = strerror(errno); + } + } + } + errnum = gzclose(iol->fd.g); + if (ret && errnum != Z_OK) { + ret = false; + if (errstr != NULL) + *errstr = errnum == Z_ERRNO ? strerror(errno) : "unknown error"; + } + } else +#endif + if (fclose(iol->fd.f) != 0) { + ret = false; + if (errstr != NULL) + *errstr = strerror(errno); + } + + debug_return_bool(ret); +} diff --git a/lib/iolog/iolog_eof.c b/lib/iolog/iolog_eof.c new file mode 100644 index 000000000..c1cece287 --- /dev/null +++ b/lib/iolog/iolog_eof.c @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * 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) != 0; + else +#endif + ret = feof(iol->fd.f) != 0; + debug_return_int(ret); +} diff --git a/lib/iolog/iolog_fileio.c b/lib/iolog/iolog_fileio.c deleted file mode 100644 index 0df7ddba0..000000000 --- a/lib/iolog/iolog_fileio.c +++ /dev/null @@ -1,815 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright (c) 2009-2021 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_STDBOOL_H -# include -#else -# include "compat/stdbool.h" -#endif -#include -#include -#include -#include -#include - -#include "sudo_compat.h" -#include "sudo_debug.h" -#include "sudo_fatal.h" -#include "sudo_gettext.h" -#include "sudo_iolog.h" -#include "sudo_util.h" - -static unsigned char const gzip_magic[2] = {0x1f, 0x8b}; - -/* - * 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; - const uid_t iolog_uid = iolog_get_uid(); - const gid_t iolog_gid = iolog_get_gid(); - debug_decl(io_swapids, SUDO_DEBUG_UTIL); - - if (user_euid == (uid_t)-1) - user_euid = geteuid(); - if (user_egid == (gid_t)-1) - user_egid = 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 -iolog_mkdirs(char *path) -{ - const mode_t iolog_filemode = iolog_get_file_mode(); - const mode_t iolog_dirmode = iolog_get_dir_mode(); - const uid_t iolog_uid = iolog_get_uid(); - const gid_t iolog_gid = iolog_get_gid(); - bool ok = true, uid_changed = false; - struct stat sb; - mode_t omask; - int dfd; - debug_decl(iolog_mkdirs, SUDO_DEBUG_UTIL); - - dfd = open(path, O_RDONLY|O_NONBLOCK); - if (dfd == -1 && errno == EACCES) { - /* Try again as the I/O log owner (for NFS). */ - if (io_swapids(false)) { - dfd = open(path, O_RDONLY|O_NONBLOCK); - if (!io_swapids(true)) { - ok = false; - goto done; - } - } - } - if (dfd != -1 && fstat(dfd, &sb) != -1) { - if (S_ISDIR(sb.st_mode)) { - if (sb.st_uid != iolog_uid || sb.st_gid != iolog_gid) { - if (fchown(dfd, 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 (fchmod(dfd, 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; - } - - /* umask must not be more restrictive than the file modes. */ - omask = umask(ACCESSPERMS & ~(iolog_filemode|iolog_dirmode)); - - 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; - } - - umask(omask); - -done: - if (dfd != -1) - close(dfd); - debug_return_bool(ok); -} - -/* - * Create temporary directory and any parent directories as needed. - */ -bool -iolog_mkdtemp(char *path) -{ - const mode_t iolog_dirmode = iolog_get_dir_mode(); - const uid_t iolog_uid = iolog_get_uid(); - const gid_t iolog_gid = iolog_get_gid(); - bool ok, uid_changed = false; - debug_decl(iolog_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); -} - -/* - * Like rename(2) but changes UID as needed. - */ -bool -iolog_rename(const char *from, const char *to) -{ - bool ok, uid_changed = false; - debug_decl(iolog_rename, SUDO_DEBUG_UTIL); - - ok = rename(from, to) == 0; - if (!ok && errno == EACCES) { - uid_changed = io_swapids(false); - if (uid_changed) - ok = rename(from, to) == 0; - } - - if (uid_changed) { - if (!io_swapids(true)) - ok = false; - } - debug_return_bool(ok); -} - -/* - * Wrapper for openat(2) that sets umask and retries as iolog_uid/iolog_gid - * if openat(2) returns EACCES. - */ -int -iolog_openat(int dfd, const char *path, int flags) -{ - const mode_t iolog_filemode = iolog_get_file_mode(); - const mode_t iolog_dirmode = iolog_get_dir_mode(); - mode_t omask = S_IRWXG|S_IRWXO; - int fd; - debug_decl(iolog_openat, 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 = openat(dfd, path, flags, iolog_filemode); - if (fd == -1 && errno == EACCES) { - /* Enable write bit if it is missing. */ - struct stat sb; - if (fstatat(dfd, path, &sb, 0) == 0) { - mode_t write_bits = iolog_filemode & (S_IWUSR|S_IWGRP|S_IWOTH); - if ((sb.st_mode & write_bits) != write_bits) { - if (fchmodat(dfd, path, iolog_filemode, 0) == 0) - fd = openat(dfd, path, flags, iolog_filemode); - } - } - } - if (fd == -1 && errno == EACCES) { - /* Try again as the I/O log owner (for NFS). */ - if (io_swapids(false)) { - fd = openat(dfd, 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"; - const uid_t iolog_uid = iolog_get_uid(); - const gid_t iolog_gid = iolog_get_gid(); - debug_decl(iolog_nextid, SUDO_DEBUG_UTIL); - - /* - * Create I/O log directory if it doesn't already exist. - */ - if (!iolog_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; - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: %s/seq", __func__, iolog_dir); - goto done; - } - fd = iolog_openat(AT_FDCWD, pathbuf, O_RDWR|O_CREAT); - if (fd == -1) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to open %s", __func__, pathbuf); - goto done; - } - if (!sudo_lock_file(fd, SUDO_LOCK)) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "unable to lock %s", pathbuf); - goto done; - } - 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). */ - 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 >= iolog_get_maxseq()) { - 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 - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, - "%s: unable to write %s", __func__, pathbuf); - goto done; - } - ret = true; - -done: - if (fd != -1) - close(fd); - debug_return_bool(ret); -} - -/* - * Create path and any intermediate directories. - * If path ends in 'XXXXXX', use mkdtemp(). - */ -bool -iolog_mkpath(char *path) -{ - size_t len; - bool ret; - debug_decl(iolog_mkpath, SUDO_DEBUG_UTIL); - - /* - * 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. - */ - len = strlen(path); - if (len >= 6 && strcmp(&path[len - 6], "XXXXXX") == 0) - ret = iolog_mkdtemp(path); - else - ret = iolog_mkdirs(path); - - sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "iolog path %s", path); - - debug_return_bool(ret); -} - -/* - * 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, int dfd, int iofd, const char *mode) -{ - int flags; - const char *file; - unsigned char magic[2]; - const uid_t iolog_uid = iolog_get_uid(); - const gid_t iolog_gid = iolog_get_gid(); - 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); - } - if ((file = iolog_fd_to_name(iofd)) == NULL) { - sudo_debug_printf(SUDO_DEBUG_ERROR, - "%s: invalid iofd %d", __func__, iofd); - debug_return_bool(false); - } - - iol->writable = false; - iol->compressed = false; - if (iol->enabled) { - int fd = iolog_openat(dfd, file, 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, file); - } - iol->compressed = iolog_get_compress(); - } else { - /* check for gzip magic number */ - if (pread(fd, magic, sizeof(magic), 0) == ssizeof(magic)) { - if (magic[0] == gzip_magic[0] && magic[1] == gzip_magic[1]) - iol->compressed = true; - } - } - if (fcntl(fd, F_SETFD, FD_CLOEXEC) != -1) { -#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) { - switch ((flags & O_ACCMODE)) { - case O_WRONLY: - case O_RDWR: - iol->writable = true; - break; - } - } else { - 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)unlinkat(dfd, file, 0); - } - } - debug_return_bool(true); -} - -#ifdef HAVE_ZLIB_H -static const char * -gzstrerror(gzFile file) -{ - const char *errstr; - int errnum; - - errstr = gzerror(file, &errnum); - if (errnum == Z_ERRNO) - errstr = strerror(errno); - - return errstr; -} -#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) { - int errnum; - - /* Must check error indicator before closing. */ - if (iol->writable) { - if (gzflush(iol->fd.g, Z_SYNC_FLUSH) != Z_OK) { - ret = false; - if (errstr != NULL) - *errstr = gzstrerror(iol->fd.g); - } - } - errnum = gzclose(iol->fd.g); - if (ret && errnum != Z_OK) { - ret = false; - if (errstr != NULL) - *errstr = errnum == Z_ERRNO ? strerror(errno) : "unknown error"; - } - } 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 - ret = fseeko(iol->fd.f, offset, whence); - - //debug_return_off_t(ret); - return ret; -} - -/* - * I/O log wrapper for rewind/gzrewind. - */ -void -iolog_rewind(struct iolog_file *iol) -{ - debug_decl(iolog_rewind, SUDO_DEBUG_UTIL); - -#ifdef HAVE_ZLIB_H - if (iol->compressed) - (void)gzrewind(iol->fd.g); - else -#endif - rewind(iol->fd.f); - - debug_return; -} - -/* - * 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_get_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_get_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) != 0; - else -#endif - ret = feof(iol->fd.f) != 0; - debug_return_int(ret); -} - -void -iolog_clearerr(struct iolog_file *iol) -{ - debug_decl(iolog_eof, SUDO_DEBUG_UTIL); - -#ifdef HAVE_ZLIB_H - if (iol->compressed) - gzclearerr(iol->fd.g); - else -#endif - clearerr(iol->fd.f); - debug_return; -} - -/* - * 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); -} - -/* - * Map IOFD_* -> name. - */ -const char * -iolog_fd_to_name(int iofd) -{ - const char *ret; - 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: - ret = "unknown"; - sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: unexpected iofd %d", - __func__, iofd); - break; - } - debug_return_const_str(ret); -} diff --git a/lib/iolog/iolog_gets.c b/lib/iolog/iolog_gets.c new file mode 100644 index 000000000..0ef5a2362 --- /dev/null +++ b/lib/iolog/iolog_gets.c @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * 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) { + int errnum; + *errstr = gzerror(iol->fd.g, &errnum); + if (errnum == Z_ERRNO) + *errstr = strerror(errno); + } + } + } else +#endif + { + if ((str = fgets(buf, nbytes, iol->fd.f)) == NULL) { + if (errstr != NULL) + *errstr = strerror(errno); + } + } + debug_return_str(str); +} diff --git a/lib/iolog/iolog_mkdirs.c b/lib/iolog/iolog_mkdirs.c new file mode 100644 index 000000000..baa9eba00 --- /dev/null +++ b/lib/iolog/iolog_mkdirs.c @@ -0,0 +1,139 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_fatal.h" +#include "sudo_gettext.h" +#include "sudo_iolog.h" +#include "sudo_util.h" + +/* + * Create directory and any parent directories as needed. + */ +bool +iolog_mkdirs(char *path) +{ + const mode_t iolog_filemode = iolog_get_file_mode(); + const mode_t iolog_dirmode = iolog_get_dir_mode(); + const uid_t iolog_uid = iolog_get_uid(); + const gid_t iolog_gid = iolog_get_gid(); + bool ok = true, uid_changed = false; + struct stat sb; + mode_t omask; + int dfd; + debug_decl(iolog_mkdirs, SUDO_DEBUG_UTIL); + + dfd = open(path, O_RDONLY|O_NONBLOCK); + if (dfd == -1 && errno == EACCES) { + /* Try again as the I/O log owner (for NFS). */ + if (iolog_swapids(false)) { + dfd = open(path, O_RDONLY|O_NONBLOCK); + if (!iolog_swapids(true)) { + ok = false; + goto done; + } + } + } + if (dfd != -1 && fstat(dfd, &sb) != -1) { + if (S_ISDIR(sb.st_mode)) { + if (sb.st_uid != iolog_uid || sb.st_gid != iolog_gid) { + if (fchown(dfd, 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 (fchmod(dfd, 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; + } + + /* umask must not be more restrictive than the file modes. */ + omask = umask(ACCESSPERMS & ~(iolog_filemode|iolog_dirmode)); + + 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 = iolog_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 = iolog_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 (!iolog_swapids(true)) + ok = false; + } + + umask(omask); + +done: + if (dfd != -1) + close(dfd); + debug_return_bool(ok); +} diff --git a/lib/iolog/iolog_mkdtemp.c b/lib/iolog/iolog_mkdtemp.c new file mode 100644 index 000000000..72c180414 --- /dev/null +++ b/lib/iolog/iolog_mkdtemp.c @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_fatal.h" +#include "sudo_gettext.h" +#include "sudo_util.h" +#include "sudo_iolog.h" + +/* + * Create temporary directory and any parent directories as needed. + */ +bool +iolog_mkdtemp(char *path) +{ + const mode_t iolog_dirmode = iolog_get_dir_mode(); + const uid_t iolog_uid = iolog_get_uid(); + const gid_t iolog_gid = iolog_get_gid(); + bool ok, uid_changed = false; + debug_decl(iolog_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 = iolog_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 = iolog_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 (!iolog_swapids(true)) + ok = false; + } + debug_return_bool(ok); +} diff --git a/lib/iolog/iolog_mkpath.c b/lib/iolog/iolog_mkpath.c new file mode 100644 index 000000000..b4d177a99 --- /dev/null +++ b/lib/iolog/iolog_mkpath.c @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * Create path and any intermediate directories. + * If path ends in 'XXXXXX', use mkdtemp(). + */ +bool +iolog_mkpath(char *path) +{ + size_t len; + bool ret; + debug_decl(iolog_mkpath, SUDO_DEBUG_UTIL); + + /* + * 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. + */ + len = strlen(path); + if (len >= 6 && strcmp(&path[len - 6], "XXXXXX") == 0) + ret = iolog_mkdtemp(path); + else + ret = iolog_mkdirs(path); + + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "iolog path %s", path); + + debug_return_bool(ret); +} diff --git a/lib/iolog/iolog_nextid.c b/lib/iolog/iolog_nextid.c new file mode 100644 index 000000000..5ca0ca691 --- /dev/null +++ b/lib/iolog/iolog_nextid.c @@ -0,0 +1,143 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_fatal.h" +#include "sudo_gettext.h" +#include "sudo_iolog.h" +#include "sudo_util.h" + +/* + * 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"; + const uid_t iolog_uid = iolog_get_uid(); + const gid_t iolog_gid = iolog_get_gid(); + debug_decl(iolog_nextid, SUDO_DEBUG_UTIL); + + /* + * Create I/O log directory if it doesn't already exist. + */ + if (!iolog_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; + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: %s/seq", __func__, iolog_dir); + goto done; + } + fd = iolog_openat(AT_FDCWD, pathbuf, O_RDWR|O_CREAT); + if (fd == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to open %s", __func__, pathbuf); + goto done; + } + if (!sudo_lock_file(fd, SUDO_LOCK)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "unable to lock %s", pathbuf); + goto done; + } + 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). */ + 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 >= iolog_get_maxseq()) { + 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. */ + if (pwrite(fd, buf, 7, 0) != 7) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, + "%s: unable to write %s", __func__, pathbuf); + goto done; + } + ret = true; + +done: + if (fd != -1) + close(fd); + debug_return_bool(ret); +} diff --git a/lib/iolog/iolog_open.c b/lib/iolog/iolog_open.c new file mode 100644 index 000000000..c2556a7f8 --- /dev/null +++ b/lib/iolog/iolog_open.c @@ -0,0 +1,127 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" +#include "sudo_util.h" + +static unsigned char const gzip_magic[2] = {0x1f, 0x8b}; + +/* + * Open the specified I/O log file and store in iol. + * Stores the open file handle which has the close-on-exec flag set. + */ +bool +iolog_open(struct iolog_file *iol, int dfd, int iofd, const char *mode) +{ + int flags; + const char *file; + unsigned char magic[2]; + const uid_t iolog_uid = iolog_get_uid(); + const gid_t iolog_gid = iolog_get_gid(); + 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); + } + if ((file = iolog_fd_to_name(iofd)) == NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR, + "%s: invalid iofd %d", __func__, iofd); + debug_return_bool(false); + } + + iol->writable = false; + iol->compressed = false; + if (iol->enabled) { + int fd = iolog_openat(dfd, file, 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, file); + } + iol->compressed = iolog_get_compress(); + } else { + /* check for gzip magic number */ + if (pread(fd, magic, sizeof(magic), 0) == ssizeof(magic)) { + if (magic[0] == gzip_magic[0] && magic[1] == gzip_magic[1]) + iol->compressed = true; + } + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != -1) { +#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) { + switch ((flags & O_ACCMODE)) { + case O_WRONLY: + case O_RDWR: + iol->writable = true; + break; + } + } else { + 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)unlinkat(dfd, file, 0); + } + } + debug_return_bool(true); +} diff --git a/lib/iolog/iolog_openat.c b/lib/iolog/iolog_openat.c new file mode 100644 index 000000000..f6cd21b57 --- /dev/null +++ b/lib/iolog/iolog_openat.c @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_fatal.h" +#include "sudo_gettext.h" +#include "sudo_iolog.h" +#include "sudo_util.h" + +/* + * Wrapper for openat(2) that sets umask and retries as iolog_uid/iolog_gid + * if openat(2) returns EACCES. + */ +int +iolog_openat(int dfd, const char *path, int flags) +{ + const mode_t iolog_filemode = iolog_get_file_mode(); + const mode_t iolog_dirmode = iolog_get_dir_mode(); + mode_t omask = S_IRWXG|S_IRWXO; + int fd; + debug_decl(iolog_openat, 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 = openat(dfd, path, flags, iolog_filemode); + if (fd == -1 && errno == EACCES) { + /* Enable write bit if it is missing. */ + struct stat sb; + if (fstatat(dfd, path, &sb, 0) == 0) { + mode_t write_bits = iolog_filemode & (S_IWUSR|S_IWGRP|S_IWOTH); + if ((sb.st_mode & write_bits) != write_bits) { + if (fchmodat(dfd, path, iolog_filemode, 0) == 0) + fd = openat(dfd, path, flags, iolog_filemode); + } + } + } + if (fd == -1 && errno == EACCES) { + /* Try again as the I/O log owner (for NFS). */ + if (iolog_swapids(false)) { + fd = openat(dfd, path, flags, iolog_filemode); + if (!iolog_swapids(true)) { + /* iolog_swapids() warns on error. */ + if (fd != -1) { + close(fd); + fd = -1; + } + } + } + } + if (ISSET(flags, O_CREAT)) + umask(omask); + debug_return_int(fd); +} diff --git a/lib/iolog/iolog_read.c b/lib/iolog/iolog_read.c new file mode 100644 index 000000000..71f778a14 --- /dev/null +++ b/lib/iolog/iolog_read.c @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * 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) { + int errnum; + *errstr = gzerror(iol->fd.g, &errnum); + if (errnum == Z_ERRNO) + *errstr = strerror(errno); + } + } + } 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); +} diff --git a/lib/iolog/iolog_seek.c b/lib/iolog/iolog_seek.c new file mode 100644 index 000000000..928ae6ef6 --- /dev/null +++ b/lib/iolog/iolog_seek.c @@ -0,0 +1,69 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * 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 + ret = fseeko(iol->fd.f, offset, whence); + + //debug_return_off_t(ret); + return ret; +} + +/* + * I/O log wrapper for rewind/gzrewind. + */ +void +iolog_rewind(struct iolog_file *iol) +{ + debug_decl(iolog_rewind, SUDO_DEBUG_UTIL); + +#ifdef HAVE_ZLIB_H + if (iol->compressed) + (void)gzrewind(iol->fd.g); + else +#endif + rewind(iol->fd.f); + + debug_return; +} diff --git a/lib/iolog/iolog_swapids.c b/lib/iolog/iolog_swapids.c new file mode 100644 index 000000000..a0a361498 --- /dev/null +++ b/lib/iolog/iolog_swapids.c @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_fatal.h" +#include "sudo_gettext.h" +#include "sudo_iolog.h" + +/* + * Set effective user and group-IDs to iolog_uid and iolog_gid. + * If restore flag is set, swap them back. + */ +bool +iolog_swapids(bool restore) +{ +#ifdef HAVE_SETEUID + static uid_t user_euid = (uid_t)-1; + static gid_t user_egid = (gid_t)-1; + const uid_t iolog_uid = iolog_get_uid(); + const gid_t iolog_gid = iolog_get_gid(); + debug_decl(io_swapids, SUDO_DEBUG_UTIL); + + if (user_euid == (uid_t)-1) + user_euid = geteuid(); + if (user_egid == (gid_t)-1) + user_egid = 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 +} diff --git a/lib/iolog/iolog_util.c b/lib/iolog/iolog_util.c new file mode 100644 index 000000000..bb1dde8b0 --- /dev/null +++ b/lib/iolog/iolog_util.c @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * Map IOFD_* -> name. + */ +const char * +iolog_fd_to_name(int iofd) +{ + const char *ret; + 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: + ret = "unknown"; + sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: unexpected iofd %d", + __func__, iofd); + break; + } + debug_return_const_str(ret); +} diff --git a/lib/iolog/iolog_write.c b/lib/iolog/iolog_write.c new file mode 100644 index 000000000..82a3f6dee --- /dev/null +++ b/lib/iolog/iolog_write.c @@ -0,0 +1,105 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2021 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 +#ifdef HAVE_STDBOOL_H +# include +#else +# include "compat/stdbool.h" +#endif +#include +#include +#include +#include + +#include "sudo_compat.h" +#include "sudo_debug.h" +#include "sudo_iolog.h" + +/* + * 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) { + int errnum; + + ret = gzwrite(iol->fd.g, (const voidp)buf, len); + if (ret == 0) { + ret = -1; + if (errstr != NULL) { + *errstr = gzerror(iol->fd.g, &errnum); + if (errnum == Z_ERRNO) + *errstr = strerror(errno); + } + goto done; + } + if (iolog_get_flush()) { + if (gzflush(iol->fd.g, Z_SYNC_FLUSH) != Z_OK) { + ret = -1; + if (errstr != NULL) { + *errstr = gzerror(iol->fd.g, &errnum); + if (errnum == Z_ERRNO) + *errstr = strerror(errno); + } + 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_get_flush()) { + if (fflush(iol->fd.f) != 0) { + ret = -1; + if (errstr != NULL) + *errstr = strerror(errno); + goto done; + } + } + } + +done: + debug_return_ssize_t(ret); +} diff --git a/lib/iolog/regress/fuzz/fuzz_iolog_timing.c b/lib/iolog/regress/fuzz/fuzz_iolog_timing.c index f5fb3fffb..d92845296 100644 --- a/lib/iolog/regress/fuzz/fuzz_iolog_timing.c +++ b/lib/iolog/regress/fuzz/fuzz_iolog_timing.c @@ -91,3 +91,10 @@ cleanup: return 0; } + +/* STUB */ +bool +iolog_swapids(bool restore) +{ + return false; +} diff --git a/logsrvd/iolog_writer.c b/logsrvd/iolog_writer.c index 6f620b4ca..07a6f1168 100644 --- a/logsrvd/iolog_writer.c +++ b/logsrvd/iolog_writer.c @@ -727,6 +727,29 @@ iolog_copy(struct iolog_file *src, struct iolog_file *dst, off_t remainder, debug_return_bool(true); } +/* + * Like rename(2) but changes UID as needed. + */ +static bool +iolog_rename(const char *from, const char *to) +{ + bool ok, uid_changed = false; + debug_decl(iolog_rename, SUDO_DEBUG_UTIL); + + ok = rename(from, to) == 0; + if (!ok && errno == EACCES) { + uid_changed = iolog_swapids(false); + if (uid_changed) + ok = rename(from, to) == 0; + } + + if (uid_changed) { + if (!iolog_swapids(true)) + ok = false; + } + debug_return_bool(ok); +} + /* Compressed logs don't support random access, need to rewrite them. */ static bool iolog_rewrite(const struct timespec *target, struct connection_closure *closure) diff --git a/logsrvd/regress/fuzz/fuzz_logsrvd_conf.c b/logsrvd/regress/fuzz/fuzz_logsrvd_conf.c index daeac01a2..b1a36b11c 100644 --- a/logsrvd/regress/fuzz/fuzz_logsrvd_conf.c +++ b/logsrvd/regress/fuzz/fuzz_logsrvd_conf.c @@ -74,45 +74,3 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 0; } - -void -iolog_set_defaults(void) -{ - return; -} - -void -iolog_set_maxseq(unsigned int newval) -{ - return; -} - -void -iolog_set_owner(uid_t uid, gid_t gid) -{ - return; -} - -void -iolog_set_gid(gid_t gid) -{ - return; -} - -void -iolog_set_mode(mode_t mode) -{ - return; -} - -void -iolog_set_compress(bool newval) -{ - return; -} - -void -iolog_set_flush(bool newval) -{ - return; -}