aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2020-04-17 18:23:52 +0200
committerHarald Welte <laforge@osmocom.org>2020-04-18 15:06:59 +0200
commitf3cc731d40c223c25c81c063d0f477b0c88ca069 (patch)
treeaff5b923cbffec20645e2b75b7f1e481e3161db4
parente7d0d70ce7914eb0304f17baf46bc9f00fe634c7 (diff)
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
-rw-r--r--include/osmocom/core/exec.h1
-rw-r--r--src/exec.c50
2 files changed, 49 insertions, 2 deletions
diff --git a/include/osmocom/core/exec.h b/include/osmocom/core/exec.h
index 6bbd352c..e63ec114 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 62f59194..578e2b11 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -23,6 +23,7 @@
#include "config.h"
#ifndef EMBEDDED
+#define _GNU_SOURCE
#include <unistd.h>
#include <errno.h>
@@ -31,6 +32,7 @@
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
+#include <pwd.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
@@ -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 */