Merge branch 'map-user' of https://github.com/mat8913/util-linux
* '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 usernspull/1012/head
commit
75c7684724
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue