chiark / gitweb /
core: make EPERM errors when applying OOM adjustment for forked processes non-fatal
[elogind.git] / src / core / execute.c
index c472dadfeda734e73c981a304f6c3c3685c8c0cd..63d295cf41f372a2b814edbb78715fc921bc5ed4 100644 (file)
@@ -219,12 +219,52 @@ 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) {
-        int fd, r;
+static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
         union sockaddr_union sa = {
                 .un.sun_family = AF_UNIX,
                 .un.sun_path = "/run/systemd/journal/stdout",
         };
+        uid_t olduid = UID_INVALID;
+        gid_t oldgid = GID_INVALID;
+        int r;
+
+        if (gid != GID_INVALID) {
+                oldgid = getgid();
+
+                r = setegid(gid);
+                if (r < 0)
+                        return -errno;
+        }
+
+        if (uid != UID_INVALID) {
+                olduid = getuid();
+
+                r = seteuid(uid);
+                if (r < 0) {
+                        r = -errno;
+                        goto restore_gid;
+                }
+        }
+
+        r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
+        if (r < 0)
+                r = -errno;
+
+        /* If we fail to restore the uid or gid, things will likely
+           fail later on. This should only happen if an LSM interferes. */
+
+        if (uid != UID_INVALID)
+                (void) seteuid(olduid);
+
+ restore_gid:
+        if (gid != GID_INVALID)
+                (void) setegid(oldgid);
+
+        return r;
+}
+
+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;
 
         assert(context);
         assert(output < _EXEC_OUTPUT_MAX);
@@ -235,11 +275,9 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
         if (fd < 0)
                 return -errno;
 
-        r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
-        if (r < 0) {
-                safe_close(fd);
-                return -errno;
-        }
+        r = connect_journal_socket(fd, uid, gid);
+        if (r < 0)
+                return r;
 
         if (shutdown(fd, SHUT_RD) < 0) {
                 safe_close(fd);
@@ -358,7 +396,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,10 +463,10 @@ 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,
+                                        LOG_ERR,
                                         LOG_MESSAGE("Failed to connect %s of %s to the journal socket: %s",
                                                     fileno == STDOUT_FILENO ? "stdout" : "stderr",
                                                     unit_id, strerror(-r)),
@@ -759,7 +797,7 @@ static int setup_pam(
          * daemon. We do things this way to ensure that the main PID
          * of the daemon is the one we initially fork()ed. */
 
-        if (log_get_max_level() < LOG_PRI(LOG_DEBUG))
+        if (log_get_max_level() < LOG_DEBUG)
                 flags |= PAM_SILENT;
 
         pam_code = pam_start(name, user, &conv, &handle);
@@ -1327,6 +1365,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 +1385,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;
@@ -1359,12 +1406,16 @@ static int exec_child(ExecCommand *command,
         }
 
         if (context->oom_score_adjust_set) {
-                char t[16];
+                char t[DECIMAL_STR_MAX(context->oom_score_adjust)];
 
-                snprintf(t, sizeof(t), "%i", context->oom_score_adjust);
-                char_array_0(t);
+                /* When we can't make this change due to EPERM, then
+                 * let's silently skip over it. User namespaces
+                 * prohibit write access to this file, and we
+                 * shouldn't trip up over that. */
 
-                if (write_string_file("/proc/self/oom_score_adj", t) < 0) {
+                sprintf(t, "%i", context->oom_score_adjust);
+                err = write_string_file("/proc/self/oom_score_adj", t);
+                if (err < 0 && err != -EPERM) {
                         *error = EXIT_OOM_ADJUST;
                         return -errno;
                 }
@@ -1419,21 +1470,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
@@ -1744,7 +1786,7 @@ static int exec_child(ExecCommand *command,
 
         final_env = strv_env_clean(final_env);
 
-        if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
+        if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
                 _cleanup_free_ char *line;
 
                 line = exec_command_line(final_argv);
@@ -2026,6 +2068,17 @@ void exec_command_free_array(ExecCommand **c, unsigned n) {
                 c[i] = exec_command_free_list(c[i]);
 }
 
+typedef struct InvalidEnvInfo {
+        const char *unit_id;
+        const char *path;
+} InvalidEnvInfo;
+
+static void invalid_env(const char *p, void *userdata) {
+        InvalidEnvInfo *info = userdata;
+
+        log_unit_error(info->unit_id, "Ignoring invalid environment assignment '%s': %s", p, info->path);
+}
+
 int exec_context_load_environment(const ExecContext *c, const char *unit_id, char ***l) {
         char **i, **r = NULL;
 
@@ -2082,8 +2135,14 @@ int exec_context_load_environment(const ExecContext *c, const char *unit_id, cha
                                 return k;
                         }
                         /* Log invalid environment variables with filename */
-                        if (p)
-                                p = strv_env_clean_log(p, unit_id, pglob.gl_pathv[n]);
+                        if (p) {
+                                InvalidEnvInfo info = {
+                                        .unit_id = unit_id,
+                                        .path = pglob.gl_pathv[n]
+                                };
+
+                                p = strv_env_clean_with_callback(p, invalid_env, &info);
+                        }
 
                         if (r == NULL)
                                 r = p;