From a65ce431b90a1ad9e2254f18f29b1396edf89bc8 Mon Sep 17 00:00:00 2001 From: Oliver Sturm Date: Fri, 23 Oct 2009 10:31:22 +0200 Subject: [PATCH] Add syslog logging --- README | 11 +++-- execwrap.c | 110 +++++++++++++++++++++++++++++++++++++++------- execwrap_config.h | 3 ++ 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/README b/README index 6bf86e8..6d320de 100644 --- a/README +++ b/README @@ -70,11 +70,13 @@ DEFAULT_UID The default (numeric) UID to become, if no UID is given vi DEFAULT_GID The same as DEFAULT_UID, but for the GID. The same reasons and restrictions apply. -REQUIRE_PWENT If set, target users are reuired to have passwd-entries. These are - used to set the supplementary group access list. Defaults to 0. +REQUIRE_PWENT If set, target users are reuired to have passwd-entries. These are + used to set the supplementary group access list. Defaults to 0. -ALLOW_CHECKGID If set, enabled the CHECK_GID feature, q.v. Defaults to 1. +ALLOW_CHECKGID If set, enabled the CHECK_GID feature, q.v. Defaults to 1. +USE_SYSLOG If set, log errors and (if enabled) debug messages to syslog. + Defaults to 1. The following configuration parameters shouldn't normally be changed. In case you have a clash with some other ENV arguments to the target program, they can be changed, however. @@ -99,6 +101,8 @@ ENV_CHECK_GID The same as ENV_UID, but controls the name of the CHECK_GI ENV_NON_RESIDENT The same as ENV_UID, but controls the name of the NON_RESIDENT parameter. Defaults to "NON_RESIDENT=". +ENV_DEBUG Controls the name of environment variable used to enable debug + output to syslog (depends on USE_SYSLOG) --------------------- COMPILE AND INSTALL @@ -161,6 +165,7 @@ NON_RESIDENT If set (to anything), the wrapper will drop privileges and web-server) to terminate the target process, and thus prevents it from effectively managing it. +DEBUG If set to anything, debug output to syslog is enabled. ----------------------------------- SECURITY CHECKS AND THEIR REASONS diff --git a/execwrap.c b/execwrap.c index 4ad3c49..b56eba1 100644 --- a/execwrap.c +++ b/execwrap.c @@ -63,6 +63,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define ENV_TARGET "TARGET=" #define ENV_CHECK_GID "CHECK_GID=" #define ENV_NON_RESIDENT "NON_RESIDENT=" +#define ENV_DEBUG "DEBUG" /* Return values for various errors. */ @@ -126,6 +127,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#if USE_SYSLOG + +# include +# include + +#else /* USE_SYSLOG */ + +/* this should be after all includes */ +# undef SKIP +# undef openlog +# undef setlogmask +# undef syslog + +# define SKIP do { } while (0) +# define openlog(...) SKIP +# define setlogmask(...) SKIP +# define syslog(...) SKIP + +#endif /* USE_SYSLOG */ + /* The global child PID and previous SIGTERM handler. */ int pid; void (*oldHandler)(int); @@ -147,9 +168,15 @@ void sigTermHandler(int signal) /* Down to business. */ int main(int argc, char* argv[], char* envp[]) { + openlog("execwrap", LOG_PID, LOG_AUTHPRIV); + setlogmask(LOG_UPTO(LOG_NOTICE)); /* Verify parent UID. Only the super user and PARENT_UID are allowed. */ - if(getuid() != 0 && getuid() != PARENT_UID) return RC_CALLER_UID; + int myuid = getuid(); + if(myuid != 0 && myuid != PARENT_UID) { + syslog(LOG_ERR, "exiting with RC_CALLER_UID, UID=%d", myuid); + return RC_CALLER_UID; + } /* Command line options. */ if(argc > 1) @@ -176,6 +203,13 @@ int main(int argc, char* argv[], char* envp[]) return RC_BAD_OPTION; } + /* we need this check before the loop over the environment, + as we cannot rely on it to be the first env var we find */ + if (NULL != getenv(ENV_DEBUG)) { + setlogmask(LOG_UPTO(LOG_DEBUG)); + syslog(LOG_DEBUG, "activated debug output"); + } + /* Grab stuff from environment, and set defaults. */ uid_t uid = DEFAULT_UID; gid_t gid = DEFAULT_GID; @@ -186,19 +220,26 @@ int main(int argc, char* argv[], char* envp[]) char* s; while(NULL != (s = *p++)) { - /* Target UID. */ if(!strncmp(ENV_UID, s, ENV_UID_LEN)) { uid = atoi(s + ENV_UID_LEN); - if(uid < TARGET_MIN_UID) return RC_TARGET_UID; + if(uid < TARGET_MIN_UID) { + syslog(LOG_ERR, "exiting with RC_TARGET_UID, UID=%d", uid); + return RC_TARGET_UID; + } + syslog(LOG_DEBUG, "target UID is %d", uid); } /* Target GID. */ if(!strncmp(ENV_GID, s, ENV_GID_LEN)) { gid = atoi(s + ENV_GID_LEN); - if(gid < TARGET_MIN_GID) return RC_TARGET_GID; + if(gid < TARGET_MIN_GID) { + syslog(LOG_ERR, "exiting with RC_TARGET_GID, GID=%d", gid); + return RC_TARGET_GID; + } + syslog(LOG_DEBUG, "target GID is %d", gid); } /* Target script. */ @@ -206,7 +247,11 @@ int main(int argc, char* argv[], char* envp[]) { target = s + ENV_TARGET_LEN; if((target[0] != '/') || strchr(target, '~') || strstr(target, "..") || - strncmp(TARGET_PATH_PREFIX, target, TARGET_PATH_PREFIX_LEN)) return RC_TARGET; + strncmp(TARGET_PATH_PREFIX, target, TARGET_PATH_PREFIX_LEN)) { + syslog(LOG_ERR, "exiting with RC_TARGET, target=%s", target); + return RC_TARGET; + } + syslog(LOG_DEBUG, "TARGET is %s", target); } /* Check GID instead of UID. */ @@ -226,14 +271,24 @@ int main(int argc, char* argv[], char* envp[]) } /* See if we got all we need. */ - if(!target) return RC_MISSING_CONFIG; + if(!target) { + syslog(LOG_ERR, "exiting with RC_MISSING_CONFIG, no TARGET"); + return RC_MISSING_CONFIG; + } /* Fetch user information from passwd. */ struct passwd *pwent = getpwuid(uid); #if REQUIRE_PWENT - if(!pwent) return RC_MISSING_PWENT; + if(!pwent) { + syslog(LOG_ERR, "exiting with RC_MISSING_PWENT, pwent required by config, but not found"); + return RC_MISSING_PWENT; + } #endif + if (pwent) { + syslog(LOG_DEBUG, "pwent found, pw_name=%s", pwent->pw_name); + } + /* Install the SIGTERM handler. */ if(!non_resident) { @@ -246,23 +301,41 @@ int main(int argc, char* argv[], char* envp[]) { /* We're in the child. Drop privileges and set the group list. */ - if(pwent && initgroups(pwent->pw_name, gid)) return RC_SETGID; - if(setgid(gid)) return RC_SETGID; - if(setuid(uid)) return RC_SETUID; + if(pwent && initgroups(pwent->pw_name, gid)) { + syslog(LOG_ERR, "exiting with RC_SETGID, initgroup failed, errno=%d", errno); + return RC_SETGID; + } + if(setgid(gid)) { + syslog(LOG_ERR, "exiting with RC_SETGID, setgid failed, errno=%d", errno); + return RC_SETGID; + } + if(setuid(uid)) { + syslog(LOG_ERR, "exiting with RC_SETUID, setuid failed, errno=%d", errno); + return RC_SETUID; + } /* Stat the target script. */ char uid_ok = 1; struct stat stat_buf; - if(stat(target, &stat_buf)) return RC_STAT; + if(stat(target, &stat_buf)) { + syslog(LOG_ERR, "exiting with RC_STAT, stat failed, errno=%d", errno); + return RC_STAT; + } int modes = stat_buf.st_mode; /* Never allow world-write. */ - if(modes & S_IWOTH) return RC_WORLD_WRITE; + if(modes & S_IWOTH) { + syslog(LOG_ERR, "exiting with RC_WORLD_WRITE, modes=%d", modes); + return RC_WORLD_WRITE; + } /* Only allow user miss-match if check_gid is set. */ if(uid != stat_buf.st_uid) { - if(!check_gid) return RC_WRONG_USER; + if(!check_gid) { + syslog(LOG_ERR, "exiting with RC_WRONG_USER"); + return RC_WRONG_USER; + } uid_ok = 0; } @@ -270,11 +343,18 @@ int main(int argc, char* argv[], char* envp[]) Also, don't allow if neither user or group match. */ if(gid != stat_buf.st_gid) { - if(modes & S_IWGRP) return RC_GROUP_WRITE; - if(!uid_ok) return RC_WRONG_GROUP; + if(modes & S_IWGRP) { + syslog(LOG_ERR, "exiting with RC_GROUP_WRITE, modes=%d", modes); + return RC_GROUP_WRITE; + } + if(!uid_ok) { + syslog(LOG_ERR, "exiting with RC_WRONG_GROUP"); + return RC_WRONG_GROUP; + } } /* All checks passed, let's become the target! */ + syslog(LOG_NOTICE, "executing target=%s, UID=%d, GID=%d", target, uid, gid); execl(target, target, NULL); return RC_EXEC; diff --git a/execwrap_config.h b/execwrap_config.h index a0eb7e5..76c5de6 100644 --- a/execwrap_config.h +++ b/execwrap_config.h @@ -32,3 +32,6 @@ See the README for documentation. /* Allow use of the CHECK_GID mode? */ #define ALLOW_CHECKGID 1 + +/* Use syslog to report errors */ +#define USE_SYSLOG 1