diff mbox

[OpenWrt-Devel,procd,v2,5/5] jail: add capabilities support

Message ID 1440543643-43546-6-git-send-email-champetier.etienne@gmail.com
State Superseded
Headers show

Commit Message

Etienne Champetier Aug. 25, 2015, 11 p.m. UTC
If there is one or more capabilities in cap.keep,
drop all capabilities not in cap.keep.
Always drop all capabalities in cap.drop

exemple json syntax:
{
"cap.keep": [
        "cap_net_raw"
],
"cap.drop": []
}

Signed-off-by: Etienne CHAMPETIER <champetier.etienne@gmail.com>
---
 CMakeLists.txt         |  18 +++++---
 jail/capabilities.c    | 116 +++++++++++++++++++++++++++++++++++++++++++++++++
 jail/capabilities.h    |  14 ++++++
 jail/jail.c            |  15 +++++--
 make_capabilities_h.sh |  10 +++++
 5 files changed, 164 insertions(+), 9 deletions(-)
 create mode 100644 jail/capabilities.c
 create mode 100644 jail/capabilities.h
 create mode 100755 make_capabilities_h.sh
diff mbox

Patch

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 805e2ed..cc1e4a5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,7 +67,14 @@  ADD_CUSTOM_COMMAND(
 	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)
@@ -75,15 +82,16 @@  TARGET_LINK_LIBRARIES(preload-seccomp dl ubox blobmsg_json)
 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)
@@ -92,7 +100,7 @@  TARGET_LINK_LIBRARIES(utrace ubox ${json} blobmsg_json)
 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)
diff --git a/jail/capabilities.c b/jail/capabilities.c
new file mode 100644
index 0000000..b5ea965
--- /dev/null
+++ b/jail/capabilities.c
@@ -0,0 +1,116 @@ 
+/*
+ * 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;
+}
diff --git a/jail/capabilities.h b/jail/capabilities.h
new file mode 100644
index 0000000..e6699e9
--- /dev/null
+++ b/jail/capabilities.h
@@ -0,0 +1,14 @@ 
+/*
+ * 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);
diff --git a/jail/jail.c b/jail/jail.c
index 487d18f..cfaefdd 100644
--- a/jail/jail.c
+++ b/jail/jail.c
@@ -37,19 +37,21 @@ 
 #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;
@@ -243,6 +245,7 @@  static void usage(void)
 	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");
@@ -255,7 +258,7 @@  static void usage(void)
 	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)
@@ -273,8 +276,8 @@  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);
@@ -354,6 +357,10 @@  int main(int argc, char **argv)
 			opts.seccomp = optarg;
 			add_extra(optarg, 1);
 			break;
+		case 'C':
+			opts.capabilities = optarg;
+			add_extra(optarg, 1);
+			break;
 		case 'P':
 			opts.path = optarg;
 			break;
diff --git a/make_capabilities_h.sh b/make_capabilities_h.sh
new file mode 100755
index 0000000..635e740
--- /dev/null
+++ b/make_capabilities_h.sh
@@ -0,0 +1,10 @@ 
+#!/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 "};"