* 'map-user' of https://github.com/mat8913/util-linux:
  unshare: Support names for map-user/group options
  lib/pwdutils: add xgetgrnam
  unshare: allow custom uid/gid mappings in userns
This commit is contained in:
Karel Zak 2020-04-17 11:02:29 +02:00
commit 75c7684724
4 changed files with 114 additions and 37 deletions

View File

@ -3,8 +3,10 @@
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
extern struct passwd *xgetpwnam(const char *username, char **pwdbuf);
extern struct group *xgetgrnam(const char *groupname, char **grpbuf);
extern struct passwd *xgetpwuid(uid_t uid, char **pwdbuf);
extern char *xgetlogin(void);

View File

@ -36,6 +36,38 @@ failed:
return NULL;
}
/* Returns allocated group and allocated grpbuf to store group strings
* fields. In case of error returns NULL and set errno, for unknown group set
* errno to EINVAL
*/
struct group *xgetgrnam(const char *groupname, char **grpbuf)
{
struct group *grp = NULL, *res = NULL;
int rc;
if (!grpbuf || !groupname)
return NULL;
*grpbuf = xmalloc(UL_GETPW_BUFSIZ);
grp = xcalloc(1, sizeof(struct group));
errno = 0;
rc = getgrnam_r(groupname, grp, *grpbuf, UL_GETPW_BUFSIZ, &res);
if (rc != 0) {
errno = rc;
goto failed;
}
if (!res) {
errno = EINVAL;
goto failed;
}
return grp;
failed:
free(grp);
free(*grpbuf);
return NULL;
}
struct passwd *xgetpwuid(uid_t uid, char **pwdbuf)
{
struct passwd *pwd = NULL, *res = NULL;

View File

@ -169,6 +169,16 @@ implies creating a new mount namespace since the /proc mount would otherwise
mess up existing programs on the system. The new proc filesystem is explicitly
mounted as private (with MS_PRIVATE|MS_REC).
.TP
.BR \-\-map\-user=\fIuid|name
Run the program only after the current effective user ID has been mapped to \fIuid\fP.
If this option is specified multiple times, the last occurrence takes precedence.
This option implies \fB\-\-user\fR.
.TP
.BR \-\-map\-group=\fIgid|name
Run the program only after the current effective group ID has been mapped to \fIgid\fP.
If this option is specified multiple times, the last occurrence takes precedence.
This option implies \fB\-\-setgroups=deny\fR and \fB\-\-user\fR.
.TP
.BR \-r , " \-\-map\-root\-user"
Run the program only after the current effective user and group IDs have been mapped to
the superuser UID and GID in the newly created user namespace. This makes it possible to
@ -177,11 +187,13 @@ namespaces (such as configuring interfaces in the network namespace or mounting
the mount namespace) even when run unprivileged. As a mere convenience feature, it does not support
more sophisticated use cases, such as mapping multiple ranges of UIDs and GIDs.
This option implies \fB\-\-setgroups=deny\fR and \fB\-\-user\fR.
This option is equivalent to \fB\-\-map-user=0 \-\-map-group=0\fR.
.TP
.BR \-c , " \-\-map\-current\-user"
Run the program only after the current effective user and group IDs have been mapped to
the same UID and GID in the newly created user namespace. This option implies
\fB\-\-setgroups=deny\fR and \fB\-\-user\fR.
This option is equivalent to \fB\-\-map-user=$(id -ru) \-\-map-group=$(id -rg)\fR.
.TP
.BR "\-\-propagation private" | shared | slave | unchanged
Recursively set the mount propagation flag in the new mount namespace. The default

View File

@ -45,6 +45,7 @@
#include "all-io.h"
#include "signames.h"
#include "strutils.h"
#include "pwdutils.h"
/* synchronize parent and child by pipe */
#define PIPE_SYNC_BYTE 0x06
@ -77,12 +78,6 @@ enum {
SETGROUPS_ALLOW = 1,
};
enum {
MAP_USER_NONE,
MAP_USER_ROOT,
MAP_USER_CURRENT,
};
static const char *setgroups_strings[] =
{
[SETGROUPS_DENY] = "deny",
@ -266,6 +261,42 @@ static void bind_ns_files_from_child(pid_t *child, int fds[2])
}
}
static uid_t get_user(const char *s, const char *err)
{
struct passwd *pw;
char *buf = NULL;
uid_t ret;
pw = xgetpwnam(s, &buf);
if (pw) {
ret = pw->pw_uid;
free(pw);
free(buf);
} else {
ret = strtoul_or_err(s, err);
}
return ret;
}
static gid_t get_group(const char *s, const char *err)
{
struct group *gr;
char *buf = NULL;
gid_t ret;
gr = xgetgrnam(s, &buf);
if (gr) {
ret = gr->gr_gid;
free(gr);
free(buf);
} else {
ret = strtoul_or_err(s, err);
}
return ret;
}
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
@ -288,6 +319,8 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -T, --time[=<file>] unshare time namespace\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_(" -f, --fork fork before launching <program>\n"), out);
fputs(_(" --map-user=<uid>|<name> map current user to uid (implies --user)\n"), out);
fputs(_(" --map-group=<gid>|<name> map current group to gid (implies --user)\n"), out);
fputs(_(" -r, --map-root-user map current user to root (implies --user)\n"), out);
fputs(_(" -c, --map-current-user map current user to itself (implies --user)\n"), out);
fputs(USAGE_SEPARATOR, out);
@ -323,6 +356,8 @@ int main(int argc, char *argv[])
OPT_KEEPCAPS,
OPT_MONOTONIC,
OPT_BOOTTIME,
OPT_MAPUSER,
OPT_MAPGROUP,
};
static const struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
@ -340,6 +375,8 @@ int main(int argc, char *argv[])
{ "fork", no_argument, NULL, 'f' },
{ "kill-child", optional_argument, NULL, OPT_KILLCHILD },
{ "mount-proc", optional_argument, NULL, OPT_MOUNTPROC },
{ "map-user", required_argument, NULL, OPT_MAPUSER },
{ "map-group", required_argument, NULL, OPT_MAPGROUP },
{ "map-root-user", no_argument, NULL, 'r' },
{ "map-current-user", no_argument, NULL, 'c' },
{ "propagation", required_argument, NULL, OPT_PROPAGATION },
@ -356,7 +393,9 @@ int main(int argc, char *argv[])
int setgrpcmd = SETGROUPS_NONE;
int unshare_flags = 0;
int c, forkit = 0, mapuser = MAP_USER_NONE;
int c, forkit = 0;
uid_t mapuser = -1;
gid_t mapgroup = -1;
int kill_child_signo = 0; /* 0 means --kill-child was not used */
const char *procmnt = NULL;
const char *newroot = NULL;
@ -428,21 +467,23 @@ int main(int argc, char *argv[])
unshare_flags |= CLONE_NEWNS;
procmnt = optarg ? optarg : "/proc";
break;
case 'r':
if (mapuser == MAP_USER_CURRENT)
errx(EXIT_FAILURE, _("options --map-root-user and "
"--map-current-user are mutually exclusive"));
case OPT_MAPUSER:
unshare_flags |= CLONE_NEWUSER;
mapuser = MAP_USER_ROOT;
mapuser = get_user(optarg, _("failed to parse uid"));
break;
case OPT_MAPGROUP:
unshare_flags |= CLONE_NEWUSER;
mapgroup = get_group(optarg, _("failed to parse gid"));
break;
case 'r':
unshare_flags |= CLONE_NEWUSER;
mapuser = 0;
mapgroup = 0;
break;
case 'c':
if (mapuser == MAP_USER_ROOT)
errx(EXIT_FAILURE, _("options --map-root-user and "
"--map-current-user are mutually exclusive"));
unshare_flags |= CLONE_NEWUSER;
mapuser = MAP_USER_CURRENT;
mapuser = real_euid;
mapgroup = real_egid;
break;
case OPT_SETGROUPS:
setgrpcmd = setgroups_str2id(optarg);
@ -561,33 +602,23 @@ int main(int argc, char *argv[])
if (kill_child_signo != 0 && prctl(PR_SET_PDEATHSIG, kill_child_signo) < 0)
err(EXIT_FAILURE, "prctl failed");
if (mapuser != (uid_t) -1)
map_id(_PATH_PROC_UIDMAP, mapuser, real_euid);
/* Since Linux 3.19 unprivileged writing of /proc/self/gid_map
* has been disabled unless /proc/self/setgroups is written
* first to permanently disable the ability to call setgroups
* in that user namespace. */
switch (mapuser) {
case MAP_USER_ROOT:
if (mapgroup != (gid_t) -1) {
if (setgrpcmd == SETGROUPS_ALLOW)
errx(EXIT_FAILURE, _("options --setgroups=allow and "
"--map-root-user are mutually exclusive"));
"--map-group are mutually exclusive"));
setgroups_control(SETGROUPS_DENY);
map_id(_PATH_PROC_UIDMAP, 0, real_euid);
map_id(_PATH_PROC_GIDMAP, 0, real_egid);
break;
case MAP_USER_CURRENT:
if (setgrpcmd == SETGROUPS_ALLOW)
errx(EXIT_FAILURE, _("options --setgroups=allow and "
"--map-current-user are mutually exclusive"));
map_id(_PATH_PROC_GIDMAP, mapgroup, real_egid);
}
setgroups_control(SETGROUPS_DENY);
map_id(_PATH_PROC_UIDMAP, real_euid, real_euid);
map_id(_PATH_PROC_GIDMAP, real_egid, real_egid);
break;
case MAP_USER_NONE:
if (setgrpcmd != SETGROUPS_NONE)
setgroups_control(setgrpcmd);
}
if (setgrpcmd != SETGROUPS_NONE)
setgroups_control(setgrpcmd);
if ((unshare_flags & CLONE_NEWNS) && propagation)
set_propagation(propagation);