execute: set working directory to /root if User= is not set, but WorkingDirectory...
authorLennart Poettering <lennart@poettering.net>
Thu, 9 Feb 2017 10:58:39 +0000 (11:58 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 9 Feb 2017 12:17:58 +0000 (13:17 +0100)
Or actually, try to to do the right thing depending on what is
available:

- If we know $HOME from User=, then use that.
- If the UID for the service is 0, hardcode that WorkingDirectory=~ means WorkingDirectory=/root
- In any other case (which will be the unprivileged --user case), use
  get_home_dir() to find the $HOME of the user we are running as.
- Otherwise fail.

Fixes: #5246 #5124

src/core/execute.c

index 1e32697..492df2f 100644 (file)
@@ -2017,14 +2017,18 @@ static int apply_working_directory(
                 const char *home,
                 const bool needs_mount_ns) {
 
-        const char *d;
-        const char *wd;
+        const char *d, *wd;
 
         assert(context);
 
-        if (context->working_directory_home)
+        if (context->working_directory_home) {
+
+                if (!home)
+                        return -ENXIO;
+
                 wd = home;
-        else if (context->working_directory)
+
+        } else if (context->working_directory)
                 wd = context->working_directory;
         else
                 wd = "/";
@@ -2181,6 +2185,35 @@ static int send_user_lookup(
         return 0;
 }
 
+static int acquire_home(const ExecContext *c, uid_t uid, const char** home, char **buf) {
+        int r;
+
+        assert(c);
+        assert(home);
+        assert(buf);
+
+        /* If WorkingDirectory=~ is set, try to acquire a usable home directory. */
+
+        if (*home)
+                return 0;
+
+        if (!c->working_directory_home)
+                return 0;
+
+        if (uid == 0) {
+                /* Hardcode /root as home directory for UID 0 */
+                *home = "/root";
+                return 1;
+        }
+
+        r = get_home_dir(buf);
+        if (r < 0)
+                return r;
+
+        *home = *buf;
+        return 1;
+}
+
 static int exec_child(
                 Unit *unit,
                 ExecCommand *command,
@@ -2198,7 +2231,7 @@ static int exec_child(
                 char **error_message) {
 
         _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
-        _cleanup_free_ char *mac_selinux_context_net = NULL;
+        _cleanup_free_ char *mac_selinux_context_net = NULL, *home_buffer = NULL;
         _cleanup_free_ gid_t *supplementary_gids = NULL;
         const char *username = NULL, *groupname = NULL;
         const char *home = NULL, *shell = NULL;
@@ -2351,6 +2384,13 @@ static int exec_child(
 
         user_lookup_fd = safe_close(user_lookup_fd);
 
+        r = acquire_home(context, uid, &home, &home_buffer);
+        if (r < 0) {
+                *exit_status = EXIT_CHDIR;
+                *error_message = strdup("Failed to determine $HOME for user");
+                return r;
+        }
+
         /* If a socket is connected to STDIN/STDOUT/STDERR, we
          * must sure to drop O_NONBLOCK */
         if (socket_fd >= 0)