diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-06-05 09:55:53 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-06-05 09:55:53 +0200 |
commit | 3900e5fdff688dc3c273f177d9d913b7389d5561 (patch) | |
tree | 7752c89ee7d52087e03fc915594ee54c38bc32cb /src/core/socket.c | |
parent | a8330cd118993c20629565684144e0cc0e2edabe (diff) |
socket: add SocketUser= and SocketGroup= for chown()ing sockets in the file system
This is relatively complex, as we cannot invoke NSS from PID 1, and thus
need to fork a helper process temporarily.
Diffstat (limited to 'src/core/socket.c')
-rw-r--r-- | src/core/socket.c | 282 |
1 files changed, 205 insertions, 77 deletions
diff --git a/src/core/socket.c b/src/core/socket.c index c158aaf1de..e834b045a0 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -55,6 +55,7 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, [SOCKET_START_PRE] = UNIT_ACTIVATING, + [SOCKET_START_CHOWN] = UNIT_ACTIVATING, [SOCKET_START_POST] = UNIT_ACTIVATING, [SOCKET_LISTENING] = UNIT_ACTIVE, [SOCKET_RUNNING] = UNIT_ACTIVE, @@ -147,6 +148,9 @@ static void socket_done(Unit *u) { strv_free(s->symlinks); + free(s->user); + free(s->group); + s->timer_event_source = sd_event_source_unref(s->timer_event_source); } @@ -592,6 +596,13 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sSmackLabelIPOut: %s\n", prefix, s->smack_ip_out); + if (!isempty(s->user) || !isempty(s->group)) + fprintf(f, + "%sOwnerUser: %s\n" + "%sOwnerGroup: %s\n", + prefix, strna(s->user), + prefix, strna(s->group)); + LIST_FOREACH(port, p, s->ports) { if (p->type == SOCKET_SOCKET) { @@ -1207,6 +1218,7 @@ static void socket_set_state(Socket *s, SocketState state) { if (!IN_SET(state, SOCKET_START_PRE, + SOCKET_START_CHOWN, SOCKET_START_POST, SOCKET_STOP_PRE, SOCKET_STOP_PRE_SIGTERM, @@ -1225,6 +1237,7 @@ static void socket_set_state(Socket *s, SocketState state) { socket_unwatch_fds(s); if (!IN_SET(state, + SOCKET_START_CHOWN, SOCKET_START_POST, SOCKET_LISTENING, SOCKET_RUNNING, @@ -1250,14 +1263,16 @@ static int socket_coldplug(Unit *u) { if (s->deserialized_state == s->state) return 0; - if (s->deserialized_state == SOCKET_START_PRE || - s->deserialized_state == SOCKET_START_POST || - s->deserialized_state == SOCKET_STOP_PRE || - s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || - s->deserialized_state == SOCKET_STOP_PRE_SIGKILL || - s->deserialized_state == SOCKET_STOP_POST || - s->deserialized_state == SOCKET_FINAL_SIGTERM || - s->deserialized_state == SOCKET_FINAL_SIGKILL) { + if (IN_SET(s->deserialized_state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) { if (s->control_pid <= 0) return -EBADMSG; @@ -1271,12 +1286,14 @@ static int socket_coldplug(Unit *u) { return r; } - if (s->deserialized_state == SOCKET_START_POST || - s->deserialized_state == SOCKET_LISTENING || - s->deserialized_state == SOCKET_RUNNING || - s->deserialized_state == SOCKET_STOP_PRE || - s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || - s->deserialized_state == SOCKET_STOP_PRE_SIGKILL) { + if (IN_SET(s->deserialized_state, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_LISTENING, + SOCKET_RUNNING, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL)) { r = socket_open_fds(s); if (r < 0) return r; @@ -1293,9 +1310,9 @@ static int socket_coldplug(Unit *u) { } static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { + _cleanup_free_ char **argv = NULL; pid_t pid; int r; - char **argv; assert(s); assert(c); @@ -1333,22 +1350,100 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { s->exec_runtime, &pid); - strv_free(argv); + r = unit_watch_pid(UNIT(s), pid); if (r < 0) + /* FIXME: we need to do something here */ goto fail; + *_pid = pid; + return 0; + +fail: + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + return r; +} + +static int socket_chown(Socket *s, pid_t *_pid) { + pid_t pid; + int r; + + r = socket_arm_timer(s); + if (r < 0) + goto fail; + + /* We have to resolve the user names out-of-process, hence + * let's fork here. It's messy, but well, what can we do? */ + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + SocketPort *p; + uid_t uid = (uid_t) -1; + gid_t gid = (gid_t) -1; + int ret; + + default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); + ignore_signals(SIGPIPE, -1); + log_forget_fds(); + + if (!isempty(s->user)) { + const char *user = s->user; + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + ret = EXIT_USER; + goto fail_child; + } + } + + if (!isempty(s->group)) { + const char *group = s->group; + + r = get_group_creds(&group, &gid); + if (r < 0) { + ret = EXIT_GROUP; + goto fail_child; + } + } + + LIST_FOREACH(port, p, s->ports) { + const char *path; + + if (p->type == SOCKET_SOCKET) + path = socket_address_get_path(&p->address); + else if (p->type == SOCKET_FIFO) + path = p->path; + + if (!path) + continue; + + if (chown(path, uid, gid) < 0) { + r = -errno; + ret = EXIT_CHOWN; + goto fail_child; + } + } + + _exit(0); + + fail_child: + log_open(); + log_error("Failed to chown socket at step %s: %s", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD), strerror(-r)); + + _exit(ret); + } + r = unit_watch_pid(UNIT(s), pid); if (r < 0) - /* FIXME: we need to do something here */ goto fail; *_pid = pid; - return 0; fail: s->timer_event_source = sd_event_source_unref(s->timer_event_source); - return r; } @@ -1376,11 +1471,12 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { s->result = f; socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_STOP_POST; + s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]; - if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) goto fail; socket_set_state(s, SOCKET_STOP_POST); @@ -1448,11 +1544,12 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { s->result = f; socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_STOP_PRE; + s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]; - if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) goto fail; socket_set_state(s, SOCKET_STOP_PRE); @@ -1487,26 +1584,53 @@ static void socket_enter_start_post(Socket *s) { int r; assert(s); + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_POST; + s->control_command = s->exec_command[SOCKET_EXEC_START_POST]; + + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) { + log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + goto fail; + } + + socket_set_state(s, SOCKET_START_POST); + } else + socket_enter_listening(s); + + return; + +fail: + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_start_chown(Socket *s) { + int r; + + assert(s); + r = socket_open_fds(s); if (r < 0) { log_warning_unit(UNIT(s)->id, "%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); goto fail; } - socket_unwatch_control_pid(s); + if (!isempty(s->user) || !isempty(s->group)) { - s->control_command_id = SOCKET_EXEC_START_POST; + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_CHOWN; + s->control_command = NULL; - if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { - r = socket_spawn(s, s->control_command, &s->control_pid); + r = socket_chown(s, &s->control_pid); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, "%s failed to fork 'start-chown' task: %s", UNIT(s)->id, strerror(-r)); goto fail; } - socket_set_state(s, SOCKET_START_POST); + socket_set_state(s, SOCKET_START_CHOWN); } else - socket_enter_listening(s); + socket_enter_start_post(s); return; @@ -1519,22 +1643,23 @@ static void socket_enter_start_pre(Socket *s) { assert(s); socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_START_PRE; + s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]; - if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) { + if (s->control_command) { r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) + if (r < 0) { + log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); goto fail; + } socket_set_state(s, SOCKET_START_PRE); } else - socket_enter_start_post(s); + socket_enter_start_chown(s); return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } @@ -1709,16 +1834,19 @@ static int socket_start(Unit *u) { /* We cannot fulfill this request right now, try again later * please! */ - if (s->state == SOCKET_STOP_PRE || - s->state == SOCKET_STOP_PRE_SIGKILL || - s->state == SOCKET_STOP_PRE_SIGTERM || - s->state == SOCKET_STOP_POST || - s->state == SOCKET_FINAL_SIGTERM || - s->state == SOCKET_FINAL_SIGKILL) + if (IN_SET(s->state, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) return -EAGAIN; - if (s->state == SOCKET_START_PRE || - s->state == SOCKET_START_POST) + if (IN_SET(s->state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST)) return 0; /* Cannot run this without the service being around */ @@ -1764,18 +1892,21 @@ static int socket_stop(Unit *u) { assert(s); /* Already on it */ - if (s->state == SOCKET_STOP_PRE || - s->state == SOCKET_STOP_PRE_SIGTERM || - s->state == SOCKET_STOP_PRE_SIGKILL || - s->state == SOCKET_STOP_POST || - s->state == SOCKET_FINAL_SIGTERM || - s->state == SOCKET_FINAL_SIGKILL) + if (IN_SET(s->state, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) return 0; /* If there's already something running we go directly into * kill mode. */ - if (s->state == SOCKET_START_PRE || - s->state == SOCKET_START_POST) { + if (IN_SET(s->state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST)) { socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS); return -EAGAIN; } @@ -2191,11 +2322,18 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SOCKET_START_PRE: if (f == SOCKET_SUCCESS) - socket_enter_start_post(s); + socket_enter_start_chown(s); else socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f); break; + case SOCKET_START_CHOWN: + if (f == SOCKET_SUCCESS) + socket_enter_start_post(s); + else + socket_enter_stop_pre(s, f); + break; + case SOCKET_START_POST: if (f == SOCKET_SUCCESS) socket_enter_listening(s); @@ -2233,65 +2371,53 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use switch (s->state) { case SOCKET_START_PRE: - log_warning_unit(UNIT(s)->id, - "%s starting timed out. Terminating.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s starting timed out. Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; + case SOCKET_START_CHOWN: case SOCKET_START_POST: - log_warning_unit(UNIT(s)->id, - "%s starting timed out. Stopping.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s starting timed out. Stopping.", UNIT(s)->id); socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE: - log_warning_unit(UNIT(s)->id, - "%s stopping timed out. Terminating.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out. Killing.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out. Skipping SIGKILL. Ignoring.", - UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_STOP_PRE_SIGKILL: - log_warning_unit(UNIT(s)->id, - "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_POST: - log_warning_unit(UNIT(s)->id, - "%s stopping timed out (2). Terminating.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_FINAL_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out (2). Killing.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Killing.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, - "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", - UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_FINAL_SIGKILL: - log_warning_unit(UNIT(s)->id, - "%s still around after SIGKILL (2). Entering failed mode.", - UNIT(s)->id); + log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL (2). Entering failed mode.", UNIT(s)->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); break; @@ -2440,6 +2566,7 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) { static const char* const socket_state_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = "dead", [SOCKET_START_PRE] = "start-pre", + [SOCKET_START_CHOWN] = "start-chown", [SOCKET_START_POST] = "start-post", [SOCKET_LISTENING] = "listening", [SOCKET_RUNNING] = "running", @@ -2456,6 +2583,7 @@ DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState); static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", + [SOCKET_EXEC_START_CHOWN] = "StartChown", [SOCKET_EXEC_START_POST] = "StartPost", [SOCKET_EXEC_STOP_PRE] = "StopPre", [SOCKET_EXEC_STOP_POST] = "StopPost" |