diff --git a/NEWS.adoc b/NEWS.adoc index 8da073ce8e..06cf73e1e6 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -293,6 +293,8 @@ https://github.com/networkupstools/nut/milestone/13 not do so for single-driver runs -- addressed with this release. [#3302] - common code: + * POSIX daemons now warn if their real or effective UID remains zero after + the common credential switch. [issue #3471, PR #3609] * Refactored `common::background()` method used by numerous NUT daemons to handle parent and child code paths by separately addressable methods. This got used in the shared drivers `main` code file to preclude losing diff --git a/common/common.c b/common/common.c index 8e8538cba8..919bb9d2b2 100644 --- a/common/common.c +++ b/common/common.c @@ -983,6 +983,8 @@ void become_user(struct passwd *pw) /* if we can't switch users, then don't even try */ intmax_t initial_uid = getuid(); intmax_t initial_euid = geteuid(); + intmax_t final_uid; + intmax_t final_euid; if (!pw) { upsdebugx(1, "Can not become_user(), skipped"); @@ -1019,8 +1021,16 @@ void become_user(struct passwd *pw) if (setuid(pw->pw_uid) == -1) fatal_with_errno(EXIT_FAILURE, "setuid"); + final_uid = getuid(); + final_euid = geteuid(); + if ((final_uid == 0) || (final_euid == 0)) { + upslogx(LOG_WARNING, "Warning: running as root (UID=%jd EUID=%jd)", + final_uid, final_euid); + return; + } + upsdebugx(1, "Succeeded to become_user(%s): now UID=%jd GID=%jd", - pw->pw_name, (intmax_t)getuid(), (intmax_t)getgid()); + pw->pw_name, final_uid, (intmax_t)getgid()); #else /* WIN32 */ /* NUT_WIN32_INCOMPLETE_MAYBE_NOT_APPLICABLE(); */ upsdebugx(1, "Can not become_user(%s): not implemented on this platform", diff --git a/tests/Makefile.am b/tests/Makefile.am index eef6e490bf..b2fe3dcff7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,7 +15,7 @@ SUBDIRS = . NIT # handle that properly all: $(TESTS) $(check_PROGRAMS) $(check_SCRIPTS) -EXTRA_DIST = nut-driver-enumerator-test.sh nut-driver-enumerator-test--ups.conf +EXTRA_DIST = become-user-root-warning-test.sh nut-driver-enumerator-test.sh nut-driver-enumerator-test--ups.conf EXTRA_DIST += cppunit-warnings.h cppunit-warnings-end.h TESTS = @@ -54,6 +54,9 @@ $(top_builddir)/clients/libnutclient.la \ $(top_builddir)/clients/libnutclientstub.la: dummy @dotMAKE@ +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) +$(top_builddir)/clients/upslog$(EXEEXT): dummy @dotMAKE@ + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) upslog$(EXEEXT) + # Builds from root dir arrange stuff decently. Make sure parallel builds # started from scratch right in this dir get dependencies in proper order # (sub-makes are independent as far as trying to write into same files): @@ -310,10 +313,16 @@ memcheck: @echo " SKIP $@ : valgrind was not detected on this system by configure script" >&2 endif !HAVE_VALGRIND +CHECK_LOCAL_TARGETS = become-user-root-warning-test if WITH_VALGRIND -check-local: memcheck +CHECK_LOCAL_TARGETS += memcheck endif WITH_VALGRIND +check-local: $(CHECK_LOCAL_TARGETS) + +become-user-root-warning-test: $(top_builddir)/clients/upslog$(EXEEXT) + $(AM_V_at)UPSLOG='$(abs_top_builddir)/clients/upslog$(EXEEXT)' $(SHELL) $(srcdir)/become-user-root-warning-test.sh + dummy: BUILT_SOURCES = $(LINKED_SOURCE_FILES) diff --git a/tests/become-user-root-warning-test.sh b/tests/become-user-root-warning-test.sh new file mode 100755 index 0000000000..ffcbff952d --- /dev/null +++ b/tests/become-user-root-warning-test.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if test "`id -u`" != 0; then + echo "SKIP: root privileges are required to test become_user()" >&2 + exit 0 +fi + +testdir="`mktemp -d "${TMPDIR:-/tmp}/nut-become-user-root-warning.XXXXXX"`" || exit +trap 'rm -rf "$testdir"' EXIT HUP INT TERM + +upslog=${UPSLOG-../clients/upslog} +root_user="`id -un`" || exit +warning='Warning: running as root (UID=0 EUID=0)' + +if output="`"$upslog" -F -u "$root_user" -p "$testdir/upslog" -W 1 -d 1 -i 1 \ + -s dummy@127.0.0.1:1 -l - 2>&1`"; then + count="`printf '%s\n' "$output" | grep -F "$warning" | wc -l`" + if test "$count" -eq 1; then + exit 0 + fi +fi + +printf '%s\n' "$output" >&2 +exit 1