COMMAND ./make_syscall_h.sh ${CMAKE_C_COMPILER} > ./syscall-names.h
DEPENDS ./make_syscall_h.sh
)
-ADD_CUSTOM_TARGET(headers DEPENDS syscall-names.h)
+ADD_CUSTOM_TARGET(syscall-names-h DEPENDS syscall-names.h)
+
+ADD_CUSTOM_COMMAND(
+ OUTPUT capabilities-names.h
+ COMMAND ./make_capabilities_h.sh ${CMAKE_C_COMPILER} > ./capabilities-names.h
+ DEPENDS ./make_capabilities_h.sh
+)
+ADD_CUSTOM_TARGET(capabilities-names-h DEPENDS capabilities-names.h)
IF(SECCOMP_SUPPORT)
ADD_LIBRARY(preload-seccomp SHARED jail/preload.c jail/seccomp.c)
INSTALL(TARGETS preload-seccomp
LIBRARY DESTINATION lib
)
-ADD_DEPENDENCIES(preload-seccomp headers)
+ADD_DEPENDENCIES(preload-seccomp syscall-names-h)
endif()
IF(JAIL_SUPPORT)
-ADD_EXECUTABLE(ujail jail/jail.c jail/elf.c)
-TARGET_LINK_LIBRARIES(ujail ubox)
+ADD_EXECUTABLE(ujail jail/jail.c jail/elf.c jail/capabilities.c)
+TARGET_LINK_LIBRARIES(ujail ubox blobmsg_json)
INSTALL(TARGETS ujail
RUNTIME DESTINATION sbin
)
+ADD_DEPENDENCIES(ujail capabilities-names-h)
endif()
IF(UTRACE_SUPPORT)
INSTALL(TARGETS utrace
RUNTIME DESTINATION sbin
)
-ADD_DEPENDENCIES(utrace headers)
+ADD_DEPENDENCIES(utrace syscall-names-h)
ADD_LIBRARY(preload-trace SHARED trace/preload.c)
TARGET_LINK_LIBRARIES(preload-trace dl)
--- /dev/null
+/*
+ * Copyright (C) 2015 Etienne CHAMPETIER <champetier.etienne@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define _GNU_SOURCE 1
+#include <syslog.h>
+#include <sys/prctl.h>
+
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+
+#include "log.h"
+#include "../capabilities-names.h"
+#include "capabilities.h"
+
+static int find_capabilities(const char *name)
+{
+ int i;
+
+ for (i = 0; i <= CAP_LAST_CAP; i++)
+ if (capabilities_names[i] && !strcmp(capabilities_names[i], name))
+ return i;
+
+ return -1;
+}
+
+int drop_capabilities(const char *file)
+{
+ enum {
+ CAP_KEEP,
+ CAP_DROP,
+ __CAP_MAX
+ };
+ static const struct blobmsg_policy policy[__CAP_MAX] = {
+ [CAP_KEEP] = { .name = "cap.keep", .type = BLOBMSG_TYPE_ARRAY },
+ [CAP_DROP] = { .name = "cap.drop", .type = BLOBMSG_TYPE_ARRAY },
+ };
+ struct blob_buf b = { 0 };
+ struct blob_attr *tb[__CAP_MAX];
+ struct blob_attr *cur;
+ int rem, cap;
+ char *name;
+ uint64_t capdrop = 0LLU;
+
+ DEBUG("dropping capabilities\n");
+
+ blob_buf_init(&b, 0);
+ if (!blobmsg_add_json_from_file(&b, file)) {
+ ERROR("failed to load %s\n", file);
+ return -1;
+ }
+
+ blobmsg_parse(policy, __CAP_MAX, tb, blob_data(b.head), blob_len(b.head));
+ if (!tb[CAP_KEEP] && !tb[CAP_DROP]) {
+ ERROR("failed to parse %s\n", file);
+ return -1;
+ }
+
+ blobmsg_for_each_attr(cur, tb[CAP_KEEP], rem) {
+ name = blobmsg_get_string(cur);
+ if (!name) {
+ ERROR("invalid capability name in cap.keep\n");
+ return -1;
+ }
+ cap = find_capabilities(name);
+ if (cap == -1) {
+ ERROR("unknown capability %s in cap.keep\n", name);
+ return -1;
+ }
+ capdrop |= (1LLU << cap);
+ }
+
+ if (capdrop == 0LLU) {
+ DEBUG("cap.keep empty -> only dropping capabilities from cap.drop (blacklist)\n");
+ capdrop = 0xffffffffffffffffLLU;
+ } else {
+ DEBUG("cap.keep has at least one capability -> dropping every capabilities not in cap.keep (whitelist)\n");
+ }
+
+ blobmsg_for_each_attr(cur, tb[CAP_DROP], rem) {
+ name = blobmsg_get_string(cur);
+ if (!name) {
+ ERROR("invalid capability name in cap.drop\n");
+ return -1;
+ }
+ cap = find_capabilities(name);
+ if (cap == -1) {
+ ERROR("unknown capability %s in cap.drop\n", name);
+ return -1;
+ }
+ capdrop &= ~(1LLU << cap);
+ }
+
+ for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
+ if ( (capdrop & (1LLU << cap)) == 0) {
+ DEBUG("dropping capability %s (%d)\n", capabilities_names[cap], cap);
+ if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
+ ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s\n", cap, strerror(errno));
+ return errno;
+ }
+ } else {
+ DEBUG("keeping capability %s (%d)\n", capabilities_names[cap], cap);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 Etienne CHAMPETIER <champetier.etienne@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+int drop_capabilities(const char *file);
#include <sched.h>
#include "elf.h"
+#include "capabilities.h"
#include <libubox/utils.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
#define STACK_SIZE (1024 * 1024)
-#define OPT_ARGS "P:S:n:r:w:d:psulo"
+#define OPT_ARGS "P:S:C:n:r:w:d:psulo"
static struct {
char *path;
char *name;
char **jail_argv;
char *seccomp;
+ char *capabilities;
int procfs;
int ronly;
int sysfs;
fprintf(stderr, "ujail <options> -- <binary> <params ...>\n");
fprintf(stderr, " -P <path>\tpath where the jail will be staged\n");
fprintf(stderr, " -S <file>\tseccomp filter\n");
+ fprintf(stderr, " -C <file>\tcapabilities drop config\n");
fprintf(stderr, " -n <name>\tthe name of the jail\n");
fprintf(stderr, " -r <file>\treadonly files that should be staged\n");
fprintf(stderr, " -w <file>\twriteable files that should be staged\n");
fprintf(stderr, "\nWarning: by default root inside the jail is the same\n\
and he has the same powers as root outside the jail,\n\
thus he can escape the jail and/or break stuff.\n\
-Please use an appropriate seccomp filter (-S) to restrict his powers\n");
+Please use an appropriate seccomp/capabilities filter (-S/-C) to restrict his powers\n");
}
static int spawn_jail(void *arg)
if (!envp)
exit(EXIT_FAILURE);
- //TODO: drop capabilities() here
- //prctl(PR_CAPBSET_DROP, ..., 0, 0, 0);
+ if (opts.capabilities && drop_capabilities(opts.capabilities))
+ exit(EXIT_FAILURE);
INFO("exec-ing %s\n", *opts.jail_argv);
execve(*opts.jail_argv, opts.jail_argv, envp);
opts.seccomp = optarg;
add_extra(optarg, 1);
break;
+ case 'C':
+ opts.capabilities = optarg;
+ add_extra(optarg, 1);
+ break;
case 'P':
opts.path = optarg;
break;
--- /dev/null
+#!/bin/sh
+
+CC=$1
+[ -n "$TARGET_CC_NOCACHE" ] && CC=$TARGET_CC_NOCACHE
+
+echo "#include <linux/capability.h>"
+echo "static const char *capabilities_names[] = {"
+echo "#include <linux/capability.h>" | ${CC} -E -dM - | grep '#define CAP' | grep -vE '(CAP_TO|CAP_LAST_CAP)' | \
+ awk '{print $3" "$2}' | sort -n | awk '{print " ["$1"]\t= \""tolower($2)"\","}'
+echo "};"