Discussion:
[PATCH] nspawn: spawn shell under specified --user
(too old to reply)
Michal Vyskocil
2011-06-24 12:39:50 UTC
Permalink
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
Zbigniew Jędrzejewski-Szmek
2011-06-26 19:59:14 UTC
Permalink
Post by Michal Vyskocil
+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;
+ }
Quadratic behaviour? What aboout
while(*str)
if(!isdigit(*str++))
return 0;

Best,
Zbyszek
Michal Vyskocil
2011-06-27 07:45:59 UTC
Permalink
Post by Zbigniew Jędrzejewski-Szmek
Post by Michal Vyskocil
+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;
+ }
Quadratic behaviour? What aboout
while(*str)
if(!isdigit(*str++))
return 0;
Good point - as obvious I made the mistake in the aux function of my
code :-/

Anyway I'm still interested if Lennart would like add a new option to
his Gnome3-like container :)

Regards
Michal Vyskocil
Lennart Poettering
2011-06-27 12:01:27 UTC
Permalink
Post by Michal Vyskocil
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.
Sounds sensible, though I do wonder about the ultimate usefulness of
this given that this requires user settings configured on the host
systems in a way that makes sense in the container too. (i.e. the $HOME
and UID/GID of the user must be in sync in host and in container). Or am
I missing something?
Post by Michal Vyskocil
+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;
+ }
Please work the other way here. Use "safe_atou()" first on the
username, and if that works it's a numeric uid. If it doesn't try
getpwnam(). Code that already does this you find in get_user_creds() in
execute.c.
Post by Michal Vyskocil
+ 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;
+ }
Please use safe_mkdir() here.

Lennart
--
Lennart Poettering - Red Hat, Inc.
Michal Vyskocil
2011-06-27 12:50:06 UTC
Permalink
Post by Lennart Poettering
Post by Michal Vyskocil
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.
Sounds sensible, though I do wonder about the ultimate usefulness of
this given that this requires user settings configured on the host
systems in a way that makes sense in the container too. (i.e. the $HOME
and UID/GID of the user must be in sync in host and in container). Or am
I missing something?
Yes, that's the requirements - user must exists in chroot. But I don't
see any need why the uid/gid must be the same. All things are done after
chroot("."), so in the context of container.

The original idea behind was user systemd-nspawn instead of chroot for
local builds of our packages on systemd running with systemd. So instead
of chroot su -c $BUILD_COMMAND - $BUILD_USER simply call systemd-nspawn
-u $BUILD_USER $BUILD_COMMAND. I assume something similar have mock
(chroot + special user used for build) used by Fedora, so it might
benefit from that change as well.

I don't know it there's an another usecase, because even if it runs with
different user, it still have a lot of powerfull capabilities.
Post by Lennart Poettering
Post by Michal Vyskocil
+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;
+ }
Please work the other way here. Use "safe_atou()" first on the
username, and if that works it's a numeric uid. If it doesn't try
getpwnam(). Code that already does this you find in get_user_creds() in
execute.c.
Reading your code the get_user_creds seems to be a perfect for the user-switching in
nspawn as well. What about move it to another location like src/util.c
and use it from both execute.c and nspawn.c?
Post by Lennart Poettering
Post by Michal Vyskocil
+ 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;
+ }
Please use safe_mkdir() here.
Will do

Thanks for the review.

Michal Vyskocil
Lennart Poettering
2011-06-27 12:58:29 UTC
Permalink
Post by Michal Vyskocil
Post by Lennart Poettering
Post by Michal Vyskocil
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.
Sounds sensible, though I do wonder about the ultimate usefulness of
this given that this requires user settings configured on the host
systems in a way that makes sense in the container too. (i.e. the $HOME
and UID/GID of the user must be in sync in host and in container). Or am
I missing something?
Yes, that's the requirements - user must exists in chroot. But I don't
see any need why the uid/gid must be the same. All things are done after
chroot("."), so in the context of container.
Hmm, I wonder if this might turn out to be a problem, since the NSS
modules form the container might not be compatible with the host glibc
which we are using.

Hmm, given the rigorous compat logic glibc includes this might be safe,
so let's merge it. If it breaks, then we can still revisit the issue.
Post by Michal Vyskocil
Post by Lennart Poettering
Please work the other way here. Use "safe_atou()" first on the
username, and if that works it's a numeric uid. If it doesn't try
getpwnam(). Code that already does this you find in get_user_creds() in
execute.c.
Reading your code the get_user_creds seems to be a perfect for the user-switching in
nspawn as well. What about move it to another location like src/util.c
and use it from both execute.c and nspawn.c?
Yupp, go ahead!

