From d891502489641740373c74e761e3b615845e11a0 Mon Sep 17 00:00:00 2001
From: LiFeng <lifeng68@huawei.com>
Date: Tue, 15 Jan 2019 22:55:06 -0500
Subject: [PATCH 036/131] drop_caps: add drop caps of current process

Signed-off-by: LiFeng <lifeng68@huawei.com>
---
 src/lxc/attach.c         | 26 ++++++++++---
 src/lxc/cgroups/cgfsng.c | 35 +++++++++---------
 src/lxc/conf.c           | 79 ++++++++++++++++++++++++++++++++++++++++
 src/lxc/conf.h           |  1 +
 src/lxc/start.c          | 16 ++++++++
 5 files changed, 135 insertions(+), 22 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 8cbbf962..3f60fe10 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -936,11 +936,6 @@ static int attach_child_main(struct attach_clone_payload *payload)
 		TRACE("Loaded seccomp profile");
 	}
 
-	close(payload->ipc_socket);
-	payload->ipc_socket = -EBADF;
-	lxc_proc_put_context_info(init_ctx);
-	payload->init_ctx = NULL;
-
 	/* The following is done after the communication socket is shut down.
 	 * That way, all errors that might (though unlikely) occur up until this
 	 * point will have their messages printed to the original stderr (if
@@ -997,9 +992,30 @@ static int attach_child_main(struct attach_clone_payload *payload)
 	if (new_gid == ns_root_gid)
 		new_gid = LXC_INVALID_GID;
 
+	if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+		SYSERROR("Failed to keep permitted capabilities");
+		goto on_error;
+	}
+
 	if (!lxc_switch_uid_gid(new_uid, new_gid))
 		goto on_error;
 
+	if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+		SYSERROR("Failed to clear permitted capabilities");
+		goto on_error;
+	}
+
+	if (init_ctx->container && init_ctx->container->lxc_conf &&
+			lxc_drop_caps(init_ctx->container->lxc_conf) != 0) {
+		ERROR("Failed to drop caps.");
+		goto on_error;
+	}
+
+	close(payload->ipc_socket);
+	payload->ipc_socket = -EBADF;
+	lxc_proc_put_context_info(init_ctx);
+	payload->init_ctx = NULL;
+
 	/* We're done, so we can now do whatever the user intended us to do. */
 	_exit(payload->exec_function(payload->exec_payload, msg_fd));
 
diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c
index 8b913a6b..bc1481d4 100644
--- a/src/lxc/cgroups/cgfsng.c
+++ b/src/lxc/cgroups/cgfsng.c
@@ -2664,11 +2664,11 @@ __cgfsng_ops static bool cgfsng_data_init(struct cgroup_ops *ops, struct lxc_han
 {
 	const char *cgroup_pattern;
 	char *container_cgroup, *tmp;
-	struct lxc_conf *conf = handler->conf;
+	struct lxc_conf *conf = NULL;
 	size_t len;
 
-	if (!conf)
-		return false;
+	if (handler)
+		conf = handler->conf;
 
 	/* copy system-wide cgroup information */
 	cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
@@ -2680,21 +2680,22 @@ __cgfsng_ops static bool cgfsng_data_init(struct cgroup_ops *ops, struct lxc_han
 	ops->cgroup_pattern = must_copy_string(cgroup_pattern);
 
 	/* isulad: init ops->container_cgroup here instead of in cgfsng_payload_create*/
-	if (conf->cgroup_meta.dir)
-		tmp = lxc_string_join("/", (const char *[]){conf->cgroup_meta.dir, handler->name, NULL}, false);
-	else
-		tmp = lxc_string_replace("%n", handler->name, ops->cgroup_pattern);
-	if (!tmp) {
-		ERROR("Failed expanding cgroup name pattern");
-		return false;
-	}
-
-	len = strlen(tmp) + 1;
-	container_cgroup = must_realloc(NULL, len);
-	(void)strlcpy(container_cgroup, tmp, len);
-	free(tmp);
-	ops->container_cgroup = container_cgroup;
+	if (conf) {
+		if (conf->cgroup_meta.dir)
+			tmp = lxc_string_join("/", (const char *[]){conf->cgroup_meta.dir, handler->name, NULL}, false);
+		else
+			tmp = lxc_string_replace("%n", handler->name, ops->cgroup_pattern);
+		if (!tmp) {
+			ERROR("Failed expanding cgroup name pattern");
+			return false;
+		}
 
+		len = strlen(tmp) + 1;
+		container_cgroup = must_realloc(NULL, len);
+		(void)strlcpy(container_cgroup, tmp, len);
+		free(tmp);
+		ops->container_cgroup = container_cgroup;
+	}
 	return true;
 }
 
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index fea0f592..6134ed3b 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -4220,6 +4220,85 @@ int lxc_setup(struct lxc_handler *handler)
 	return 0;
 }
 
+/* isulad drop caps for container*/
+int lxc_drop_caps(struct lxc_conf *conf)
+{
+#define __DEF_CAP_TO_MASK(x) (1U << ((x) & 31))
+#if HAVE_LIBCAP
+	struct lxc_list *iterator;
+	char *keep_entry;
+	int i, capid;
+	int numcaps = lxc_caps_last_cap() + 1;
+	struct lxc_list *caps = NULL;
+
+	if (lxc_list_empty(&conf->keepcaps))
+		return 0;
+
+	caps = &conf->keepcaps;
+
+	if (numcaps <= 0 || numcaps > 200)
+		return -1;
+
+	// caplist[i] is 1 if we keep capability i
+	int *caplist = alloca(numcaps * sizeof(int));
+	memset(caplist, 0, numcaps * sizeof(int));
+
+	lxc_list_for_each(iterator, caps) {
+
+		keep_entry = iterator->elem;
+		/* isulad: Do not keep any cap*/
+		if (strcmp(keep_entry, "ISULAD_KEEP_NONE") == 0) {
+			DEBUG("Do not keep any capability");
+			for(i = 0; i < numcaps; i++) {
+				caplist[i] = 0;
+			}
+			break;
+		}
+
+		capid = parse_cap(keep_entry);
+
+		if (capid == -2)
+			continue;
+
+	        if (capid < 0) {
+			ERROR("unknown capability %s", keep_entry);
+			return -1;
+		}
+
+		DEBUG("keep capability '%s' (%d)", keep_entry, capid);
+
+		caplist[capid] = 1;
+	}
+
+	struct __user_cap_header_struct cap_header_data;
+	struct __user_cap_data_struct cap_data_data[2];
+
+	cap_user_header_t cap_header = &cap_header_data;
+	cap_user_data_t cap_data = &cap_data_data[0];
+
+	memset(cap_header, 0 ,sizeof(struct __user_cap_header_struct));
+	memset(cap_data, 0, sizeof(struct __user_cap_data_struct) * 2);
+
+	cap_header->pid = 0;
+	cap_header->version = _LINUX_CAPABILITY_VERSION;
+
+	for (i = 0; i < numcaps; i++) {
+		if (caplist[i]) {
+			cap_data[CAP_TO_INDEX(i)].effective = cap_data[CAP_TO_INDEX(i)].effective | __DEF_CAP_TO_MASK(i);
+			cap_data[CAP_TO_INDEX(i)].permitted = cap_data[CAP_TO_INDEX(i)].permitted | __DEF_CAP_TO_MASK(i);
+			cap_data[CAP_TO_INDEX(i)].inheritable = cap_data[CAP_TO_INDEX(i)].inheritable | __DEF_CAP_TO_MASK(i);
+		}
+	}
+
+	if (capset(cap_header, cap_data)) {
+			SYSERROR("Failed to set capabilitys");
+			return -1;
+	}
+
+#endif
+	return 0;
+}
+
 struct oci_hook_conf {
 	defs_hook *ocihook;
 
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 44feb986..b92c48e1 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -498,6 +498,7 @@ int lxc_clear_init_args(struct lxc_conf *lxc_conf);
 int lxc_clear_populate_devices(struct lxc_conf *c);
 int lxc_clear_rootfs_masked_paths(struct lxc_conf *c);
 int lxc_clear_rootfs_ro_paths(struct lxc_conf *c);
+int lxc_drop_caps(struct lxc_conf *conf);
 
 /* isulad add end */
 
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 040909c3..357e81d2 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -1411,6 +1411,11 @@ static int do_start(void *data)
 		}
 	}
 
+	if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+		SYSERROR("Failed to keep permitted capabilities");
+		goto out_warn_father;
+	}
+
 	/* The container has been setup. We can now switch to an unprivileged
 	 * uid/gid.
 	 */
@@ -1448,6 +1453,17 @@ static int do_start(void *data)
 		goto out_warn_father;
 	}
 
+	/* isulad: drop the cap of current process */
+	if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+		SYSERROR("Failed to clear permitted capabilities");
+		goto out_warn_father;
+	}
+
+	if (lxc_drop_caps(handler->conf)) {
+		SYSERROR("Failed to drop caps");
+		goto out_warn_father;
+	}
+
 	/* After this call, we are in error because this ops should not return
 	 * as it execs.
 	 */
-- 
2.23.0

