X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=isolate%2Fisolate.c;h=c383664619d0698336702e3e2b755b58f97dfed0;hb=4af13f1bd0c9c39b756071754b513a4797c88f46;hp=ea14aa9998ba4f6e79dd6a838104ad92b558af7b;hpb=28c3aa2bd73958557fc5e5441bcb7a2a2ca1c375;p=moe.git diff --git a/isolate/isolate.c b/isolate/isolate.c index ea14aa9..c383664 100644 --- a/isolate/isolate.c +++ b/isolate/isolate.c @@ -21,21 +21,21 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include +#include #define NONRET __attribute__((noreturn)) #define UNUSED __attribute__((unused)) #define ARRAY_SIZE(a) (int)(sizeof(a)/sizeof(a[0])) -#define BOX_DIR CONFIG_ISOLATE_BOX_DIR -#define BOX_UID CONFIG_ISOLATE_BOX_UID -#define BOX_GID CONFIG_ISOLATE_BOX_GID - static int timeout; /* milliseconds */ static int wall_timeout; static int extra_timeout; @@ -43,18 +43,24 @@ static int pass_environ; static int verbose; static int memory_limit; static int stack_limit; +static int block_quota; +static int inode_quota; static int max_processes = 1; static char *redir_stdin, *redir_stdout, *redir_stderr; static int cg_enable; static int cg_memory_limit; static int cg_timing; -static char *cg_root = "/sys/fs/cgroup"; +static int box_id; +static char box_dir[1024]; +static pid_t box_pid; + +static uid_t box_uid; +static gid_t box_gid; static uid_t orig_uid; static gid_t orig_gid; -static pid_t box_pid; static int partial_line; static char cleanup_cmd[256]; @@ -393,7 +399,7 @@ setup_environment(void) return env; } -/*** Mount rules ***/ +/*** Directory rules ***/ struct dir_rule { char *inside; // A relative path @@ -407,8 +413,11 @@ enum dir_rule_flags { DIR_FLAG_NOEXEC = 2, DIR_FLAG_FS = 4, DIR_FLAG_MAYBE = 8, + DIR_FLAG_DEV = 16, }; +static const char * const dir_flag_names[] = { "rw", "noexec", "fs", "maybe", "dev" }; + static struct dir_rule *first_dir_rule; static struct dir_rule **last_dir_rule = &first_dir_rule; @@ -434,14 +443,14 @@ static int add_dir_rule(char *in, char *out, unsigned int flags) // Override an existing rule struct dir_rule *r; - for (r = first_dir_rule; r; r=r->next) + for (r = first_dir_rule; r; r = r->next) if (!strcmp(r->inside, in)) break; // Add a new rule if (!r) { - struct dir_rule *r = xmalloc(sizeof(*r)); + r = xmalloc(sizeof(*r)); r->inside = in; *last_dir_rule = r; last_dir_rule = &r->next; @@ -454,14 +463,9 @@ static int add_dir_rule(char *in, char *out, unsigned int flags) static unsigned int parse_dir_option(char *opt) { - if (!strcmp(opt, "rw")) - return DIR_FLAG_RW; - if (!strcmp(opt, "noexec")) - return DIR_FLAG_NOEXEC; - if (!strcmp(opt, "fs")) - return DIR_FLAG_FS; - if (!strcmp(opt, "maybe")) - return DIR_FLAG_MAYBE; + for (unsigned int i = 0; i < ARRAY_SIZE(dir_flag_names); i++) + if (!strcmp(opt, dir_flag_names[i])) + return 1U << i; die("Unknown directory option %s", opt); } @@ -473,11 +477,11 @@ static int set_dir_action(char *arg) unsigned int flags = 0; while (colon) { - char *opt = colon + 1; - char *next = strchr(opt, ':'); + *colon++ = 0; + char *next = strchr(colon, ':'); if (next) *next = 0; - flags |= parse_dir_option(opt); + flags |= parse_dir_option(colon); colon = next; } @@ -499,7 +503,7 @@ static void init_dir_rules(void) { set_dir_action("box=./box:rw"); set_dir_action("bin"); - set_dir_action("dev"); + set_dir_action("dev:dev"); set_dir_action("lib"); set_dir_action("lib64:maybe"); set_dir_action("proc=proc:fs"); @@ -508,7 +512,8 @@ static void init_dir_rules(void) static void make_dir(char *path) { - char *sep = path; + char *sep = (path[0] == '/' ? path+1 : path); + for (;;) { sep = strchr(sep, '/'); @@ -551,18 +556,23 @@ static void apply_dir_rules(void) mount_flags |= MS_RDONLY; if (r->flags & DIR_FLAG_NOEXEC) mount_flags |= MS_NOEXEC; + if (!(r->flags & DIR_FLAG_DEV)) + mount_flags |= MS_NODEV; if (r->flags & DIR_FLAG_FS) { - msg("Mounting %s on %s\n", out, in); + msg("Mounting %s on %s (flags %lx)\n", out, in, mount_flags); if (mount("none", root_in, out, mount_flags, "") < 0) die("Cannot mount %s on %s: %m", out, in); } else { - msg("Binding %s on %s\n", out, in); - if (mount(out, root_in, "none", MS_BIND | MS_NOSUID | MS_NODEV | mount_flags, "") < 0) - die("Cannot bind %s on %s: %m", out, in); + mount_flags |= MS_BIND | MS_NOSUID; + msg("Binding %s on %s (flags %lx)\n", out, in, mount_flags); + // Most mount flags need remount to work + if (mount(out, root_in, "none", mount_flags, "") < 0 || + mount(out, root_in, "none", MS_REMOUNT | mount_flags, "") < 0) + die("Cannot mount %s on %s: %m", out, in); } } } @@ -647,10 +657,11 @@ cg_init(void) if (!cg_enable) return; + char *cg_root = CONFIG_ISOLATE_CGROUP_ROOT; if (!dir_exists(cg_root)) die("Control group filesystem at %s not mounted", cg_root); - snprintf(cg_path, sizeof(cg_path), "%s/box-%d", cg_root, BOX_UID); + snprintf(cg_path, sizeof(cg_path), "%s/box-%d", cg_root, box_id); msg("Using control group %s\n", cg_path); } @@ -754,6 +765,86 @@ cg_remove(void) die("Cannot remove control group %s: %m", cg_path); } +/*** Disk quotas ***/ + +static int +path_begins_with(char *path, char *with) +{ + while (*with) + if (*path++ != *with++) + return 0; + return (!*with || *with == '/'); +} + +static char * +find_device(char *path) +{ + FILE *f = setmntent("/proc/mounts", "r"); + if (!f) + die("Cannot open /proc/mounts: %m"); + + struct mntent *me; + int best_len = 0; + char *best_dev = NULL; + while (me = getmntent(f)) + { + if (!path_begins_with(me->mnt_fsname, "/dev")) + continue; + if (path_begins_with(path, me->mnt_dir)) + { + int len = strlen(me->mnt_dir); + if (len > best_len) + { + best_len = len; + free(best_dev); + best_dev = xstrdup(me->mnt_fsname); + } + } + } + endmntent(f); + return best_dev; +} + +static void +set_quota(void) +{ + if (!block_quota) + return; + + char cwd[PATH_MAX]; + if (!getcwd(cwd, sizeof(cwd))) + die("getcwd: %m"); + + char *dev = find_device(cwd); + if (!dev) + die("Cannot identify filesystem which contains %s", cwd); + msg("Quota: Mapped path %s to a filesystem on %s\n", cwd, dev); + + // Sanity check + struct stat dev_st, cwd_st; + if (stat(dev, &dev_st) < 0) + die("Cannot identify block device %s: %m", dev); + if (!S_ISBLK(dev_st.st_mode)) + die("Expected that %s is a block device", dev); + if (stat(".", &cwd_st) < 0) + die("Cannot stat cwd: %m"); + if (cwd_st.st_dev != dev_st.st_rdev) + die("Identified %s as a filesystem on %s, but it is obviously false", cwd, dev); + + struct dqblk dq = { + .dqb_bhardlimit = block_quota, + .dqb_bsoftlimit = block_quota, + .dqb_ihardlimit = inode_quota, + .dqb_isoftlimit = inode_quota, + .dqb_valid = QIF_LIMITS, + }; + if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), dev, box_uid, (caddr_t) &dq) < 0) + die("Cannot set disk quota: %m"); + msg("Quota: Set block quota %d and inode quota %d\n", block_quota, inode_quota); + + free(dev); +} + /*** The keeper process ***/ static void @@ -969,11 +1060,11 @@ setup_root(void) static void setup_credentials(void) { - if (setresgid(BOX_GID, BOX_GID, BOX_GID) < 0) + if (setresgid(box_gid, box_gid, box_gid) < 0) die("setresgid: %m"); if (setgroups(0, NULL) < 0) die("setgroups: %m"); - if (setresuid(BOX_UID, BOX_UID, BOX_UID) < 0) + if (setresuid(box_uid, box_uid, box_uid) < 0) die("setresuid: %m"); setpgrp(); } @@ -1047,6 +1138,20 @@ box_inside(void *arg) die("execve(\"%s\"): %m", args[0]); } +static void +box_init(void) +{ + if (box_id < 0 || box_id >= CONFIG_ISOLATE_NUM_BOXES) + die("Sandbox ID out of range (allowed: 0-%d)", CONFIG_ISOLATE_NUM_BOXES-1); + box_uid = CONFIG_ISOLATE_FIRST_UID + box_id; + box_gid = CONFIG_ISOLATE_FIRST_GID + box_id; + + snprintf(box_dir, sizeof(box_dir), "%s/%d", CONFIG_ISOLATE_BOX_DIR, box_id); + make_dir(box_dir); + if (chdir(box_dir) < 0) + die("chdir(%s): %m", box_dir); +} + /*** Commands ***/ static void @@ -1060,6 +1165,9 @@ init(void) die("Cannot chown box: %m"); cg_prepare(); + set_quota(); + + puts(box_dir); } static void @@ -1069,7 +1177,9 @@ cleanup(void) die("Box directory not found, there isn't anything to clean up"); msg("Deleting sandbox directory\n"); - xsystem("rm -rf box"); + xsystem("rm -rf *"); + if (rmdir(box_dir) < 0) + die("Cannot remove %s: %m", box_dir); cg_remove(); } @@ -1080,7 +1190,7 @@ run(char **argv) die("Box directory not found, did you run `isolate --init'?"); char cmd[256]; - snprintf(cmd, sizeof(cmd), "chown -R %d.%d box", BOX_UID, BOX_GID); + snprintf(cmd, sizeof(cmd), "chown -R %d.%d box", box_uid, box_gid); xsystem(cmd); snprintf(cleanup_cmd, sizeof(cleanup_cmd), "chown -R %d.%d box", orig_uid, orig_gid); @@ -1109,8 +1219,12 @@ show_version(void) printf("Process isolator 1.0\n"); printf("(c) 2012 Martin Mares and Bernard Blackham\n"); printf("\nCompile-time configuration:\n"); - printf("Sandbox directory: %s\n", BOX_DIR); - printf("Sandbox credentials: uid=%u gid=%u\n", BOX_UID, BOX_GID); + printf("Sandbox directory: %s\n", CONFIG_ISOLATE_BOX_DIR); + printf("Sandbox credentials: uid=%u-%u gid=%u-%u\n", + CONFIG_ISOLATE_FIRST_UID, + CONFIG_ISOLATE_FIRST_UID + CONFIG_ISOLATE_NUM_BOXES - 1, + CONFIG_ISOLATE_FIRST_GID, + CONFIG_ISOLATE_FIRST_GID + CONFIG_ISOLATE_NUM_BOXES - 1); } /*** Options ***/ @@ -1123,13 +1237,19 @@ usage(void) Usage: isolate [] \n\ \n\ Options:\n\ +-b, --box-id=\tWhen multiple sandboxes are used in parallel, each must get a unique ID\n\ -c, --cg[=]\tPut process in a control group (optionally a sub-group of )\n\ --cg-mem=\tLimit memory usage of the control group to KB\n\ --cg-timing\t\tTime limits affects total run time of the control group\n\ -d, --dir=\t\tMake a directory visible inside the sandbox\n\ --dir==\tMake a directory outside visible as inside\n\ --dir==\t\tDelete a previously defined directory rule (even a default one)\n\ - --dir=...:\tSpecify options for a rule: rw, noexec, fs, maybe\n\ + --dir=...:\tSpecify options for a rule:\n\ +\t\t\t\tdev\tAllow access to special files\n\ +\t\t\t\tfs\tMount a filesystem (e.g., --dir=/proc:proc:fs)\n\ +\t\t\t\tmaybe\tSkip the rule if does not exist\n\ +\t\t\t\tnoexec\tDo not allow execution of binaries\n\ +\t\t\t\trw\tAllow read-write access\n\ -E, --env=\t\tInherit the environment variable from the parent process\n\ -E, --env==\tSet the environment variable to ; unset it if is empty\n\ -x, --extra-time=