[systemd-devel] [RFC PATCH] journal: pass uid.gid in the stream header
Zbigniew Jędrzejewski-Szmek
zbyszek at in.waw.pl
Wed Dec 31 19:40:41 PST 2014
When systemd starts a service, it first opens /run/systemd/journal/stdout
socket, and only later switches to the right user.group (if they are
specified). Later on, journald looks at the credentials, and sees
root.root, because credentials are stored at the time the socket is
opened. As a result, all messages passed over _TRANSPORT=stdout were
logged with _UID=0, _GID=0.
This patch modifies the stream protocol to pass the uid and gid, like
the unit name already was. journald will looks at the values passed in
the header only when the connection was opened by a privileged sender.
An alternative aproach would be to delay opening the socket until
after the user has been switched. But this would make things more
complicated, since some kinds of output (e.g. the console) can only be
opened as root, and also some users might not be allowed to open the
socket to the journal (as a result of InaccessibleDirectories, SELinux
rules, etc). Getting all the cases right here seems rather complicated
compared to the straightforward approach of passing the credentials in
the stream header.
A disadvantage of the solution implemented here, otoh, is that both
systemd and journald must be restarted for it to take effect.
---
Not sure if this is the right approach. Commentes and alternative/better
ideas welcome.
src/core/execute.c | 42 ++++++++++++++++++---------------
src/journal/journal-send.c | 12 ++++++----
src/journal/journald-stream.c | 55 +++++++++++++++++++++++++++++++------------
3 files changed, 71 insertions(+), 38 deletions(-)
diff --git a/src/core/execute.c b/src/core/execute.c
index a806d42827..767a86a0e1 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -219,7 +219,7 @@ static int open_null_as(int flags, int nfd) {
return r;
}
-static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) {
+static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
int fd, r;
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
@@ -255,14 +255,18 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
"%i\n"
"%i\n"
"%i\n"
- "%i\n",
+ "%i\n"
+ UID_FMT"\n"
+ GID_FMT"\n",
context->syslog_identifier ? context->syslog_identifier : ident,
unit_id,
context->syslog_priority,
!!context->syslog_level_prefix,
output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
- is_terminal_output(output));
+ is_terminal_output(output),
+ uid == UID_INVALID ? 0 : uid,
+ gid == GID_INVALID ? 0 : gid);
if (fd != nfd) {
r = dup2(fd, nfd) < 0 ? -errno : nfd;
@@ -358,7 +362,7 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
}
}
-static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
+static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin, uid_t uid, gid_t gid) {
ExecOutput o;
ExecInput i;
int r;
@@ -425,7 +429,7 @@ static int setup_output(const ExecContext *context, int fileno, int socket_fd, c
case EXEC_OUTPUT_KMSG_AND_CONSOLE:
case EXEC_OUTPUT_JOURNAL:
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- r = connect_logger_as(context, o, ident, unit_id, fileno);
+ r = connect_logger_as(context, o, ident, unit_id, fileno, uid, gid);
if (r < 0) {
log_unit_struct(unit_id,
LOG_CRIT,
@@ -1327,6 +1331,15 @@ static int exec_child(ExecCommand *command,
}
}
+ if (context->user) {
+ username = context->user;
+ err = get_user_creds(&username, &uid, &gid, &home, &shell);
+ if (err < 0) {
+ *error = EXIT_USER;
+ return err;
+ }
+ }
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
@@ -1338,13 +1351,13 @@ static int exec_child(ExecCommand *command,
return err;
}
- err = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin);
+ err = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
if (err < 0) {
*error = EXIT_STDOUT;
return err;
}
- err = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin);
+ err = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin, uid, gid);
if (err < 0) {
*error = EXIT_STDERR;
return err;
@@ -1419,21 +1432,12 @@ static int exec_child(ExecCommand *command,
if (context->utmp_id)
utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
- if (context->user) {
- username = context->user;
- err = get_user_creds(&username, &uid, &gid, &home, &shell);
+ if (context->user && is_terminal_input(context->std_input)) {
+ err = chown_terminal(STDIN_FILENO, uid);
if (err < 0) {
- *error = EXIT_USER;
+ *error = EXIT_STDIN;
return err;
}
-
- if (is_terminal_input(context->std_input)) {
- err = chown_terminal(STDIN_FILENO, uid);
- if (err < 0) {
- *error = EXIT_STDIN;
- return err;
- }
- }
}
#ifdef ENABLE_KDBUS
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index 56a96c55dd..e6e5fcd3fc 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -437,7 +437,7 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
identifier = "";
l = strlen(identifier);
- header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
+ header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2 + 2 + 2);
memcpy(header, identifier, l);
header[l++] = '\n';
@@ -446,11 +446,15 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
header[l++] = '\n';
header[l++] = '0' + !!level_prefix;
header[l++] = '\n';
- header[l++] = '0';
+ header[l++] = '0'; /* forward to syslog */
header[l++] = '\n';
- header[l++] = '0';
+ header[l++] = '0'; /* forward to kmsg */
header[l++] = '\n';
- header[l++] = '0';
+ header[l++] = '0'; /* forward to console */
+ header[l++] = '\n';
+ header[l++] = '0'; /* UID */
+ header[l++] = '\n';
+ header[l++] = '0'; /* GID */
header[l++] = '\n';
r = loop_write(fd, header, l, false);
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index be498d4919..0d368f6aa1 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -47,6 +47,8 @@ typedef enum StdoutStreamState {
STDOUT_STREAM_FORWARD_TO_SYSLOG,
STDOUT_STREAM_FORWARD_TO_KMSG,
STDOUT_STREAM_FORWARD_TO_CONSOLE,
+ STDOUT_STREAM_UID,
+ STDOUT_STREAM_GID,
STDOUT_STREAM_RUNNING
} StdoutStreamState;
@@ -68,6 +70,7 @@ struct StdoutStream {
bool forward_to_syslog:1;
bool forward_to_kmsg:1;
bool forward_to_console:1;
+ bool privileged:1;
char buffer[LINE_MAX+1];
size_t length;
@@ -160,21 +163,17 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
return log_oom();
}
- s->state = STDOUT_STREAM_UNIT_ID;
+ s->state ++;
return 0;
case STDOUT_STREAM_UNIT_ID:
- if (s->ucred.uid == 0) {
- if (isempty(p))
- s->unit_id = NULL;
- else {
- s->unit_id = strdup(p);
- if (!s->unit_id)
- return log_oom();
- }
+ if (s->privileged && !isempty(p)) {
+ s->unit_id = strdup(p);
+ if (!s->unit_id)
+ return log_oom();
}
- s->state = STDOUT_STREAM_PRIORITY;
+ s->state ++;
return 0;
case STDOUT_STREAM_PRIORITY:
@@ -184,7 +183,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
return -EINVAL;
}
- s->state = STDOUT_STREAM_LEVEL_PREFIX;
+ s->state ++;
return 0;
case STDOUT_STREAM_LEVEL_PREFIX:
@@ -195,7 +194,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
}
s->level_prefix = !!r;
- s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG;
+ s->state ++;
return 0;
case STDOUT_STREAM_FORWARD_TO_SYSLOG:
@@ -206,7 +205,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
}
s->forward_to_syslog = !!r;
- s->state = STDOUT_STREAM_FORWARD_TO_KMSG;
+ s->state ++;
return 0;
case STDOUT_STREAM_FORWARD_TO_KMSG:
@@ -217,7 +216,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
}
s->forward_to_kmsg = !!r;
- s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE;
+ s->state ++;
return 0;
case STDOUT_STREAM_FORWARD_TO_CONSOLE:
@@ -228,7 +227,31 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
}
s->forward_to_console = !!r;
- s->state = STDOUT_STREAM_RUNNING;
+ s->state ++;
+ return 0;
+
+ case STDOUT_STREAM_UID:
+ if (s->privileged) {
+ r = parse_uid(p, &s->ucred.uid);
+ if (r < 0) {
+ log_warning("Failed to parse sender UID.");
+ return -EINVAL;
+ }
+ }
+
+ s->state ++;
+ return 0;
+
+ case STDOUT_STREAM_GID:
+ if (s->privileged) {
+ r = parse_gid(p, &s->ucred.gid);
+ if (r < 0) {
+ log_warning("Failed to parse sender GID.");
+ return -EINVAL;
+ }
+ }
+
+ s->state ++;
return 0;
case STDOUT_STREAM_RUNNING:
@@ -394,6 +417,8 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
goto fail;
}
+ stream->privileged = stream->ucred.uid == 0;
+
#ifdef HAVE_SELINUX
if (mac_selinux_use()) {
if (getpeercon(fd, &stream->security_context) < 0 && errno != ENOPROTOOPT)
--
2.1.1
More information about the systemd-devel
mailing list