|
@@ -1,3 +1,4 @@
|
|
|
|
+
|
|
#define _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#include <endian.h>
|
|
#include <endian.h>
|
|
#include <errno.h>
|
|
#include <errno.h>
|
|
@@ -19,6 +20,8 @@
|
|
#include <sys/prctl.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/types.h>
|
|
|
|
+#include <sys/wait.h>
|
|
|
|
+
|
|
|
|
|
|
#include <linux/limits.h>
|
|
#include <linux/limits.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/netlink.h>
|
|
@@ -64,7 +67,13 @@ struct clone_t {
|
|
|
|
|
|
struct nlconfig_t {
|
|
struct nlconfig_t {
|
|
char *data;
|
|
char *data;
|
|
|
|
+
|
|
|
|
+ /* Process settings. */
|
|
uint32_t cloneflags;
|
|
uint32_t cloneflags;
|
|
|
|
+ char *oom_score_adj;
|
|
|
|
+ size_t oom_score_adj_len;
|
|
|
|
+
|
|
|
|
+ /* User namespace settings.*/
|
|
char *uidmap;
|
|
char *uidmap;
|
|
size_t uidmap_len;
|
|
size_t uidmap_len;
|
|
char *gidmap;
|
|
char *gidmap;
|
|
@@ -72,9 +81,13 @@ struct nlconfig_t {
|
|
char *namespaces;
|
|
char *namespaces;
|
|
size_t namespaces_len;
|
|
size_t namespaces_len;
|
|
uint8_t is_setgroup;
|
|
uint8_t is_setgroup;
|
|
|
|
+
|
|
|
|
+ /* Rootless container settings.*/
|
|
uint8_t is_rootless;
|
|
uint8_t is_rootless;
|
|
- char *oom_score_adj;
|
|
|
|
- size_t oom_score_adj_len;
|
|
|
|
|
|
+ char *uidmappath;
|
|
|
|
+ size_t uidmappath_len;
|
|
|
|
+ char *gidmappath;
|
|
|
|
+ size_t gidmappath_len;
|
|
};
|
|
};
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -89,6 +102,8 @@ struct nlconfig_t {
|
|
#define SETGROUP_ATTR 27285
|
|
#define SETGROUP_ATTR 27285
|
|
#define OOM_SCORE_ADJ_ATTR 27286
|
|
#define OOM_SCORE_ADJ_ATTR 27286
|
|
#define ROOTLESS_ATTR 27287
|
|
#define ROOTLESS_ATTR 27287
|
|
|
|
+#define UIDMAPPATH_ATTR 27288
|
|
|
|
+#define GIDMAPPATH_ATTR 27289
|
|
|
|
|
|
/*
|
|
/*
|
|
* Use the raw syscall for versions of glibc which don't include a function for
|
|
* Use the raw syscall for versions of glibc which don't include a function for
|
|
@@ -191,22 +206,96 @@ static void update_setgroups(int pid, enum policy_t setgroup)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static void update_uidmap(int pid, char *map, size_t map_len)
|
|
|
|
|
|
+static int try_mapping_tool(const char *app, int pid, char *map, size_t map_len)
|
|
|
|
+{
|
|
|
|
+ int child;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If @app is NULL, execve will segfault. Just check it here and bail (if
|
|
|
|
+ * we're in this path, the caller is already getting desparate and there
|
|
|
|
+ * isn't a backup to this failing). This usually would be a configuration
|
|
|
|
+ * or programming issue.
|
|
|
|
+ */
|
|
|
|
+ if (!app)
|
|
|
|
+ bail("mapping tool not present");
|
|
|
|
+
|
|
|
|
+ child = fork();
|
|
|
|
+ if (child < 0)
|
|
|
|
+ bail("failed to fork");
|
|
|
|
+
|
|
|
|
+ if (!child) {
|
|
|
|
+#define MAX_ARGV 20
|
|
|
|
+ char *argv[MAX_ARGV];
|
|
|
|
+ char *envp[] = {NULL};
|
|
|
|
+ char pid_fmt[16];
|
|
|
|
+ int argc = 0;
|
|
|
|
+ char *next;
|
|
|
|
+
|
|
|
|
+ snprintf(pid_fmt, 16, "%d", pid);
|
|
|
|
+
|
|
|
|
+ argv[argc++] = (char *) app;
|
|
|
|
+ argv[argc++] = pid_fmt;
|
|
|
|
+ /*
|
|
|
|
+ * Convert the map string into a list of argument that
|
|
|
|
+ * newuidmap/newgidmap can understand.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ while (argc < MAX_ARGV) {
|
|
|
|
+ if (*map == '\0') {
|
|
|
|
+ argv[argc++] = NULL;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ argv[argc++] = map;
|
|
|
|
+ next = strpbrk(map, "\n ");
|
|
|
|
+ if (next == NULL)
|
|
|
|
+ break;
|
|
|
|
+ *next++ = '\0';
|
|
|
|
+ map = next + strspn(next, "\n ");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ execve(app, argv, envp);
|
|
|
|
+ bail("failed to execv");
|
|
|
|
+ } else {
|
|
|
|
+ int status;
|
|
|
|
+
|
|
|
|
+ while (true) {
|
|
|
|
+ if (waitpid(child, &status, 0) < 0) {
|
|
|
|
+ if (errno == EINTR)
|
|
|
|
+ continue;
|
|
|
|
+ bail("failed to waitpid");
|
|
|
|
+ }
|
|
|
|
+ if (WIFEXITED(status) || WIFSIGNALED(status))
|
|
|
|
+ return WEXITSTATUS(status);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void update_uidmap(const char *path, int pid, char *map, size_t map_len)
|
|
{
|
|
{
|
|
if (map == NULL || map_len <= 0)
|
|
if (map == NULL || map_len <= 0)
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (write_file(map, map_len, "/proc/%d/uid_map", pid) < 0)
|
|
|
|
- bail("failed to update /proc/%d/uid_map", pid);
|
|
|
|
|
|
+ if (write_file(map, map_len, "/proc/%d/uid_map", pid) < 0) {
|
|
|
|
+ if (errno != EPERM)
|
|
|
|
+ bail("failed to update /proc/%d/uid_map", pid);
|
|
|
|
+ if (try_mapping_tool(path, pid, map, map_len))
|
|
|
|
+ bail("failed to use newuid map on %d", pid);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-static void update_gidmap(int pid, char *map, size_t map_len)
|
|
|
|
|
|
+static void update_gidmap(const char *path, int pid, char *map, size_t map_len)
|
|
{
|
|
{
|
|
if (map == NULL || map_len <= 0)
|
|
if (map == NULL || map_len <= 0)
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (write_file(map, map_len, "/proc/%d/gid_map", pid) < 0)
|
|
|
|
- bail("failed to update /proc/%d/gid_map", pid);
|
|
|
|
|
|
+ if (write_file(map, map_len, "/proc/%d/gid_map", pid) < 0) {
|
|
|
|
+ if (errno != EPERM)
|
|
|
|
+ bail("failed to update /proc/%d/gid_map", pid);
|
|
|
|
+ if (try_mapping_tool(path, pid, map, map_len))
|
|
|
|
+ bail("failed to use newgid map on %d", pid);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static void update_oom_score_adj(char *data, size_t len)
|
|
static void update_oom_score_adj(char *data, size_t len)
|
|
@@ -350,6 +439,14 @@ static void nl_parse(int fd, struct nlconfig_t *config)
|
|
config->gidmap = current;
|
|
config->gidmap = current;
|
|
config->gidmap_len = payload_len;
|
|
config->gidmap_len = payload_len;
|
|
break;
|
|
break;
|
|
|
|
+ case UIDMAPPATH_ATTR:
|
|
|
|
+ config->uidmappath = current;
|
|
|
|
+ config->uidmappath_len = payload_len;
|
|
|
|
+ break;
|
|
|
|
+ case GIDMAPPATH_ATTR:
|
|
|
|
+ config->gidmappath = current;
|
|
|
|
+ config->gidmappath_len = payload_len;
|
|
|
|
+ break;
|
|
case SETGROUP_ATTR:
|
|
case SETGROUP_ATTR:
|
|
config->is_setgroup = readint8(current);
|
|
config->is_setgroup = readint8(current);
|
|
break;
|
|
break;
|
|
@@ -596,8 +693,8 @@ void nsexec(void)
|
|
update_setgroups(child, SETGROUPS_DENY);
|
|
update_setgroups(child, SETGROUPS_DENY);
|
|
|
|
|
|
/* Set up mappings. */
|
|
/* Set up mappings. */
|
|
- update_uidmap(child, config.uidmap, config.uidmap_len);
|
|
|
|
- update_gidmap(child, config.gidmap, config.gidmap_len);
|
|
|
|
|
|
+ update_uidmap(config.uidmappath, child, config.uidmap, config.uidmap_len);
|
|
|
|
+ update_gidmap(config.gidmappath, child, config.gidmap, config.gidmap_len);
|
|
|
|
|
|
s = SYNC_USERMAP_ACK;
|
|
s = SYNC_USERMAP_ACK;
|
|
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
|
|
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
|