|
|
@ -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 <sys/types.h> |
|
|
|
#include <grp.h> |
|
|
|
|
|
|
|
#if USE_SYSLOG |
|
|
|
|
|
|
|
# include <syslog.h> |
|
|
|
# include <errno.h> |
|
|
|
|
|
|
|
#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; |
|
|
|
|
|
|
|