Esempio n. 1
0
/**
 * seccomp_add_filter - adds a filter for an unfiltered syscall
 * @filters: filters object to add a filter/action to
 * @syscall_nr: system call number to add a filter for
 * @filter_string: the filter string to apply
 *
 * Returns 0 on success and non-zero otherwise.
 */
static int seccomp_add_filter(struct seccomp_filters *filters, int syscall_nr,
			      char *filter_string)
{
	struct event_filter *filter;
	int ret = 0;

	if (!strcmp(SECCOMP_FILTER_ALLOW, filter_string)) {
		set_seccomp_filter(filters, syscall_nr,
				   SECCOMP_ACTION_ALLOW, NULL);
		goto out;
	}

	/* ftrace events are not aware of CONFIG_COMPAT system calls and will
	 * use the incorrect argument metadata if enabled.
	 */
	ret = -ENOSYS;
	if (filters->flags.compat)
		goto out;

	ret = 0;
	filter = alloc_event_filter(syscall_nr, filter_string);
	if (IS_ERR(filter)) {
		ret = PTR_ERR(filter);
		goto out;
	}
	/* Always add to the last slot available since additions are
	 * are only done one at a time.
	 */
	set_seccomp_filter(filters, syscall_nr, filters->count - 1, filter);
out:
	return ret;
}
Esempio n. 2
0
/**
 * seccomp_extend_filter - appends more text to a syscall_nr's filter
 * @filters: unattached filter object to operate on
 * @syscall_nr: syscall number to update filters for
 * @filter_string: string to append to the existing filter
 *
 * The new string will be &&'d to the original filter string to ensure that it
 * always matches the existing predicates or less:
 *   (old_filter) && @filter_string
 * A new seccomp_filters instance is returned on success and a ERR_PTR on
 * failure.
 */
static int seccomp_extend_filter(struct seccomp_filters *filters,
				 int syscall_nr, char *filter_string)
{
	struct event_filter *filter;
	uint16_t id = seccomp_filter_id(filters, syscall_nr);
	char *merged = NULL;
	int ret = -EINVAL, expected;

	/* No extending with a "1". */
	if (!strcmp(SECCOMP_FILTER_ALLOW, filter_string))
		goto out;

	/* ftrace events are not aware of CONFIG_COMPAT system calls and will
	 * use the incorrect argument metadata if enabled.
	 */
	ret = -ENOSYS;
	if (filters->flags.compat)
		goto out;

	filter = seccomp_dynamic_filter(filters, id);
	ret = -ENOENT;
	if (!filter)
		goto out;

	merged = kzalloc(SECCOMP_MAX_FILTER_LENGTH + 1, GFP_KERNEL);
	ret = -ENOMEM;
	if (!merged)
		goto out;

	/* Encapsulate the filter strings in parentheses to isolate operator
	 * precedence behavior.
	 */
	expected = snprintf(merged, SECCOMP_MAX_FILTER_LENGTH, "(%s) && (%s)",
			    get_filter_string(filter), filter_string);
	ret = -E2BIG;
	if (expected >= SECCOMP_MAX_FILTER_LENGTH || expected < 0)
		goto out;

	/* Free the old filter */
	free_event_filter(filter);
	set_seccomp_filter(filters, syscall_nr, id, NULL);

	/* Replace it */
	filter = alloc_event_filter(syscall_nr, merged);
	if (IS_ERR(filter)) {
		ret = PTR_ERR(filter);
		goto out;
	}
	set_seccomp_filter(filters, syscall_nr, id, filter);
	ret = 0;

out:
	kfree(merged);
	return ret;
}
Esempio n. 3
0
/**
 * seccomp_filters_copy - copies filters from src to dst.
 *
 * @dst: seccomp_filters to populate.
 * @src: table to read from.
 * @skip: specifies an entry, by system call, to skip.
 *
 * Returns non-zero on failure.
 * Both the source and the destination should have no simultaneous
 * writers, and dst should be exclusive to the caller.
 * If @skip is < 0, it is ignored.
 */