Thanks,

Lennart
--
Lennart Poettering - Red Hat, Inc.
Ludwig Nussel
2011-06-27 15:32:21 UTC
Permalink
Post by Lennart Poettering
Post by Michal Vyskocil
Post by Lennart Poettering
Post by Michal Vyskocil
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.
Sounds sensible, though I do wonder about the ultimate usefulness of
this given that this requires user settings configured on the host
systems in a way that makes sense in the container too. (i.e. the $HOME
and UID/GID of the user must be in sync in host and in container). Or am
I missing something?
Yes, that's the requirements - user must exists in chroot. But I don't
see any need why the uid/gid must be the same. All things are done after
chroot("."), so in the context of container.
Not necessarily. If there's a connection to nscd open you will keep
talking to the host.
http://lists.rpm.org/pipermail/rpm-maint/2011-May/003010.html
Post by Lennart Poettering
Hmm, I wonder if this might turn out to be a problem, since the NSS
modules form the container might not be compatible with the host glibc
which we are using.
Hmm, given the rigorous compat logic glibc includes this might be safe,
so let's merge it. If it breaks, then we can still revisit the issue.
It did indeed break at least once in the past. glibc 2.2/2.3 or
something like that.

Also keep in mind that you are loading shared libs from the chroot
into the process which is still running as root. So name based user
switching is not a security feature.

cu
Ludwig
--
(o_ Ludwig Nussel
//\
V_/_ http://www.suse.de/
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer, HRB 16746 (AG Nürnberg)
Lennart Poettering
2011-06-27 16:20:46 UTC
Permalink
Post by Ludwig Nussel
Post by Michal Vyskocil
Post by Lennart Poettering
Post by Michal Vyskocil
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.
Sounds sensible, though I do wonder about the ultimate usefulness of
this given that this requires user settings configured on the host
systems in a way that makes sense in the container too. (i.e. the $HOME
and UID/GID of the user must be in sync in host and in container). Or am
I missing something?
Yes, that's the requirements - user must exists in chroot. But I don't
see any need why the uid/gid must be the same. All things are done after
chroot("."), so in the context of container.
Not necessarily. If there's a connection to nscd open you will keep
talking to the host.
http://lists.rpm.org/pipermail/rpm-maint/2011-May/003010.html
Well, but we do not really do any other NSS call, and since NSS is
initialized lazily we should be safe.
Post by Ludwig Nussel
Also keep in mind that you are loading shared libs from the chroot
into the process which is still running as root. So name based user
switching is not a security feature.
Well, the man page already clarifies that nspawn is in no way secure. So
I am not too concerned about this.

Michal's patch adds an optional argument --user. It might be worth
explaining in the man page that by using this you are running into
certain risks.

Lennart
--
Lennart Poettering - Red Hat, Inc.
Michal Vyskocil
2011-06-29 12:21:56 UTC
Permalink
Move the get_user_creds from execute.c to utils.c for later usage in
nspawn.c.
---
src/execute.c | 46 ----------------------------------------------
src/util.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
src/util.h | 2 ++
3 files changed, 49 insertions(+), 46 deletions(-)

diff --git a/src/execute.c b/src/execute.c
index b00ccde..912f2ce 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -579,52 +579,6 @@ static int get_group_creds(const char *groupname, gid_t *gid) {
return 0;
}

-static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
- struct passwd *p;
- unsigned long lu;
-
- assert(username);
- assert(*username);
- assert(uid);
- assert(gid);
- assert(home);
-
- /* We enforce some special rules for uid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*username, "root") || streq(*username, "0")) {
- *username = "root";
- *uid = 0;
- *gid = 0;
- *home = "/root";
- return 0;
- }
-
- if (safe_atolu(*username, &lu) >= 0) {
- errno = 0;
- p = getpwuid((uid_t) lu);
-
- /* If there are multiple users with the same id, make
- * sure to leave $USER to the configured value instead
- * of the first occurrence in the database. However if
- * the uid was configured by a numeric uid, then let's
- * pick the real username from /etc/passwd. */
- if (*username && p)
- *username = p->pw_name;
- } else {
- errno = 0;
- p = getpwnam(*username);
- }
-
- if (!p)
- return errno != 0 ? -errno : -ESRCH;
-
- *uid = p->pw_uid;
- *gid = p->pw_gid;
- *home = p->pw_dir;
- return 0;
-}
-
static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
bool keep_groups = false;
int r;
diff --git a/src/util.c b/src/util.c
index 278f018..d3875a5 100644
--- a/src/util.c
+++ b/src/util.c
@@ -5164,6 +5164,53 @@ int socket_from_display(const char *display, char **path) {
return 0;
}

