예제 #1
0
static void epoll_add(int epoll_fd, int fd, uint32_t events) {
    struct epoll_event event = { .data.fd = fd, .events = events };
    check_posix(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event), "epoll_ctl");
}

static void copy_to_stdstream(int in_fd, int out_fd) {
    uint8_t buffer[BUFSIZ];
    ssize_t n = read(in_fd, buffer, sizeof buffer);
    if (check_eagain(n, "read")) return;
    check_posix(write(out_fd, buffer, (size_t)n), "write");
}
예제 #2
0
static void do_trace(const struct signalfd_siginfo *si, bool *trace_init, FILE *learn) {
    int status;
    if (waitpid((pid_t)si->ssi_pid, &status, WNOHANG) != (pid_t)si->ssi_pid)
        errx(EXIT_FAILURE, "waitpid");

    if (WIFEXITED(status) || WIFSIGNALED(status) || !WIFSTOPPED(status))
        errx(EXIT_FAILURE, "unexpected ptrace event");

    int inject_signal = 0;
    if (*trace_init) {
        int signal = WSTOPSIG(status);
        if (signal != SIGTRAP || !(status & PTRACE_EVENT_SECCOMP))
            inject_signal = signal;
        else {
            errno = 0;
#ifdef __x86_64__
            long syscall = ptrace(PTRACE_PEEKUSER, si->ssi_pid, sizeof(long)*ORIG_RAX);
#else
            long syscall = ptrace(PTRACE_PEEKUSER, si->ssi_pid, sizeof(long)*ORIG_EAX);
#endif
            if (errno) err(EXIT_FAILURE, "ptrace");
            char *name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, (int)syscall);
            if (!name) errx(EXIT_FAILURE, "seccomp_syscall_resolve_num_arch");

            rewind(learn);
            char line[SYSCALL_NAME_MAX];
            while (fgets(line, sizeof line, learn)) {
                char *pos;
                if ((pos = strchr(line, '\n'))) *pos = '\0';
                if (!strcmp(name, line)) {
                    name = NULL;
                    break;
                }
            }

            if (name) {
                fprintf(learn, "%s\n", name);
                free(name);
            }
        }
    } else {
        check_posix(ptrace(PTRACE_SETOPTIONS, si->ssi_pid, 0, PTRACE_O_TRACESECCOMP), "ptrace");
        *trace_init = true;
    }
    check_posix(ptrace(PTRACE_CONT, si->ssi_pid, 0, inject_signal), "ptrace");
}
예제 #3
0
int main(int argc, char *argv[])
{
    if (argc < 2)
        return -1;

    int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    check_posix(fd, "failed to open uinput");

    check_posix(ioctl(fd, UI_SET_EVBIT, EV_KEY), "failed to set EV_KEY");
    check_posix(ioctl(fd, UI_SET_KEYBIT, KEY_LEFTSHIFT), "failed to set UI_SET_KEYBIT");

    for (int key = KEY_1; key <= KEY_SPACE; ++key) {
        check_posix(ioctl(fd, UI_SET_KEYBIT, key & KEYMASK),
                    "failed to set UI_SET_KEYBIT");
    }

    struct uinput_user_dev uidev = {
        .id.bustype = BUS_VIRTUAL,
        .id.vendor = 0x1,
        .id.product = 0x1,
        .id.version = 1
    };

    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "Virtual Injector Keyboard");
    check_posix(write(fd, &uidev, sizeof(uidev)),
                "failed to write uinput_user_dev struct");
    check_posix(ioctl(fd, UI_DEV_CREATE),
                "failed to create uinput device");

    usleep(500000);
    ev_inject_keypresses(fd, argv[1]);

    ioctl(fd, UI_DEV_DESTROY);
    close(fd);
}
예제 #4
0
// Mark any extra file descriptors `CLOEXEC`. Only `stdin`, `stdout` and `stderr` are left open.
static void prevent_leaked_file_descriptors() {
    DIR *dir = opendir("/proc/self/fd");
    if (!dir) err(EXIT_FAILURE, "opendir");
    struct dirent *dp;
    while ((dp = readdir(dir))) {
        char *end;
        int fd = (int)strtol(dp->d_name, &end, 10);
        if (*end == '\0' && fd > 2 && fd != dirfd(dir)) {
            check_posix(ioctl(fd, FIOCLEX), "ioctl");
        }
    }
    closedir(dir);
}
예제 #5
0
static void handle_signal(int sig_fd, sd_bus *connection, const char *unit_name,
                          bool *trace_init, FILE *learn) {
    struct signalfd_siginfo si;
    ssize_t bytes_r = read(sig_fd, &si, sizeof(si));
    check_posix(bytes_r, "read");

    if (bytes_r != sizeof(si))
        errx(EXIT_FAILURE, "read the wrong amount of bytes");

    switch (si.ssi_signo) {
    case SIGHUP:
    case SIGINT:
    case SIGTERM:
        stop_scope_unit(connection, unit_name);
        errx(EXIT_FAILURE, "interrupted, stopping early");
    }

    if (si.ssi_signo != SIGCHLD)
        errx(EXIT_FAILURE, "got an unexpected signal");

    switch (si.ssi_code) {
    case CLD_EXITED:
        if (si.ssi_status) {
            warnx("application terminated with error code %d", si.ssi_status);
        }
        exit(si.ssi_status);
    case CLD_KILLED:
    case CLD_DUMPED:
        errx(EXIT_FAILURE, "application terminated abnormally with signal %d (%s)",
             si.ssi_status, strsignal(si.ssi_status));
    case CLD_TRAPPED:
        do_trace(&si, trace_init, learn);
    case CLD_STOPPED:
    default:
        break;
    }
}
예제 #6
0
static void mountx(const char *source, const char *target, const char *filesystemtype,
                   unsigned long mountflags, const void *data) {
    check_posix(mount(source, target, filesystemtype, mountflags, data),
                "mounting %s as %s (%s) failed", source, target, filesystemtype);
}
예제 #7
0
static char *join_path(const char *left, const char *right) {
    char *dst;
    check_posix(asprintf(&dst, "%s/%s", left, right), "asprintf");
    return dst;
}
예제 #8
0
int main(int argc, char **argv) {
    prevent_leaked_file_descriptors();

    bool mount_proc = false;
    bool mount_dev = false;
    const char *username = "******";
    const char *hostname = "playpen";
    long timeout = 0;
    long memory_limit = 128;
    struct bind_list *binds = NULL, *binds_tail = NULL;
    char *devices = NULL;
    char *syscalls = NULL;
    const char *syscalls_file = NULL;
    const char *learn_name = NULL;

    static const struct option opts[] = {
        { "help",          no_argument,       0, 'h' },
        { "version",       no_argument,       0, 'v' },
        { "mount-proc",    no_argument,       0, 'p' },
        { "mount-dev",     no_argument,       0, 0x100 },
        { "bind",          required_argument, 0, 'b' },
        { "bind-rw",       required_argument, 0, 'B' },
        { "user",          required_argument, 0, 'u' },
        { "hostname",      required_argument, 0, 'n' },
        { "timeout",       required_argument, 0, 't' },
        { "memory-limit",  required_argument, 0, 'm' },
        { "devices",       required_argument, 0, 'd' },
        { "syscalls",      required_argument, 0, 's' },
        { "syscalls-file", required_argument, 0, 'S' },
        { "learn",         required_argument, 0, 'l' },
        { 0, 0, 0, 0 }
    };

    for (;;) {
        int opt = getopt_long(argc, argv, "hvpb:B:u:n:t:m:d:s:S:l:", opts, NULL);
        if (opt == -1)
            break;

        switch (opt) {
        case 'h':
            usage(stdout);
        case 'v':
            printf("%s %s\n", program_invocation_short_name, VERSION);
            return 0;
        case 'p':
            mount_proc = true;
            break;
        case 0x100:
            mount_dev = true;
            break;
        case 'b':
        case 'B':
            if (binds) {
                binds_tail->next = bind_list_alloc(optarg, opt == 'b');
                binds_tail = binds_tail->next;
            } else {
                binds = binds_tail = bind_list_alloc(optarg, opt == 'b');
            }
            break;
        case 'u':
            username = optarg;
            break;
        case 'n':
            hostname = optarg;
            break;
        case 't':
            timeout = strtolx_positive(optarg, "timeout");
            break;
        case 'm':
            memory_limit = strtolx_positive(optarg, "memory limit");
            break;
        case 'd':
            devices = optarg;
            break;
        case 's':
            syscalls = optarg;
            break;
        case 'S':
            syscalls_file = optarg;
            break;
        case 'l':
            learn_name = optarg;
            break;
        default:
            usage(stderr);
        }
    }

    if (argc - optind < 2) {
        usage(stderr);
    }

    const char *root = argv[optind];
    optind++;

    scmp_filter_ctx ctx = seccomp_init(learn_name ? SCMP_ACT_TRACE(0) : SCMP_ACT_KILL);
    if (!ctx) errx(EXIT_FAILURE, "seccomp_init");

    if (syscalls_file) {
        char name[SYSCALL_NAME_MAX];
        FILE *file = fopen(syscalls_file, "r");
        if (!file) err(EXIT_FAILURE, "failed to open syscalls file: %s", syscalls_file);
        while (fgets(name, sizeof name, file)) {
            char *pos;
            if ((pos = strchr(name, '\n'))) *pos = '\0';
            check(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, get_syscall_nr(name), 0));
        }
        fclose(file);
    }

    check(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, __NR_execve, 0));

    if (syscalls) {
        for (char *s_ptr = syscalls, *saveptr; ; s_ptr = NULL) {
            const char *syscall = strtok_r(s_ptr, ",", &saveptr);
            if (!syscall) break;
            check(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, get_syscall_nr(syscall), 0));
        }
    }

    int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    check_posix(epoll_fd, "epoll_create1");

    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);

    check_posix(sigprocmask(SIG_BLOCK, &mask, NULL), "sigprocmask");

    int sig_fd = signalfd(-1, &mask, SFD_CLOEXEC);
    check_posix(sig_fd, "signalfd");

    epoll_add(epoll_fd, sig_fd, EPOLLIN);

    int pipe_in[2];
    int pipe_out[2];
    int pipe_err[2];
    check_posix(pipe(pipe_in), "pipe");
    check_posix(pipe(pipe_out), "pipe");
    set_non_blocking(pipe_out[0]);
    check_posix(pipe(pipe_err), "pipe");
    set_non_blocking(pipe_err[0]);

    int rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO,
                       &(struct epoll_event){ .data.fd = STDIN_FILENO, .events = EPOLLIN });
예제 #9
0
static void set_non_blocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    check_posix(flags, "fcntl");
    check_posix(fcntl(fd, F_SETFL, flags | O_NONBLOCK), "fcntl");
}