static int seccomp_filters_copy(struct seccomp_filters *dst,
				const struct seccomp_filters *src,
				int skip)
{
	int id = 0, ret = 0, nr;
	memcpy(&dst->flags, &src->flags, sizeof(src->flags));
	memcpy(dst->syscalls, src->syscalls, sizeof(dst->syscalls));
	if (!src->count)
		goto done;
	for (nr = 0; nr < NR_syscalls; ++nr) {
		struct event_filter *filter;
		char *str;
		uint16_t src_id = seccomp_filter_id(src, nr);
		if (nr == skip) {
			set_seccomp_filter(dst, nr, SECCOMP_ACTION_DENY,
					   NULL);
			continue;
		}
		if (!seccomp_filter_dynamic(src_id))
			continue;
		if (id >= dst->count) {
			ret = -EINVAL;
			goto done;
		}
		str = get_filter_string(seccomp_dynamic_filter(src, src_id));
		filter = alloc_event_filter(nr, str);
		if (IS_ERR(filter)) {
			ret = PTR_ERR(filter);
			goto done;
		}
		set_seccomp_filter(dst, nr, id, filter);
		id++;
	}

done:
	return ret;
}
Esempio n. 4
0
void API minijail_enter(const struct minijail *j)
{
	if (j->flags.pids)
		die("tried to enter a pid-namespaced jail;"
		    "try minijail_run()?");

	if (j->flags.usergroups && !j->user)
		die("usergroup inheritance without username");

	/*
	 * We can't recover from failures if we've dropped privileges partially,
	 * so we don't even try. If any of our operations fail, we abort() the
	 * entire process.
	 */
	if (j->flags.vfs && unshare(CLONE_NEWNS))
		pdie("unshare(vfs)");

	if (j->flags.net && unshare(CLONE_NEWNET))
		pdie("unshare(net)");

	if (j->flags.chroot && enter_chroot(j))
		pdie("chroot");

	if (j->flags.chroot && j->flags.mount_tmp && mount_tmp())
		pdie("mount_tmp");

	if (j->flags.readonly && remount_readonly(j))
		pdie("remount");

	if (j->flags.caps) {
		/*
		 * POSIX capabilities are a bit tricky. If we drop our
		 * capability to change uids, our attempt to use setuid()
		 * below will fail. Hang on to root caps across setuid(), then
		 * lock securebits.
		 */
		if (prctl(PR_SET_KEEPCAPS, 1))
			pdie("prctl(PR_SET_KEEPCAPS)");
		if (prctl
		    (PR_SET_SECUREBITS, SECURE_ALL_BITS | SECURE_ALL_LOCKS))
			pdie("prctl(PR_SET_SECUREBITS)");
	}

	/*
	 * If we're setting no_new_privs, we can drop privileges
	 * before setting seccomp filter. This way filter policies
	 * don't need to allow privilege-dropping syscalls.
	 */
	if (j->flags.no_new_privs) {
		drop_ugid(j);
		if (j->flags.caps)
			drop_caps(j);

		set_seccomp_filter(j);
	} else {
		/*
		 * If we're not setting no_new_privs,
		 * we need to set seccomp filter *before* dropping privileges.
		 * WARNING: this means that filter policies *must* allow
		 * setgroups()/setresgid()/setresuid() for dropping root and
		 * capget()/capset()/prctl() for dropping caps.
		 */
		set_seccomp_filter(j);

		drop_ugid(j);
		if (j->flags.caps)
			drop_caps(j);
	}

	/*
	 * seccomp has to come last since it cuts off all the other
	 * privilege-dropping syscalls :)
	 */
	if (j->flags.seccomp && prctl(PR_SET_SECCOMP, 1))
		pdie("prctl(PR_SET_SECCOMP)");
}
Esempio n. 5
0
noinline int do_action(const char* arg) {
    // Prefixes.
    if (!strncmp(arg, "wait-", strlen("wait-"))) {
      char buf[1];
      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
      return do_action(arg + strlen("wait-"));
    } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
      errno = 0;
      while (errno != EMFILE) {
        open("/dev/null", O_RDONLY);
      }
      return do_action(arg + strlen("exhaustfd-"));
    } else if (!strncmp(arg, "thread-", strlen("thread-"))) {
        return do_action_on_thread(arg + strlen("thread-"));
    }

    // Actions.
    if (!strcasecmp(arg, "SIGSEGV-non-null")) {
        sigsegv_non_null();
    } else if (!strcasecmp(arg, "smash-stack")) {
        volatile int len = 128;
        return smash_stack(&len);
    } else if (!strcasecmp(arg, "stack-overflow")) {
        overflow_stack(nullptr);
    } else if (!strcasecmp(arg, "nostack")) {
        crashnostack();
    } else if (!strcasecmp(arg, "exit")) {
        exit(1);
    } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
        return crash(42);
    } else if (!strcasecmp(arg, "abort")) {
        maybe_abort();
    } else if (!strcasecmp(arg, "assert")) {
        __assert("some_file.c", 123, "false");
    } else if (!strcasecmp(arg, "assert2")) {
        __assert2("some_file.c", 123, "some_function", "false");
    } else if (!strcasecmp(arg, "fortify")) {
        char buf[10];
        __read_chk(-1, buf, 32, 10);
        while (true) pause();
    } else if (!strcasecmp(arg, "LOG(FATAL)")) {
        LOG(FATAL) << "hello " << 123;
    } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
        LOG_ALWAYS_FATAL("hello %s", "world");
    } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
    } else if (!strcasecmp(arg, "SIGFPE")) {
        raise(SIGFPE);
        return EXIT_SUCCESS;
    } else if (!strcasecmp(arg, "SIGTRAP")) {
        raise(SIGTRAP);
        return EXIT_SUCCESS;
    } else if (!strcasecmp(arg, "fprintf-NULL")) {
        fprintf_null();
    } else if (!strcasecmp(arg, "readdir-NULL")) {
        readdir_null();
    } else if (!strcasecmp(arg, "strlen-NULL")) {
        return strlen_null();
    } else if (!strcasecmp(arg, "pthread_join-NULL")) {
        return pthread_join(0, nullptr);
    } else if (!strcasecmp(arg, "heap-usage")) {
        abuse_heap();
    } else if (!strcasecmp(arg, "leak")) {
        leak();
    } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
        char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
                                                 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
        munmap(map, sizeof(int));
        map[0] = '8';
    } else if (!strcasecmp(arg, "seccomp")) {
        set_seccomp_filter();
        syscall(99999);
#if defined(__arm__)
    } else if (!strcasecmp(arg, "kuser_helper_version")) {
        return __kuser_helper_version;
    } else if (!strcasecmp(arg, "kuser_get_tls")) {
        return !__kuser_get_tls();
    } else if (!strcasecmp(arg, "kuser_cmpxchg")) {
        return __kuser_cmpxchg(0, 0, 0);
    } else if (!strcasecmp(arg, "kuser_memory_barrier")) {
        __kuser_dmb();
    } else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
        return __kuser_cmpxchg64(0, 0, 0);
#endif
    } else if (!strcasecmp(arg, "no_new_privs")) {
        if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
          fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
          return EXIT_SUCCESS;
        }
        abort();
    } else {
        return usage();
    }

    fprintf(stderr, "%s: exiting normally!\n", getprogname());
    return EXIT_SUCCESS;
}