+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+ struct passwd *p;
+ unsigned long lu;
+
+ assert(username);
+ assert(*username);
+ assert(uid);
+ assert(gid);
+ assert(home);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+ *uid = 0;
+ *gid = 0;
+ *home = "/root";
+ return 0;
+ }
+
+ if (safe_atolu(*username, &lu) >= 0) {
+ errno = 0;
+ p = getpwuid((uid_t) lu);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurrence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (*username && p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno != 0 ? -errno : -ESRCH;
+
+ *uid = p->pw_uid;
+ *gid = p->pw_gid;
+ *home = p->pw_dir;
+ return 0;
+}
+
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/util.h b/src/util.h
index a26fb6f..e9f0567 100644
--- a/src/util.h
+++ b/src/util.h
@@ -477,4 +477,6 @@ int signal_from_string(const char *s);

int signal_from_string_try_harder(const char *s);

+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home);
+
#endif
--
1.7.4.1
Michal Vyskocil
2011-06-29 12:22:46 UTC
Permalink
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. The user home is
created as well.

It also setup HOME, USER, LOGNAME and SHELL variables .
---
man/systemd-nspawn.xml | 13 +++++++++
src/nspawn.c | 70 +++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 77 insertions(+), 6 deletions(-)

diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index e1b33f7..d3d056c 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -138,6 +138,19 @@
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, create home
+ directory and cd into it. As rest
+ of systemd-nspawn, this is not
+ the security feature and limits
+ against accidental changes only.
+ </para></listitem>
+ </varlistentry>

</variablelist>

diff --git a/src/nspawn.c b/src/nspawn.c
index 1ade6e2..5c44d15 100644
--- a/src/nspawn.c
+++ b/src/nspawn.c
@@ -36,6 +36,7 @@
#include <sys/epoll.h>
#include <termios.h>
#include <sys/signalfd.h>
+#include <grp.h>

#include "log.h"
#include "util.h"
@@ -45,13 +46,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 +65,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 +74,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 +91,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;

@@ -693,14 +706,19 @@ int main(int argc, char *argv[]) {
/* child */

const char *hn;
+ const char *home = NULL;
+ uid_t uid = (uid_t) -1;
+ gid_t gid = (gid_t) -1;
const char *envp[] = {
- "HOME=/root",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- NULL,
+ NULL, /* TERM */
+ NULL, /* HOME */
+ NULL, /* USER */
+ NULL, /* LOGNAME */
NULL
};

- envp[2] = strv_find_prefix(environ, "TERM=");
+ envp[1] = strv_find_prefix(environ, "TERM=");

close_nointr_nofail(master);

@@ -757,13 +775,53 @@ int main(int argc, char *argv[]) {
if (drop_capabilities() < 0)
goto child_fail;

+ if (arg_user) {
+
+ if (get_user_creds((const char**)&arg_user, &uid, &gid, &home) < 0) {
+ log_error("get_user_creds() failed: %m");
+ goto child_fail;
+ }
+
+ if (mkdir_parents(home, 0775) < 0) {
+ log_error("mkdir_parents() failed: %m");
+ goto child_fail;
+ }
+
+ if (safe_mkdir(home, 0775, uid, gid) < 0) {
+ log_error("safe_mkdir() failed: %m");
+ goto child_fail;
+ }
+
+ if (initgroups((const char*)arg_user, gid) < 0) {
+ log_error("initgroups() failed: %m");
+ goto child_fail;
+ }
+
+ if (setregid(gid, gid) < 0) {
+ log_error("setregid() failed: %m");
+ goto child_fail;
+ }
+
+ if (setreuid(uid, uid) < 0) {
+ log_error("setreuid() failed: %m");
+ goto child_fail;
+ }
+ }
+
+ if ((asprintf((char**)(envp + 2), "HOME=%s", home? home: "/root") < 0) ||
+ (asprintf((char**)(envp + 3), "USER=%s", arg_user? arg_user : "root") < 0) ||
+ (asprintf((char**)(envp + 4), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) {
+ log_error("environment setup failed: %m");
+ goto child_fail;
+ }
+
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(home? home : "/root");
execle("/bin/bash", "-bash", NULL, (char**) envp);
}
--
1.7.4.1
Lennart Poettering
2011-07-01 21:58:16 UTC
Permalink
Post by Michal Vyskocil
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. The user home is
created as well.
It also setup HOME, USER, LOGNAME and SHELL variables .
Thanks a lot, applied both patches!

Lennart
--
Lennart Poettering - Red Hat, Inc.
Loading...