jail: parse and apply OCI sysctl values
authorDaniel Golle <daniel@makrotopia.org>
Sat, 18 Jul 2020 21:58:22 +0000 (22:58 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Sun, 19 Jul 2020 18:26:09 +0000 (19:26 +0100)
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
jail/jail.c

index ca214cd3a0519be363d912f17ea65a450fa1aba5..f844b57127b72c14f8a32ed97d3baecf9fecc463 100644 (file)
@@ -59,6 +59,11 @@ struct hook_execvpe {
        int timeout;
 };
 
+struct sysctl_val {
+       char *entry;
+       char *value;
+};
+
 static struct {
        char *name;
        char *hostname;
@@ -76,6 +81,7 @@ static struct {
        char **envp;
        char *uidmap;
        char *gidmap;
+       struct sysctl_val **sysctl;
        int no_new_privs;
        int namespace;
        int procfs;
@@ -122,6 +128,18 @@ static void free_hooklist(struct hook_execvpe **hooklist)
        free(hooklist);
 }
 
+static void free_sysctl(void) {
+       struct sysctl_val *cur;
+       cur = *opts.sysctl;
+
+       while (cur) {
+               free(cur->entry);
+               free(cur->value);
+               free(cur++);
+       }
+       free(opts.sysctl);
+}
+
 static void free_opts(bool child) {
        char **tmp;
 
@@ -145,6 +163,7 @@ static void free_opts(bool child) {
                free(opts.envp);
        };
 
+       free_sysctl();
        free(opts.hostname);
        free(opts.cwd);
        free(opts.extroot);
@@ -402,6 +421,50 @@ static int run_hooks(struct hook_execvpe **hooklist)
        return 0;
 }
 
+static int apply_sysctl(const char *jail_root)
+{
+       struct sysctl_val **cur;
+       char *procdir, *fname;
+       int f;
+
+       if (!opts.sysctl)
+               return 0;
+
+       asprintf(&procdir, "%s/proc", jail_root);
+       if (!procdir)
+               return ENOMEM;
+
+       mkdir(procdir, 0700);
+       if (mount("proc", procdir, "proc", MS_NOATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID, 0))
+               return EPERM;
+
+       cur = opts.sysctl;
+
+       while (*cur) {
+               asprintf(&fname, "%s/sys/%s", procdir, (*cur)->entry);
+               if (!fname)
+                       return ENOMEM;
+
+               DEBUG("sysctl: writing '%s' to %s\n", (*cur)->value, fname);
+
+               f = open(fname, O_WRONLY);
+               if (f == -1) {
+                       ERROR("sysctl: can't open %s\n", fname);
+                       return errno;
+               }
+               write(f, (*cur)->value, strlen((*cur)->value));
+
+               free(fname);
+               close(f);
+               ++cur;
+       }
+       umount(procdir);
+       rmdir(procdir);
+       free(procdir);
+
+       return 0;
+}
+
 static int build_jail_fs(void)
 {
        char jail_root[] = "/tmp/ujail-XXXXXX";
@@ -415,6 +478,11 @@ static int build_jail_fs(void)
                return -1;
        }
 
+       if (apply_sysctl(jail_root)) {
+               ERROR("failed to apply sysctl values\n");
+               return -1;
+       }
+
        /* oldroot can't be MS_SHARED else pivot_root() fails */
        if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL)) {
                ERROR("private mount failed %m\n");
@@ -1381,6 +1449,49 @@ static const struct blobmsg_policy oci_linux_policy[] = {
        [OCI_LINUX_ROOTFSPROPAGATION] = { "rootfsPropagation", BLOBMSG_TYPE_STRING },
 };
 
+static int parseOCIsysctl(struct blob_attr *msg)
+{
+       struct blob_attr *cur;
+       int rem;
+       char *tmp, *tc;
+       size_t cnt = 0;
+
+       blobmsg_for_each_attr(cur, msg, rem) {
+               if (!blobmsg_name(cur) || !blobmsg_get_string(cur))
+                       return EINVAL;
+
+               ++cnt;
+       }
+
+       if (!cnt)
+               return 0;
+
+       opts.sysctl = calloc(cnt + 1, sizeof(struct sysctl_val *));
+       if (!opts.sysctl)
+               return ENOMEM;
+
+       cnt = 0;
+       blobmsg_for_each_attr(cur, msg, rem) {
+               opts.sysctl[cnt] = malloc(sizeof(struct sysctl_val));
+               if (!opts.sysctl[cnt])
+                       return ENOMEM;
+
+               /* replace '.' with '/' in entry name */
+               tc = tmp = strdup(blobmsg_name(cur));
+               while ((tc = strchr(tc, '.')))
+                       *tc = '/';
+
+               opts.sysctl[cnt]->value = strdup(blobmsg_get_string(cur));
+               opts.sysctl[cnt]->entry = tmp;
+
+               ++cnt;
+       }
+
+       opts.sysctl[cnt] = NULL;
+
+       return 0;
+}
+
 static int parseOCIlinux(struct blob_attr *msg)
 {
        struct blob_attr *tb[__OCI_LINUX_MAX];
@@ -1426,6 +1537,12 @@ static int parseOCIlinux(struct blob_attr *msg)
                }
        }
 
+       if (tb[OCI_LINUX_SYSCTL]) {
+               res = parseOCIsysctl(tb[OCI_LINUX_SYSCTL]);
+               if (res)
+                       return res;
+       }
+
        if (tb[OCI_LINUX_SECCOMP]) {
                opts.ociseccomp = parseOCIlinuxseccomp(tb[OCI_LINUX_SECCOMP]);
                if (!opts.ociseccomp)