From f3cc731d40c223c25c81c063d0f477b0c88ca069 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 17 Apr 2020 18:23:52 +0200 Subject: [PATCH] exec: Introduce osmo_system_nowait2() to allow specify a user For a process running as root, it may be desirable to drop privileges down to a normal user before executing an external command. Let's add a new API function for that. Change-Id: If1431f930f72a8d6c1d102426874a11b7a2debd9 --- include/osmocom/core/exec.h | 1 + src/exec.c | 50 +++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/include/osmocom/core/exec.h b/include/osmocom/core/exec.h index 6bbd352c2..e63ec114c 100644 --- a/include/osmocom/core/exec.h +++ b/include/osmocom/core/exec.h @@ -25,4 +25,5 @@ extern const char *osmo_environment_whitelist[]; int osmo_environment_filter(char **out, size_t out_len, char **in, const char **whitelist); int osmo_environment_append(char **out, size_t out_len, char **in); int osmo_close_all_fds_above(int last_fd_to_keep); +int osmo_system_nowait2(const char *command, const char **env_whitelist, char **addl_env, const char *user); int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env); diff --git a/src/exec.c b/src/exec.c index 62f591943..578e2b116 100644 --- a/src/exec.c +++ b/src/exec.c @@ -23,6 +23,7 @@ #include "config.h" #ifndef EMBEDDED +#define _GNU_SOURCE #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include @@ -192,23 +194,34 @@ int osmo_close_all_fds_above(int last_fd_to_keep) /* Seems like POSIX has no header file for this, and even glibc + __USE_GNU doesn't help */ extern char **environ; -/*! call an external shell command without waiting for it. +/*! call an external shell command as 'user' without waiting for it. * * This mimics the behavior of system(3), with the following differences: * - it doesn't wait for completion of the child process * - it closes all non-stdio file descriptors by iterating /proc/self/fd * - it constructs a reduced environment where only whitelisted keys survive * - it (optionally) appends additional variables to the environment + * - it (optionally) changes the user ID to that of 'user' (requires execution as root) * * \param[in] command the shell command to be executed, see system(3) * \param[in] env_whitelist A white-list of keys for environment variables * \param[in] addl_env any additional environment variables to be appended + * \param[in] user name of the user to which we should switch before executing the command * \returns PID of generated child process; negative on error */ -int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env) +int osmo_system_nowait2(const char *command, const char **env_whitelist, char **addl_env, const char *user) { + struct passwd _pw, *pw; + int getpw_buflen = sysconf(_SC_GETPW_R_SIZE_MAX); int rc; + if (user) { + char buf[getpw_buflen]; + getpwnam_r(user, &_pw, buf, sizeof(buf), &pw); + if (!pw) + return -EINVAL; + } + rc = fork(); if (rc == 0) { /* we are in the child */ @@ -232,6 +245,20 @@ int osmo_system_nowait(const char *command, const char **env_whitelist, char **a return rc; } + /* drop privileges */ + if (pw) { + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0) { + perror("setresgid() during privilege drop"); + exit(1); + } + + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) { + perror("setresuid() during privilege drop"); + exit(1); + } + + } + /* if we want to behave like system(3), we must go via the shell */ execle("/bin/sh", "sh", "-c", command, (char *) NULL, new_env); /* only reached in case of error */ @@ -244,4 +271,23 @@ int osmo_system_nowait(const char *command, const char **env_whitelist, char **a } } +/*! call an external shell command without waiting for it. + * + * This mimics the behavior of system(3), with the following differences: + * - it doesn't wait for completion of the child process + * - it closes all non-stdio file descriptors by iterating /proc/self/fd + * - it constructs a reduced environment where only whitelisted keys survive + * - it (optionally) appends additional variables to the environment + * + * \param[in] command the shell command to be executed, see system(3) + * \param[in] env_whitelist A white-list of keys for environment variables + * \param[in] addl_env any additional environment variables to be appended + * \returns PID of generated child process; negative on error + */ +int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env) +{ + return osmo_system_nowait2(command, env_whitelist, addl_env, NULL); +} + + #endif /* EMBEDDED */