summaryrefslogtreecommitdiff
path: root/src/core/execute.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-08-01 19:24:40 +0200
committerLennart Poettering <lennart@poettering.net>2016-08-19 00:37:25 +0200
commit00d9ef8560c252d8504be99cb38d1a54d35a9144 (patch)
tree388323761f8f32b4ec3b83a017a8931c4fd450b9 /src/core/execute.c
parent51d73fd96a55810ca40324eec098e66c6657699b (diff)
core: add RemoveIPC= setting
This adds the boolean RemoveIPC= setting to service, socket, mount and swap units (i.e. all unit types that may invoke processes). if turned on, and the unit's user/group is not root, all IPC objects of the user/group are removed when the service is shut down. The life-cycle of the IPC objects is hence bound to the unit life-cycle. This is particularly relevant for units with dynamic users, as it is essential that no objects owned by the dynamic users survive the service exiting. In fact, this patch adds code to imply RemoveIPC= if DynamicUser= is set. In order to communicate the UID/GID of an executed process back to PID 1 this adds a new "user lookup" socket pair, that is inherited into the forked processes, and closed before the exec(). This is needed since we cannot do NSS from PID 1 due to deadlock risks, However need to know the used UID/GID in order to clean up IPC owned by it if the unit shuts down.
Diffstat (limited to 'src/core/execute.c')
-rw-r--r--src/core/execute.c46
1 files changed, 44 insertions, 2 deletions
diff --git a/src/core/execute.c b/src/core/execute.c
index 18bb421cab..4c786a2e33 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1723,11 +1723,12 @@ static int close_remaining_fds(
const ExecParameters *params,
ExecRuntime *runtime,
DynamicCreds *dcreds,
+ int user_lookup_fd,
int socket_fd,
int *fds, unsigned n_fds) {
unsigned n_dont_close = 0;
- int dont_close[n_fds + 11];
+ int dont_close[n_fds + 12];
assert(params);
@@ -1755,9 +1756,40 @@ static int close_remaining_fds(
append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
}
+ if (user_lookup_fd >= 0)
+ dont_close[n_dont_close++] = user_lookup_fd;
+
return close_all_fds(dont_close, n_dont_close);
}
+static int send_user_lookup(
+ Unit *unit,
+ int user_lookup_fd,
+ uid_t uid,
+ gid_t gid) {
+
+ assert(unit);
+
+ /* Send the resolved UID/GID to PID 1 after we learnt it. We send a single datagram, containing the UID/GID
+ * data as well as the unit name. Note that we suppress sending this if no user/group to resolve was
+ * specified. */
+
+ if (user_lookup_fd < 0)
+ return 0;
+
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return 0;
+
+ if (writev(user_lookup_fd,
+ (struct iovec[]) {
+ { .iov_base = &uid, .iov_len = sizeof(uid) },
+ { .iov_base = &gid, .iov_len = sizeof(gid) },
+ { .iov_base = unit->id, .iov_len = strlen(unit->id) }}, 3) < 0)
+ return -errno;
+
+ return 0;
+}
+
static int exec_child(
Unit *unit,
ExecCommand *command,
@@ -1769,6 +1801,7 @@ static int exec_child(
int socket_fd,
int *fds, unsigned n_fds,
char **files_env,
+ int user_lookup_fd,
int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
@@ -1815,7 +1848,7 @@ static int exec_child(
log_forget_fds();
- r = close_remaining_fds(params, runtime, dcreds, socket_fd, fds, n_fds);
+ r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
@@ -1902,6 +1935,14 @@ static int exec_child(
}
}
+ r = send_user_lookup(unit, user_lookup_fd, uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_USER;
+ return r;
+ }
+
+ user_lookup_fd = safe_close(user_lookup_fd);
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
@@ -2501,6 +2542,7 @@ int exec_spawn(Unit *unit,
socket_fd,
fds, n_fds,
files_env,
+ unit->manager->user_lookup_fds[1],
&exit_status);
if (r < 0) {
log_open();