diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in
index 713a30e7f..14fa77d79 100644
--- a/doc/lxc-attach.sgml.in
+++ b/doc/lxc-attach.sgml.in
@@ -60,6 +60,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--clear-env
-v, --set-var variable
--keep-var variable
+ -u, --uid uid
+ -g, --gid gid
-- command
@@ -282,6 +284,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+
+
+
+
+ Executes the command with user ID
+ uid inside the container.
+
+
+
+
+
+
+
+
+
+
+ Executes the command with group ID
+ gid inside the container.
+
+
+
+
diff --git a/doc/lxc-execute.sgml.in b/doc/lxc-execute.sgml.in
index 20814348d..8b249b329 100644
--- a/doc/lxc-execute.sgml.in
+++ b/doc/lxc-execute.sgml.in
@@ -53,6 +53,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-d
-f config_file
-s KEY=VAL
+ -u, --uid uid
+ -g, --gid gid
-- command
@@ -139,6 +141,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+
+
+
+
+ Executes the command with user ID
+ uid inside the container.
+
+
+
+
+
+
+
+
+
+
+ Executes the command with group ID
+ gid inside the container.
+
+
+
+
diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 87f14398f..425f257e4 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -749,6 +749,8 @@ static int attach_child_main(struct attach_clone_payload *payload)
int fd, lsm_fd, ret;
uid_t new_uid;
gid_t new_gid;
+ uid_t ns_root_uid = 0;
+ gid_t ns_root_gid = 0;
lxc_attach_options_t* options = payload->options;
struct lxc_proc_context_info* init_ctx = payload->init_ctx;
bool needs_lsm = (options->namespaces & CLONE_NEWNS) &&
@@ -836,33 +838,40 @@ static int attach_child_main(struct attach_clone_payload *payload)
goto on_error;
}
- /* Set {u,g}id. */
- new_uid = 0;
- new_gid = 0;
+ if (options->namespaces & CLONE_NEWUSER) {
+ /* Check whether nsuid 0 has a mapping. */
+ ns_root_uid = get_ns_uid(0);
- /* Ignore errors, we will fall back to root in that case (/proc was not
- * mounted etc.).
- */
- if (options->namespaces & CLONE_NEWUSER)
- lxc_attach_get_init_uidgid(&new_uid, &new_gid);
+ /* Check whether nsgid 0 has a mapping. */
+ ns_root_gid = get_ns_gid(0);
- if (options->uid != (uid_t)-1)
- new_uid = options->uid;
+ /* If there's no mapping for nsuid 0 try to retrieve the nsuid
+ * init was started with.
+ */
+ if (ns_root_uid == LXC_INVALID_UID)
+ lxc_attach_get_init_uidgid(&ns_root_uid, &ns_root_gid);
- if (options->gid != (gid_t)-1)
- new_gid = options->gid;
+ if (ns_root_uid == LXC_INVALID_UID)
+ goto on_error;
- /* Try to set the {u,g}id combination. */
- if (new_uid != 0 || new_gid != 0 || options->namespaces & CLONE_NEWUSER) {
- ret = lxc_switch_uid_gid(new_uid, new_gid);
- if (ret < 0)
+ if (!lxc_switch_uid_gid(ns_root_uid, ns_root_gid))
goto on_error;
}
- ret = lxc_setgroups(0, NULL);
- if (ret < 0 && errno != EPERM)
+ if (!lxc_setgroups(0, NULL) && errno != EPERM)
goto on_error;
+ /* Set {u,g}id. */
+ if (options->uid != LXC_INVALID_UID)
+ new_uid = options->uid;
+ else
+ new_uid = ns_root_uid;
+
+ if (options->gid != LXC_INVALID_GID)
+ new_gid = options->gid;
+ else
+ new_gid = ns_root_gid;
+
if ((init_ctx->container && init_ctx->container->lxc_conf &&
init_ctx->container->lxc_conf->no_new_privs) ||
(options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) {
@@ -952,6 +961,16 @@ static int attach_child_main(struct attach_clone_payload *payload)
TRACE("Prepared terminal file descriptor %d", payload->terminal_slave_fd);
}
+ /* Avoid unnecessary syscalls. */
+ if (new_uid == ns_root_uid)
+ new_uid = LXC_INVALID_UID;
+
+ if (new_gid == ns_root_gid)
+ new_gid = LXC_INVALID_GID;
+
+ if (!lxc_switch_uid_gid(new_uid, new_gid))
+ goto on_error;
+
/* We're done, so we can now do whatever the user intended us to do. */
_exit(payload->exec_function(payload->exec_payload));
diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c
index 1fe561498..c37456b92 100644
--- a/src/lxc/cgroups/cgfsng.c
+++ b/src/lxc/cgroups/cgfsng.c
@@ -1388,6 +1388,8 @@ static int chown_cgroup_wrapper(void *data)
}
destuid = get_ns_uid(arg->origuid);
+ if (destuid == LXC_INVALID_UID)
+ destuid = 0;
for (i = 0; arg->hierarchies[i]; i++) {
char *fullpath;
diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index bdfef0fb2..e5b5d1f01 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -104,12 +104,10 @@ static int do_child(void *vargv)
char **argv = (char **)vargv;
/* Assume we want to become root */
- ret = lxc_switch_uid_gid(0, 0);
- if (ret < 0)
+ if (!lxc_switch_uid_gid(0, 0))
return -1;
- ret = lxc_setgroups(0, NULL);
- if (ret < 0)
+ if (!lxc_setgroups(0, NULL))
return -1;
ret = unshare(CLONE_NEWNS);
diff --git a/src/lxc/macro.h b/src/lxc/macro.h
index d44e2f9b1..c0a50371d 100644
--- a/src/lxc/macro.h
+++ b/src/lxc/macro.h
@@ -340,4 +340,7 @@ extern int __build_bug_on_failed;
#define PTR_TO_INTMAX(p) ((intmax_t)((intptr_t)(p)))
#define INTMAX_TO_PTR(u) ((void *)((intptr_t)(u)))
+#define LXC_INVALID_UID ((uid_t)-1)
+#define LXC_INVALID_GID ((gid_t)-1)
+
#endif /* __LXC_MACRO_H */
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 66a801eaf..8d3a7ced5 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -1046,10 +1046,11 @@ static int do_start(void *data)
{
int ret;
char path[PATH_MAX];
- bool have_cap_setgid;
uid_t new_uid;
gid_t new_gid;
struct lxc_list *iterator;
+ uid_t nsuid = 0;
+ gid_t nsgid = 0;
int devnull_fd = -1;
struct lxc_handler *handler = data;
@@ -1117,22 +1118,20 @@ static int do_start(void *data)
* privilege over our namespace.
*/
if (!lxc_list_empty(&handler->conf->id_map)) {
- uid_t nsuid = (handler->conf->root_nsuid_map != NULL)
- ? 0
- : handler->conf->init_uid;
- gid_t nsgid = (handler->conf->root_nsgid_map != NULL)
- ? 0
- : handler->conf->init_gid;
+ if (!handler->conf->root_nsuid_map)
+ nsuid = handler->conf->init_uid;
- ret = lxc_switch_uid_gid(nsuid, nsgid);
- if (ret < 0)
+ if (!handler->conf->root_nsgid_map)
+ nsgid = handler->conf->init_gid;
+
+ if (!lxc_switch_uid_gid(nsuid, nsgid))
goto out_warn_father;
/* Drop groups only after we switched to a valid gid in the new
* user namespace.
*/
- ret = lxc_setgroups(0, NULL);
- if (ret < 0 && (handler->am_root || errno != EPERM))
+ if (!lxc_setgroups(0, NULL) &&
+ (handler->am_root || errno != EPERM))
goto out_warn_father;
ret = prctl(PR_SET_DUMPABLE, prctl_arg(1), prctl_arg(0),
@@ -1355,25 +1354,27 @@ static int do_start(void *data)
new_uid = handler->conf->init_uid;
new_gid = handler->conf->init_gid;
- /* If we are in a new user namespace we already dropped all groups when
- * we switched to root in the new user namespace further above. Only
- * drop groups if we can, so ensure that we have necessary privilege.
- */
- #if HAVE_LIBCAP
- have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
- #else
- have_cap_setgid = false;
- #endif
- if (lxc_list_empty(&handler->conf->id_map) && have_cap_setgid) {
- ret = lxc_setgroups(0, NULL);
- if (ret < 0)
- goto out_warn_father;
- }
+ /* Avoid unnecessary syscalls. */
+ if (new_uid == nsuid)
+ new_uid = LXC_INVALID_UID;
- ret = lxc_switch_uid_gid(new_uid, new_gid);
- if (ret < 0)
+ if (new_gid == nsgid)
+ new_gid = LXC_INVALID_GID;
+
+ if (!lxc_switch_uid_gid(new_uid, new_gid))
goto out_warn_father;
+ /* If we are in a new user namespace we already dropped all groups when
+ * we switched to root in the new user namespace further above. Only
+ * drop groups if we can, so ensure that we have necessary privilege.
+ */
+ if (lxc_list_empty(&handler->conf->id_map))
+ #if HAVE_LIBCAP
+ if (lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE))
+ #endif
+ if (!lxc_setgroups(0, NULL))
+ goto out_warn_father;
+
ret = lxc_ambient_caps_down();
if (ret < 0) {
ERROR("Failed to clear ambient capabilities");
diff --git a/src/lxc/storage/rsync.c b/src/lxc/storage/rsync.c
index 83871ae80..ca2da186e 100644
--- a/src/lxc/storage/rsync.c
+++ b/src/lxc/storage/rsync.c
@@ -50,12 +50,10 @@ int lxc_rsync_exec_wrapper(void *data)
int ret;
struct rsync_data_char *args = data;
- ret = lxc_switch_uid_gid(0, 0);
- if (ret < 0)
+ if (!lxc_switch_uid_gid(0, 0))
return -1;
- ret = lxc_setgroups(0, NULL);
- if (ret < 0)
+ if (!lxc_setgroups(0, NULL))
return -1;
return lxc_rsync_exec(args->src, args->dest);
@@ -117,12 +115,10 @@ int lxc_rsync(struct rsync_data *data)
return -1;
}
- ret = lxc_switch_uid_gid(0, 0);
- if (ret < 0)
+ if (!lxc_switch_uid_gid(0, 0))
return -1;
- ret = lxc_setgroups(0, NULL);
- if (ret < 0)
+ if (!lxc_setgroups(0, NULL))
return -1;
src = lxc_storage_get_path(orig->dest, orig->type);
diff --git a/src/lxc/tools/lxc_attach.c b/src/lxc/tools/lxc_attach.c
index e98f0a056..551f9f396 100644
--- a/src/lxc/tools/lxc_attach.c
+++ b/src/lxc/tools/lxc_attach.c
@@ -72,6 +72,8 @@ static const struct option my_longopts[] = {
{"set-var", required_argument, 0, 'v'},
{"pty-log", required_argument, 0, 'L'},
{"rcfile", required_argument, 0, 'f'},
+ {"uid", required_argument, 0, 'u'},
+ {"gid", required_argument, 0, 'g'},
LXC_COMMON_OPTIONS
};
@@ -122,6 +124,8 @@ Options :\n\
multiple times.\n\
-f, --rcfile=FILE\n\
Load configuration file FILE\n\
+ -u, --uid=UID Execute COMMAND with UID inside the container\n\
+ -g, --gid=GID Execute COMMAND with GID inside the container\n\
",
.options = my_longopts,
.parser = my_parser,
@@ -187,6 +191,14 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg)
case 'f':
args->rcfile = arg;
break;
+ case 'u':
+ if (lxc_safe_uint(arg, &args->uid) < 0)
+ return -1;
+ break;
+ case 'g':
+ if (lxc_safe_uint(arg, &args->gid) < 0)
+ return -1;
+ break;
}
return 0;
@@ -333,6 +345,12 @@ int main(int argc, char *argv[])
goto out;
}
+ if (my_args.uid)
+ attach_options.uid = my_args.uid;
+
+ if (my_args.gid)
+ attach_options.gid = my_args.gid;
+
if (command.program)
ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
else
diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index 9795b51b6..9b6f0a617 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -544,7 +544,34 @@ uid_t get_ns_uid(uid_t orig)
}
}
- nsid = 0;
+ nsid = LXC_INVALID_UID;
+
+found:
+ fclose(f);
+ free(line);
+ return nsid;
+}
+
+gid_t get_ns_gid(gid_t orig)
+{
+ char *line = NULL;
+ size_t sz = 0;
+ gid_t nsid, hostid, range;
+ FILE *f = fopen("/proc/self/gid_map", "r");
+ if (!f)
+ return 0;
+
+ while (getline(&line, &sz, f) != -1) {
+ if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
+ continue;
+
+ if (hostid <= orig && hostid + range > orig) {
+ nsid += orig - hostid;
+ goto found;
+ }
+ }
+
+ nsid = LXC_INVALID_GID;
found:
fclose(f);
@@ -1324,33 +1351,41 @@ int lxc_preserve_ns(const int pid, const char *ns)
return open(path, O_RDONLY | O_CLOEXEC);
}
-int lxc_switch_uid_gid(uid_t uid, gid_t gid)
+bool lxc_switch_uid_gid(uid_t uid, gid_t gid)
{
- if (setgid(gid) < 0) {
- SYSERROR("Failed to switch to gid %d.", gid);
- return -errno;
- }
- NOTICE("Switched to gid %d.", gid);
+ int ret = 0;
- if (setuid(uid) < 0) {
- SYSERROR("Failed to switch to uid %d.", uid);
- return -errno;
+ if (gid != LXC_INVALID_GID) {
+ ret = setgid(gid);
+ if (ret < 0) {
+ SYSERROR("Failed to switch to gid %d", gid);
+ return false;
+ }
+ NOTICE("Switched to gid %d", gid);
}
- NOTICE("Switched to uid %d.", uid);
- return 0;
+ if (uid != LXC_INVALID_UID) {
+ ret = setuid(uid);
+ if (ret < 0) {
+ SYSERROR("Failed to switch to uid %d", uid);
+ return false;
+ }
+ NOTICE("Switched to uid %d", uid);
+ }
+
+ return true;
}
/* Simple covenience function which enables uniform logging. */
-int lxc_setgroups(int size, gid_t list[])
+bool lxc_setgroups(int size, gid_t list[])
{
if (setgroups(size, list) < 0) {
- SYSERROR("Failed to setgroups().");
- return -errno;
+ SYSERROR("Failed to setgroups()");
+ return false;
}
- NOTICE("Dropped additional groups.");
+ NOTICE("Dropped additional groups");
- return 0;
+ return true;
}
static int lxc_get_unused_loop_dev_legacy(char *loop_name)
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index f2d802991..0f121e673 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -328,6 +328,10 @@ inline static bool am_host_unpriv(void)
* parse /proc/self/uid_map to find what @orig maps to
*/
extern uid_t get_ns_uid(uid_t orig);
+/*
+ * parse /proc/self/gid_map to find what @orig maps to
+ */
+extern gid_t get_ns_gid(gid_t orig);
extern bool dir_exists(const char *path);
@@ -354,9 +358,11 @@ extern int lxc_preserve_ns(const int pid, const char *ns);
/* Check whether a signal is blocked by a process. */
extern bool task_blocks_signal(pid_t pid, int signal);
-/* Switch to a new uid and gid. */
-extern int lxc_switch_uid_gid(uid_t uid, gid_t gid);
-extern int lxc_setgroups(int size, gid_t list[]);
+/* Switch to a new uid and gid.
+ * If LXC_INVALID_{G,U}ID is passed then the set{g,u}id() will not be called.
+ */
+extern bool lxc_switch_uid_gid(uid_t uid, gid_t gid);
+extern bool lxc_setgroups(int size, gid_t list[]);
/* Find an unused loop device and associate it with source. */
extern int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags);