[systemd-devel] [PATCH] nspawn: spawn shell under specified --user

Michal Vyskocil mvyskocil at suse.cz
Fri Jun 24 05:39:50 PDT 2011


Add -u/--user option, which changes the effective and real user and
group id to the new value. The user must exists in the chroot, otherwise
it will fail. Both username and user id are accepted.

It also setup HOME, USER, LOGNAME and SHELL variables from /etc/passwd
entries and create the user home, which will be the working dir after
chroot.

The use case for it might be building packages inside systemd enhanced
chroot under different user than root.
---
 man/systemd-nspawn.xml |   11 +++++
 src/nspawn.c           |  109 +++++++++++++++++++++++++++++++++++++++++++++--
 src/util.c             |   14 ++++++
 src/util.h             |    2 +
 4 files changed, 131 insertions(+), 5 deletions(-)

diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index e1b33f7..0addf03 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -138,6 +138,17 @@
                                 directory will be
                                 used.</para></listitem>
                         </varlistentry>
+                        
+                        <varlistentry>
+                                <term><option>--user=</option></term>
+                                <term><option>--u</option></term>
+
+                                <listitem><para>Run the command under
+                                specified user name or user id. It also
+                                change the HOME, TERM, USER and LOGNAME
+                                variables and create and cd into home dir.
+                                </para></listitem>
+                        </varlistentry>
 
                 </variablelist>
 
diff --git a/src/nspawn.c b/src/nspawn.c
index b5908d6..17bd21b 100644
--- a/src/nspawn.c
+++ b/src/nspawn.c
@@ -36,6 +36,9 @@
 #include <sys/epoll.h>
 #include <termios.h>
 #include <sys/signalfd.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include "log.h"
 #include "util.h"
@@ -45,13 +48,15 @@
 #include "strv.h"
 
 static char *arg_directory = NULL;
+static char *arg_user = NULL;
 
 static int help(void) {
 
         printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
                "Spawn a minimal namespace container for debugging, testing and building.\n\n"
                "  -h --help            Show this help\n"
-               "  -D --directory=NAME  Root directory for the container\n",
+               "  -D --directory=NAME  Root directory for the container\n"
+               "  -u --user=USER       Run the command under specified user or uid\n",
                program_invocation_short_name);
 
         return 0;
@@ -62,6 +67,7 @@ static int parse_argv(int argc, char *argv[]) {
         static const struct option options[] = {
                 { "help",      no_argument,       NULL, 'h' },
                 { "directory", required_argument, NULL, 'D' },
+                { "user",      optional_argument, NULL, 'u' },
                 { NULL,        0,                 NULL, 0   }
         };
 
@@ -70,7 +76,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hD:", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "+hD:u:", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -87,6 +93,15 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case 'u':
+                        free(arg_user);
+                        if (!(arg_user = strdup(optarg))) {
+                                log_error("Failed to duplicate user name.");
+                                return -ENOMEM;
+                        }
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -305,6 +320,66 @@ finish:
         return r;
 }
 
+static struct passwd *getpwun(const char* user) {
+        
+        struct passwd *pw;
+
+        pw = getpwnam(user);
+
+        if (!pw && isdigits(user)) {
+                pw = getpwuid((uid_t)atoi(user));
+        }
+
+        if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
+                 && pw->pw_passwd)) {
+                log_error("user name or id %s does not exist: %m", user);
+                return NULL;
+        }
+
+        return pw;
+}
+
+static int change_user(struct passwd *pw) {
+
+        if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
+                log_error("initgroups() failed: %m");
+                return -errno;
+        }
+    
+        if (setregid(pw->pw_gid, pw->pw_gid) < 0) {
+                log_error("setregid() failed: %m");
+                return -errno;
+        }
+
+        if (setreuid(pw->pw_uid, pw->pw_uid) < 0) {
+                log_error("setreuid() failed: %m");
+                return -errno;
+        }
+
+        return 0;
+
+}
+
+static int setup_environment(struct passwd* pw, char** envp) {
+
+        if (asprintf(envp, "HOME=%s", pw->pw_dir) < 0)
+                goto fail;
+        if (asprintf(envp + 3, "SHELL=%s", pw->pw_shell) < 0)
+                goto fail;
+        if (asprintf(envp + 4, "USER=%s", pw->pw_name) < 0)
+                goto fail;
+        if (asprintf(envp + 5, "LOGNAME=%s", pw->pw_name) < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        log_error("environment setup failed: %m");
+        return -errno;
+
+}
+
+
 static int drop_capabilities(void) {
         static const unsigned long retain[] = {
                 CAP_CHOWN,
@@ -694,11 +769,15 @@ int main(int argc, char *argv[]) {
 
                 const char *hn;
                 const char *envp[] = {
-                        "HOME=/root",
+                        NULL,  /* HOME */
                         "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-                        NULL,
+                        NULL, /* TERM */
+                        NULL, /* SHELL */
+                        NULL, /* USER */
+                        NULL, /* LOGNAME */
                         NULL
                 };
+                struct passwd *pw = NULL;
 
                 envp[2] = strv_find_prefix(environ, "TERM=");
 
@@ -757,13 +836,33 @@ int main(int argc, char *argv[]) {
                 if (drop_capabilities() < 0)
                         goto child_fail;
 
+                if (arg_user) {
+                        if (!(pw = getpwun(arg_user)))
+                                goto child_fail;
+
+                        mkdir_p(pw->pw_dir, 0755);
+                        if (chown(pw->pw_dir, pw->pw_uid, pw->pw_gid) < 0) {
+                                log_error("chown(%s) failed: %m", pw->pw_dir);
+                                goto child_fail;
+                        }
+
+                       if (change_user(pw) < 0)
+                                goto child_fail;
+
+                        if (setup_environment(pw, (char**) envp))
+                                goto child_fail;
+                }
+                else {
+                       envp[0] = "HOME=/root";
+                }
+
                 if ((hn = file_name_from_path(arg_directory)))
                         sethostname(hn, strlen(hn));
 
                 if (argc > optind)
                         execvpe(argv[optind], argv + optind, (char**) envp);
                 else {
-                        chdir("/root");
+                        chdir(pw ? pw->pw_dir : "/root");
                         execle("/bin/bash", "-bash", NULL, (char**) envp);
                 }
 
diff --git a/src/util.c b/src/util.c
index 4046938..696da8d 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4841,3 +4841,17 @@ int hwclock_set_time(const struct tm *tm) {
 
         return err;
 }
+
+int isdigits(const char* str) {
+        size_t i;
+
+        if (!str)
+                return 0;
+
+        for (i = 0; i != strlen(str); i++) {
+                if (!isdigit(str[i]))
+                        return 0;
+        }
+
+        return 1;
+}
diff --git a/src/util.h b/src/util.h
index 79d634b..8098c63 100644
--- a/src/util.h
+++ b/src/util.h
@@ -460,4 +460,6 @@ int hwclock_get_time(struct tm *tm);
 
 int hwclock_set_time(const struct tm *tm);
 
+int isdigits(const char* str);
+
 #endif
-- 
1.7.4.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/systemd-devel/attachments/20110624/eb81cccf/attachment.pgp>


More information about the systemd-devel mailing list