From patchwork Wed Apr 12 04:32:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208466 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49F59C77B75 for ; Wed, 12 Apr 2023 04:33:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229658AbjDLEdZ convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:33:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53838 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229651AbjDLEdX (ORCPT ); Wed, 12 Apr 2023 00:33:23 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 48FD949FD for ; Tue, 11 Apr 2023 21:33:22 -0700 (PDT) Received: from pps.filterd (m0109333.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTSFC011352 for ; Tue, 11 Apr 2023 21:33:22 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pwahbm2hb-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:21 -0700 Received: from twshared52232.38.frc1.facebook.com (2620:10d:c0a8:1b::d) by mail.thefacebook.com (2620:10d:c0a8:82::c) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:19 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id F341A2DCF4424; Tue, 11 Apr 2023 21:33:14 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 1/8] bpf: move unprivileged checks into map_create() and bpf_prog_load() Date: Tue, 11 Apr 2023 21:32:53 -0700 Message-ID: <20230412043300.360803-2-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: X5SMYaGHPmGIXUwh3zg4D0IEPhypCtoX X-Proofpoint-ORIG-GUID: X5SMYaGHPmGIXUwh3zg4D0IEPhypCtoX X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Make each bpf() syscall command a bit more self-contained, making it easier to further enhance it. We move sysctl_unprivileged_bpf_disabled handling down to map_create() and bpf_prog_load(), two special commands in this regard. Signed-off-by: Andrii Nakryiko --- kernel/bpf/syscall.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6d575505f89c..c1d268025985 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1130,6 +1130,17 @@ static int map_create(union bpf_attr *attr) int f_flags; int err; + /* Intent here is for unprivileged_bpf_disabled to block key object + * creation commands for unprivileged users; other actions depend + * of fd availability and access to bpffs, so are dependent on + * object creation success. Capabilities are later verified for + * operations such as load and map create, so even with unprivileged + * BPF disabled, capability checks are still carried out for these + * and other operations. + */ + if (!bpf_capable() && sysctl_unprivileged_bpf_disabled) + return -EPERM; + err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; @@ -2512,6 +2523,17 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) char license[128]; bool is_gpl; + /* Intent here is for unprivileged_bpf_disabled to block key object + * creation commands for unprivileged users; other actions depend + * of fd availability and access to bpffs, so are dependent on + * object creation success. Capabilities are later verified for + * operations such as load and map create, so even with unprivileged + * BPF disabled, capability checks are still carried out for these + * and other operations. + */ + if (!bpf_capable() && sysctl_unprivileged_bpf_disabled) + return -EPERM; + if (CHECK_ATTR(BPF_PROG_LOAD)) return -EINVAL; @@ -5008,23 +5030,8 @@ static int bpf_prog_bind_map(union bpf_attr *attr) static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size) { union bpf_attr attr; - bool capable; int err; - capable = bpf_capable() || !sysctl_unprivileged_bpf_disabled; - - /* Intent here is for unprivileged_bpf_disabled to block key object - * creation commands for unprivileged users; other actions depend - * of fd availability and access to bpffs, so are dependent on - * object creation success. Capabilities are later verified for - * operations such as load and map create, so even with unprivileged - * BPF disabled, capability checks are still carried out for these - * and other operations. - */ - if (!capable && - (cmd == BPF_MAP_CREATE || cmd == BPF_PROG_LOAD)) - return -EPERM; - err = bpf_check_uarg_tail_zero(uattr, sizeof(attr), size); if (err) return err; From patchwork Wed Apr 12 04:32:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208468 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7923DC7619A for ; Wed, 12 Apr 2023 04:33:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229727AbjDLEdx convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:33:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54014 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229656AbjDLEdt (ORCPT ); Wed, 12 Apr 2023 00:33:49 -0400 Received: from mx0b-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2259C558F for ; Tue, 11 Apr 2023 21:33:32 -0700 (PDT) Received: from pps.filterd (m0148460.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTXbs002359 for ; Tue, 11 Apr 2023 21:33:32 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pvyvw7bp0-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:31 -0700 Received: from twshared29091.48.prn1.facebook.com (2620:10d:c085:208::f) by mail.thefacebook.com (2620:10d:c085:11d::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:30 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 15AE92DCF4443; Tue, 11 Apr 2023 21:33:17 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 2/8] bpf: inline map creation logic in map_create() function Date: Tue, 11 Apr 2023 21:32:54 -0700 Message-ID: <20230412043300.360803-3-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: IqjK82CN3vuHJlzMGHL9I4YgRflK-WK0 X-Proofpoint-GUID: IqjK82CN3vuHJlzMGHL9I4YgRflK-WK0 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Keep all the relevant generic sanity checks, permission checks, and creation and initialization logic in one linear piece of code. Currently helper function that handles memory allocation and partial initialization is split apart and is about 1000 lines higher in the file, hurting readability. Signed-off-by: Andrii Nakryiko --- kernel/bpf/syscall.c | 54 ++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c1d268025985..a090737f98ea 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -108,37 +108,6 @@ const struct bpf_map_ops bpf_map_offload_ops = { .map_mem_usage = bpf_map_offload_map_mem_usage, }; -static struct bpf_map *find_and_alloc_map(union bpf_attr *attr) -{ - const struct bpf_map_ops *ops; - u32 type = attr->map_type; - struct bpf_map *map; - int err; - - if (type >= ARRAY_SIZE(bpf_map_types)) - return ERR_PTR(-EINVAL); - type = array_index_nospec(type, ARRAY_SIZE(bpf_map_types)); - ops = bpf_map_types[type]; - if (!ops) - return ERR_PTR(-EINVAL); - - if (ops->map_alloc_check) { - err = ops->map_alloc_check(attr); - if (err) - return ERR_PTR(err); - } - if (attr->map_ifindex) - ops = &bpf_map_offload_ops; - if (!ops->map_mem_usage) - return ERR_PTR(-EINVAL); - map = ops->map_alloc(attr); - if (IS_ERR(map)) - return map; - map->ops = ops; - map->map_type = type; - return map; -} - static void bpf_map_write_active_inc(struct bpf_map *map) { atomic64_inc(&map->writecnt); @@ -1124,7 +1093,9 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, /* called via syscall */ static int map_create(union bpf_attr *attr) { + const struct bpf_map_ops *ops; int numa_node = bpf_map_attr_numa_node(attr); + u32 map_type = attr->map_type; struct btf_field_offs *foffs; struct bpf_map *map; int f_flags; @@ -1167,9 +1138,28 @@ static int map_create(union bpf_attr *attr) return -EINVAL; /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ - map = find_and_alloc_map(attr); + map_type = attr->map_type; + if (map_type >= ARRAY_SIZE(bpf_map_types)) + return -EINVAL; + map_type = array_index_nospec(map_type, ARRAY_SIZE(bpf_map_types)); + ops = bpf_map_types[map_type]; + if (!ops) + return -EINVAL; + + if (ops->map_alloc_check) { + err = ops->map_alloc_check(attr); + if (err) + return err; + } + if (attr->map_ifindex) + ops = &bpf_map_offload_ops; + if (!ops->map_mem_usage) + return -EINVAL; + map = ops->map_alloc(attr); if (IS_ERR(map)) return PTR_ERR(map); + map->ops = ops; + map->map_type = map_type; err = bpf_obj_name_cpy(map->name, attr->map_name, sizeof(attr->map_name)); From patchwork Wed Apr 12 04:32:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208467 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 63F3DC7619A for ; Wed, 12 Apr 2023 04:33:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229688AbjDLEdb convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:33:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54002 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229456AbjDLEda (ORCPT ); Wed, 12 Apr 2023 00:33:30 -0400 Received: from mx0b-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 83F06526A for ; Tue, 11 Apr 2023 21:33:28 -0700 (PDT) Received: from pps.filterd (m0109332.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTNq6016436 for ; Tue, 11 Apr 2023 21:33:27 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pvu7nrt91-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:27 -0700 Received: from twshared7331.15.prn3.facebook.com (2620:10d:c0a8:1b::d) by mail.thefacebook.com (2620:10d:c0a8:83::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:25 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 240802DCF4484; Tue, 11 Apr 2023 21:33:19 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 3/8] bpf: centralize permissions checks for all BPF map types Date: Tue, 11 Apr 2023 21:32:55 -0700 Message-ID: <20230412043300.360803-4-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: vN1oulbIAR6ICYFtcrVTYE6S2SFZV9GE X-Proofpoint-GUID: vN1oulbIAR6ICYFtcrVTYE6S2SFZV9GE X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: This allows to do more centralized decisions later on, and generally makes it very explicit which maps are privileged and which are not. Signed-off-by: Andrii Nakryiko --- kernel/bpf/bloom_filter.c | 3 - kernel/bpf/bpf_local_storage.c | 3 - kernel/bpf/bpf_struct_ops.c | 3 - kernel/bpf/cpumap.c | 4 -- kernel/bpf/devmap.c | 3 - kernel/bpf/hashtab.c | 6 -- kernel/bpf/lpm_trie.c | 3 - kernel/bpf/queue_stack_maps.c | 4 -- kernel/bpf/reuseport_array.c | 3 - kernel/bpf/stackmap.c | 3 - kernel/bpf/syscall.c | 70 ++++++++++++++++--- net/core/sock_map.c | 4 -- net/xdp/xskmap.c | 4 -- .../bpf/prog_tests/unpriv_bpf_disabled.c | 6 +- 14 files changed, 64 insertions(+), 55 deletions(-) diff --git a/kernel/bpf/bloom_filter.c b/kernel/bpf/bloom_filter.c index 540331b610a9..addf3dd57b59 100644 --- a/kernel/bpf/bloom_filter.c +++ b/kernel/bpf/bloom_filter.c @@ -86,9 +86,6 @@ static struct bpf_map *bloom_map_alloc(union bpf_attr *attr) int numa_node = bpf_map_attr_numa_node(attr); struct bpf_bloom_filter *bloom; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - if (attr->key_size != 0 || attr->value_size == 0 || attr->max_entries == 0 || attr->map_flags & ~BLOOM_CREATE_FLAG_MASK || diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index dab2ff4c99d9..2bb35b1c3740 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -720,9 +720,6 @@ int bpf_local_storage_map_alloc_check(union bpf_attr *attr) !attr->btf_key_type_id || !attr->btf_value_type_id) return -EINVAL; - if (!bpf_capable()) - return -EPERM; - if (attr->value_size > BPF_LOCAL_STORAGE_MAX_VALUE_SIZE) return -E2BIG; diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index d3f0a4825fa6..116a0ce378ec 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -655,9 +655,6 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) const struct btf_type *t, *vt; struct bpf_map *map; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - st_ops = bpf_struct_ops_find_value(attr->btf_vmlinux_value_type_id); if (!st_ops) return ERR_PTR(-ENOTSUPP); diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index 8ec18faa74ac..8a33e8747a0e 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -89,9 +88,6 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) u32 value_size = attr->value_size; struct bpf_cpu_map *cmap; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || (value_size != offsetofend(struct bpf_cpumap_val, qsize) && diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 802692fa3905..49cc0b5671c6 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -160,9 +160,6 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) struct bpf_dtab *dtab; int err; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); - dtab = bpf_map_area_alloc(sizeof(*dtab), NUMA_NO_NODE); if (!dtab) return ERR_PTR(-ENOMEM); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 00c253b84bf5..c69db80fc947 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -422,12 +422,6 @@ static int htab_map_alloc_check(union bpf_attr *attr) BUILD_BUG_ON(offsetof(struct htab_elem, fnode.next) != offsetof(struct htab_elem, hash_node.pprev)); - if (lru && !bpf_capable()) - /* LRU implementation is much complicated than other - * maps. Hence, limit to CAP_BPF. - */ - return -EPERM; - if (zero_seed && !capable(CAP_SYS_ADMIN)) /* Guard against local DoS, and discourage production use. */ return -EPERM; diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index e0d3ddf2037a..17c7e7782a1f 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -544,9 +544,6 @@ static struct bpf_map *trie_alloc(union bpf_attr *attr) { struct lpm_trie *trie; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - /* check sanity of attributes */ if (attr->max_entries == 0 || !(attr->map_flags & BPF_F_NO_PREALLOC) || diff --git a/kernel/bpf/queue_stack_maps.c b/kernel/bpf/queue_stack_maps.c index 601609164ef3..8d2ddcb7566b 100644 --- a/kernel/bpf/queue_stack_maps.c +++ b/kernel/bpf/queue_stack_maps.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "percpu_freelist.h" @@ -46,9 +45,6 @@ static bool queue_stack_map_is_full(struct bpf_queue_stack *qs) /* Called from syscall */ static int queue_stack_map_alloc_check(union bpf_attr *attr) { - if (!bpf_capable()) - return -EPERM; - /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 0 || attr->value_size == 0 || diff --git a/kernel/bpf/reuseport_array.c b/kernel/bpf/reuseport_array.c index cbf2d8d784b8..4b4f9670f1a9 100644 --- a/kernel/bpf/reuseport_array.c +++ b/kernel/bpf/reuseport_array.c @@ -151,9 +151,6 @@ static struct bpf_map *reuseport_array_alloc(union bpf_attr *attr) int numa_node = bpf_map_attr_numa_node(attr); struct reuseport_array *array; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - /* allocate all map elements and zero-initialize them */ array = bpf_map_area_alloc(struct_size(array, ptrs, attr->max_entries), numa_node); if (!array) diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index b25fce425b2c..458bb80b14d5 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -74,9 +74,6 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr) u64 cost, n_buckets; int err; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - if (attr->map_flags & ~STACK_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index a090737f98ea..cbea4999e92f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1101,17 +1101,6 @@ static int map_create(union bpf_attr *attr) int f_flags; int err; - /* Intent here is for unprivileged_bpf_disabled to block key object - * creation commands for unprivileged users; other actions depend - * of fd availability and access to bpffs, so are dependent on - * object creation success. Capabilities are later verified for - * operations such as load and map create, so even with unprivileged - * BPF disabled, capability checks are still carried out for these - * and other operations. - */ - if (!bpf_capable() && sysctl_unprivileged_bpf_disabled) - return -EPERM; - err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; @@ -1155,6 +1144,65 @@ static int map_create(union bpf_attr *attr) ops = &bpf_map_offload_ops; if (!ops->map_mem_usage) return -EINVAL; + + /* Intent here is for unprivileged_bpf_disabled to block key object + * creation commands for unprivileged users; other actions depend + * of fd availability and access to bpffs, so are dependent on + * object creation success. Capabilities are later verified for + * operations such as load and map create, so even with unprivileged + * BPF disabled, capability checks are still carried out for these + * and other operations. + */ + if (!bpf_capable() && sysctl_unprivileged_bpf_disabled) + return -EPERM; + + /* check privileged map type permissions */ + switch (map_type) { + case BPF_MAP_TYPE_SK_STORAGE: + case BPF_MAP_TYPE_INODE_STORAGE: + case BPF_MAP_TYPE_TASK_STORAGE: + case BPF_MAP_TYPE_CGRP_STORAGE: + case BPF_MAP_TYPE_BLOOM_FILTER: + case BPF_MAP_TYPE_LPM_TRIE: + case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: + case BPF_MAP_TYPE_STACK_TRACE: + case BPF_MAP_TYPE_QUEUE: + case BPF_MAP_TYPE_STACK: + case BPF_MAP_TYPE_LRU_HASH: + case BPF_MAP_TYPE_LRU_PERCPU_HASH: + case BPF_MAP_TYPE_STRUCT_OPS: + case BPF_MAP_TYPE_CPUMAP: + if (!bpf_capable()) + return -EPERM; + break; + case BPF_MAP_TYPE_SOCKMAP: + case BPF_MAP_TYPE_SOCKHASH: + case BPF_MAP_TYPE_DEVMAP: + case BPF_MAP_TYPE_DEVMAP_HASH: + case BPF_MAP_TYPE_XSKMAP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + break; + case BPF_MAP_TYPE_ARRAY: + case BPF_MAP_TYPE_PERCPU_ARRAY: + case BPF_MAP_TYPE_PROG_ARRAY: + case BPF_MAP_TYPE_PERF_EVENT_ARRAY: + case BPF_MAP_TYPE_CGROUP_ARRAY: + case BPF_MAP_TYPE_ARRAY_OF_MAPS: + case BPF_MAP_TYPE_HASH: + case BPF_MAP_TYPE_PERCPU_HASH: + case BPF_MAP_TYPE_HASH_OF_MAPS: + case BPF_MAP_TYPE_RINGBUF: + case BPF_MAP_TYPE_USER_RINGBUF: + case BPF_MAP_TYPE_CGROUP_STORAGE: + case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: + /* unprivileged */ + break; + default: + WARN(1, "unsupported map type %d", map_type); + return -EPERM; + } + map = ops->map_alloc(attr); if (IS_ERR(map)) return PTR_ERR(map); diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 7c189c2e2fbf..4b67bb5e7f9c 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -32,8 +32,6 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr) { struct bpf_stab *stab; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); if (attr->max_entries == 0 || attr->key_size != 4 || (attr->value_size != sizeof(u32) && @@ -1085,8 +1083,6 @@ static struct bpf_map *sock_hash_alloc(union bpf_attr *attr) struct bpf_shtab *htab; int i, err; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); if (attr->max_entries == 0 || attr->key_size == 0 || (attr->value_size != sizeof(u32) && diff --git a/net/xdp/xskmap.c b/net/xdp/xskmap.c index 2c1427074a3b..e1c526f97ce3 100644 --- a/net/xdp/xskmap.c +++ b/net/xdp/xskmap.c @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -68,9 +67,6 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr) int numa_node; u64 size; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); - if (attr->max_entries == 0 || attr->key_size != 4 || attr->value_size != 4 || attr->map_flags & ~(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)) diff --git a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c index 8383a99f610f..0adf8d9475cb 100644 --- a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c +++ b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c @@ -171,7 +171,11 @@ static void test_unpriv_bpf_disabled_negative(struct test_unpriv_bpf_disabled *s prog_insns, prog_insn_cnt, &load_opts), -EPERM, "prog_load_fails"); - for (i = BPF_MAP_TYPE_HASH; i <= BPF_MAP_TYPE_BLOOM_FILTER; i++) + /* some map types require particular correct parameters which could be + * sanity-checked before enforcing -EPERM, so only validate that + * the simple ARRAY and HASH maps are failing with -EPERM + */ + for (i = BPF_MAP_TYPE_HASH; i <= BPF_MAP_TYPE_ARRAY; i++) ASSERT_EQ(bpf_map_create(i, NULL, sizeof(int), sizeof(int), 1, NULL), -EPERM, "map_create_fails"); From patchwork Wed Apr 12 04:32:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208469 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2ADDEC77B73 for ; Wed, 12 Apr 2023 04:33:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229741AbjDLEdy convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:33:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54132 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229708AbjDLEdu (ORCPT ); Wed, 12 Apr 2023 00:33:50 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1EE325272 for ; Tue, 11 Apr 2023 21:33:34 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTZnB032027 for ; Tue, 11 Apr 2023 21:33:33 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pwf17hy9g-4 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:33 -0700 Received: from twshared29091.48.prn1.facebook.com (2620:10d:c085:108::4) by mail.thefacebook.com (2620:10d:c085:11d::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:30 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 326AA2DCF44CF; Tue, 11 Apr 2023 21:33:21 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 4/8] bpf, lsm: implement bpf_map_create_security LSM hook Date: Tue, 11 Apr 2023 21:32:56 -0700 Message-ID: <20230412043300.360803-5-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: PR9VQEQZYQKUlLe1GUPvWUOHZRqsl3bj X-Proofpoint-GUID: PR9VQEQZYQKUlLe1GUPvWUOHZRqsl3bj X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Add new LSM hook, bpf_map_create_security, that allows custom LSM security policies controlling BPF map creation permissions granularly and precisely. This new LSM hook allows to implement both LSM policy that could enforce more granular and restrictive decisions about which processes can create which BPF maps, by rejecting BPF map creation based on passed in bpf_attr attributes. But also it allows to bypass CAP_BPF and CAP_NET_ADMIN restrictions, normally enforced by kernel, for applications that LSM policy deems trusted. Trustworthiness determination of the process/user/cgroup/etc is left up to custom LSM hook implementation and will dependon particular production setup of each individual use case. If LSM policy wants to rely on default kernel logic, it can return 0 to delegate back to kernel. If it returns >0 return code, kernel will bypass its normal checks. This way it's possible to perform a delegation of trust (specifically for BPF map creation) from privileged LSM custom policy implementation to unprivileged user process, verifier and trusted by custom LSM policy. Such model allows flexible and secure-by-default approach where user processes that need to use BPF features (BPF map creation, in this case) are left unprivileged with no CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON, etc. capabilities, but specific exceptions are implemented (usually in a centralized server fleet-wide fashion) for trusted processes/containers/users, allowing them to manipulate BPF facilities, as long as they are allowed and known apriori. This patch implements first required part for full-fledged BPF usage: map creation. The other one, BPF program load, will be addressed in follow up patches. Signed-off-by: Andrii Nakryiko --- include/linux/lsm_hook_defs.h | 1 + include/linux/lsm_hooks.h | 12 ++++++++++++ include/linux/security.h | 6 ++++++ kernel/bpf/bpf_lsm.c | 1 + kernel/bpf/syscall.c | 19 ++++++++++++++++--- security/security.c | 4 ++++ 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 094b76dc7164..b4fe9ed7021a 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -396,6 +396,7 @@ LSM_HOOK(void, LSM_RET_VOID, audit_rule_free, void *lsmrule) LSM_HOOK(int, 0, bpf, int cmd, union bpf_attr *attr, unsigned int size) LSM_HOOK(int, 0, bpf_map, struct bpf_map *map, fmode_t fmode) LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog) +LSM_HOOK(int, 0, bpf_map_create_security, const union bpf_attr *attr) LSM_HOOK(int, 0, bpf_map_alloc_security, struct bpf_map *map) LSM_HOOK(void, LSM_RET_VOID, bpf_map_free_security, struct bpf_map *map) LSM_HOOK(int, 0, bpf_prog_alloc_security, struct bpf_prog_aux *aux) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 6e156d2acffc..42bf7c0aa4d8 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1598,6 +1598,18 @@ * @prog: bpf prog that userspace want to use. * Return 0 if permission is granted. * + * @bpf_map_create_security: + * Do a check to determine permission to create requested BPF map. + * Implementation can override kernel capabilities checks according to + * the rules below: + * - 0 should be returned to delegate permission checks to other + * installed LSM callbacks and/or hard-wired kernel logic, which + * would enforce CAP_BPF/CAP_NET_ADMIN capabilities; + * - reject BPF map creation by returning -EPERM or any other + * negative error code; + * - allow BPF map creation, overriding kernel checks, by returning + * a positive result. + * * @bpf_map_alloc_security: * Initialize the security field inside bpf map. * Return 0 on success, error on failure. diff --git a/include/linux/security.h b/include/linux/security.h index 5984d0d550b4..e5374fe92ef6 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2023,6 +2023,7 @@ struct bpf_prog_aux; extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size); extern int security_bpf_map(struct bpf_map *map, fmode_t fmode); extern int security_bpf_prog(struct bpf_prog *prog); +extern int security_bpf_map_create(const union bpf_attr *attr); extern int security_bpf_map_alloc(struct bpf_map *map); extern void security_bpf_map_free(struct bpf_map *map); extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux); @@ -2044,6 +2045,11 @@ static inline int security_bpf_prog(struct bpf_prog *prog) return 0; } +static inline int security_bpf_map_create(const union bpf_attr *attr) +{ + return 0; +} + static inline int security_bpf_map_alloc(struct bpf_map *map) { return 0; diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index e14c822f8911..931d4dda5dac 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -260,6 +260,7 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) BTF_SET_START(sleepable_lsm_hooks) BTF_ID(func, bpf_lsm_bpf) BTF_ID(func, bpf_lsm_bpf_map) +BTF_ID(func, bpf_lsm_bpf_map_create_security) BTF_ID(func, bpf_lsm_bpf_map_alloc_security) BTF_ID(func, bpf_lsm_bpf_map_free_security) BTF_ID(func, bpf_lsm_bpf_prog) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index cbea4999e92f..7d1165814efc 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -980,7 +980,7 @@ int map_check_no_btf(const struct bpf_map *map, } static int map_check_btf(struct bpf_map *map, const struct btf *btf, - u32 btf_key_id, u32 btf_value_id) + u32 btf_key_id, u32 btf_value_id, bool priv_checked) { const struct btf_type *key_type, *value_type; u32 key_size, value_size; @@ -1008,7 +1008,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, if (!IS_ERR_OR_NULL(map->record)) { int i; - if (!bpf_capable()) { + if (!priv_checked && !bpf_capable()) { ret = -EPERM; goto free_map_tab; } @@ -1097,10 +1097,12 @@ static int map_create(union bpf_attr *attr) int numa_node = bpf_map_attr_numa_node(attr); u32 map_type = attr->map_type; struct btf_field_offs *foffs; + bool priv_checked = false; struct bpf_map *map; int f_flags; int err; + /* sanity checks */ err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; @@ -1145,6 +1147,15 @@ static int map_create(union bpf_attr *attr) if (!ops->map_mem_usage) return -EINVAL; + /* security checks */ + err = security_bpf_map_create(attr); + if (err < 0) + return err; + if (err > 0) { + priv_checked = true; + goto skip_priv_checks; + } + /* Intent here is for unprivileged_bpf_disabled to block key object * creation commands for unprivileged users; other actions depend * of fd availability and access to bpffs, so are dependent on @@ -1203,6 +1214,8 @@ static int map_create(union bpf_attr *attr) return -EPERM; } +skip_priv_checks: + /* create and init map */ map = ops->map_alloc(attr); if (IS_ERR(map)) return PTR_ERR(map); @@ -1243,7 +1256,7 @@ static int map_create(union bpf_attr *attr) if (attr->btf_value_type_id) { err = map_check_btf(map, btf, attr->btf_key_type_id, - attr->btf_value_type_id); + attr->btf_value_type_id, priv_checked); if (err) goto free_map; } diff --git a/security/security.c b/security/security.c index cf6cc576736f..f9b885680966 100644 --- a/security/security.c +++ b/security/security.c @@ -2682,6 +2682,10 @@ int security_bpf_prog(struct bpf_prog *prog) { return call_int_hook(bpf_prog, 0, prog); } +int security_bpf_map_create(const union bpf_attr *attr) +{ + return call_int_hook(bpf_map_create_security, 0, attr); +} int security_bpf_map_alloc(struct bpf_map *map) { return call_int_hook(bpf_map_alloc_security, 0, map); From patchwork Wed Apr 12 04:32:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208470 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5F676C7619A for ; Wed, 12 Apr 2023 04:33:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229690AbjDLEdz convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:33:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54454 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229709AbjDLEdu (ORCPT ); Wed, 12 Apr 2023 00:33:50 -0400 Received: from mx0b-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 306475277 for ; Tue, 11 Apr 2023 21:33:34 -0700 (PDT) Received: from pps.filterd (m0109331.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTgos016733 for ; Tue, 11 Apr 2023 21:33:33 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pw088fcwe-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:33 -0700 Received: from twshared52232.38.frc1.facebook.com (2620:10d:c0a8:1b::d) by mail.thefacebook.com (2620:10d:c0a8:83::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:32 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 48FCF2DCF4531; Tue, 11 Apr 2023 21:33:23 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 5/8] selftests/bpf: validate new bpf_map_create_security LSM hook Date: Tue, 11 Apr 2023 21:32:57 -0700 Message-ID: <20230412043300.360803-6-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: 5TBZZSh2pziIAX4IYnLvK4b3wzZy5cZv X-Proofpoint-GUID: 5TBZZSh2pziIAX4IYnLvK4b3wzZy5cZv X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Add selftests that goes over every known map type and validates that a combination of privileged/unprivileged modes and allow/reject/pass-through LSM policy decisions behave as expected. Signed-off-by: Andrii Nakryiko --- .../selftests/bpf/prog_tests/lsm_map_create.c | 143 ++++++++++++++++++ .../selftests/bpf/progs/lsm_map_create.c | 32 ++++ tools/testing/selftests/bpf/test_progs.h | 6 + 3 files changed, 181 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_map_create.c create mode 100644 tools/testing/selftests/bpf/progs/lsm_map_create.c diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c new file mode 100644 index 000000000000..fee78b0448c3 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ +#include "linux/bpf.h" +#include +#include +#include "cap_helpers.h" +#include "lsm_map_create.skel.h" + +static int drop_priv_caps(__u64 *old_caps) +{ + return cap_disable_effective((1ULL << CAP_BPF) | + (1ULL << CAP_PERFMON) | + (1ULL << CAP_NET_ADMIN) | + (1ULL << CAP_SYS_ADMIN), old_caps); +} + +static int restore_priv_caps(__u64 old_caps) +{ + return cap_enable_effective(old_caps, NULL); +} + +void test_lsm_map_create(void) +{ + struct btf *btf = NULL; + struct lsm_map_create *skel = NULL; + const struct btf_type *t; + const struct btf_enum *e; + int i, n, id, err, ret; + + skel = lsm_map_create__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + skel->bss->my_tid = syscall(SYS_gettid); + skel->bss->decision = 0; + + err = lsm_map_create__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto cleanup; + + btf = btf__parse("/sys/kernel/btf/vmlinux", NULL); + if (!ASSERT_OK_PTR(btf, "btf_parse")) + goto cleanup; + + /* find enum bpf_map_type and enumerate each value */ + id = btf__find_by_name_kind(btf, "bpf_map_type", BTF_KIND_ENUM); + if (!ASSERT_GT(id, 0, "bpf_map_type_id")) + goto cleanup; + + t = btf__type_by_id(btf, id); + e = btf_enum(t); + n = btf_vlen(t); + for (i = 0; i < n; e++, i++) { + enum bpf_map_type map_type = (enum bpf_map_type)e->val; + const char *map_type_name; + __u64 orig_caps; + bool is_map_priv; + bool needs_btf; + + if (map_type == BPF_MAP_TYPE_UNSPEC) + continue; + + /* this will show which map type we are working with in verbose log */ + map_type_name = btf__str_by_offset(btf, e->name_off); + ASSERT_OK_PTR(map_type_name, map_type_name); + + switch (map_type) { + case BPF_MAP_TYPE_ARRAY: + case BPF_MAP_TYPE_PERCPU_ARRAY: + case BPF_MAP_TYPE_PROG_ARRAY: + case BPF_MAP_TYPE_PERF_EVENT_ARRAY: + case BPF_MAP_TYPE_CGROUP_ARRAY: + case BPF_MAP_TYPE_ARRAY_OF_MAPS: + case BPF_MAP_TYPE_HASH: + case BPF_MAP_TYPE_PERCPU_HASH: + case BPF_MAP_TYPE_HASH_OF_MAPS: + case BPF_MAP_TYPE_RINGBUF: + case BPF_MAP_TYPE_USER_RINGBUF: + case BPF_MAP_TYPE_CGROUP_STORAGE: + case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: + is_map_priv = false; + needs_btf = false; + break; + case BPF_MAP_TYPE_SK_STORAGE: + case BPF_MAP_TYPE_INODE_STORAGE: + case BPF_MAP_TYPE_TASK_STORAGE: + case BPF_MAP_TYPE_CGRP_STORAGE: + is_map_priv = true; + needs_btf = true; + break; + default: + is_map_priv = true; + needs_btf = false; + } + + /* make sure we delegate to kernel for final decision */ + skel->bss->decision = 0; + + /* we are normally under sudo, so all maps should succeed */ + ret = libbpf_probe_bpf_map_type(map_type, NULL); + ASSERT_EQ(ret, 1, "default_priv_mode"); + + /* local storage needs custom BTF to be loaded, which we + * currently can't do once we drop privileges, so skip few + * checks for such maps + */ + if (needs_btf) + goto skip_if_needs_btf; + + /* now let's drop privileges, and chech that unpriv maps are + * still possible to create + */ + if (!ASSERT_OK(drop_priv_caps(&orig_caps), "drop_caps")) + goto cleanup; + + ret = libbpf_probe_bpf_map_type(map_type, NULL); + ASSERT_EQ(ret, is_map_priv ? 0 : 1, "default_unpriv_mode"); + + /* allow any map creation for our thread */ + skel->bss->decision = 1; + ret = libbpf_probe_bpf_map_type(map_type, NULL); + ASSERT_EQ(ret, 1, "lsm_allow_unpriv_mode"); + + /* reject any map creation for our thread */ + skel->bss->decision = -1; + ret = libbpf_probe_bpf_map_type(map_type, NULL); + ASSERT_EQ(ret, 0, "lsm_reject_unpriv_mode"); + + /* restore privileges, but keep reject LSM policy */ + if (!ASSERT_OK(restore_priv_caps(orig_caps), "restore_caps")) + goto cleanup; + +skip_if_needs_btf: + /* even with all caps map create will fail */ + skel->bss->decision = -1; + ret = libbpf_probe_bpf_map_type(map_type, NULL); + ASSERT_EQ(ret, 0, "lsm_reject_priv_mode"); + } + +cleanup: + btf__free(btf); + lsm_map_create__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/lsm_map_create.c b/tools/testing/selftests/bpf/progs/lsm_map_create.c new file mode 100644 index 000000000000..093825c68459 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lsm_map_create.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include +#include + +char _license[] SEC("license") = "GPL"; + +int my_tid; +/* LSM enforcement: + * - 0, delegate to kernel; + * - 1, allow; + * - -1, reject. + */ +int decision; + +SEC("lsm/bpf_map_create_security") +int BPF_PROG(allow_unpriv_maps, union bpf_attr *attr) +{ + if (!my_tid || (u32)bpf_get_current_pid_tgid() != my_tid) + return 0; /* keep processing LSM hooks */ + + if (decision == 0) + return 0; + + if (decision > 0) + return 1; /* allow */ + + return -EPERM; +} diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h index 10ba43250668..12f9c6652d40 100644 --- a/tools/testing/selftests/bpf/test_progs.h +++ b/tools/testing/selftests/bpf/test_progs.h @@ -23,6 +23,7 @@ typedef __u16 __sum16; #include #include #include +#include #include #include @@ -176,6 +177,11 @@ void test__skip(void); void test__fail(void); int test__join_cgroup(const char *path); +static inline int gettid(void) +{ + return syscall(SYS_gettid); +} + #define PRINT_FAIL(format...) \ ({ \ test__fail(); \ From patchwork Wed Apr 12 04:32:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208476 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8CBDAC7619A for ; Wed, 12 Apr 2023 04:35:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229631AbjDLEfw convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:35:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55138 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229469AbjDLEfv (ORCPT ); Wed, 12 Apr 2023 00:35:51 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3DAFFE66 for ; Tue, 11 Apr 2023 21:35:50 -0700 (PDT) Received: from pps.filterd (m0044012.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTnNk005888 for ; Tue, 11 Apr 2023 21:35:49 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pw3jc6h5u-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:35:49 -0700 Received: from ash-exhub204.TheFacebook.com (2620:10d:c0a8:83::4) by ash-exhub101.TheFacebook.com (2620:10d:c0a8:82::e) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:35:48 -0700 Received: from twshared6687.46.prn1.facebook.com (2620:10d:c0a8:1b::d) by mail.thefacebook.com (2620:10d:c0a8:83::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:35:47 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 5B7492DCF455A; Tue, 11 Apr 2023 21:33:25 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 6/8] bpf: drop unnecessary bpf_capable() check in BPF_MAP_FREEZE command Date: Tue, 11 Apr 2023 21:32:58 -0700 Message-ID: <20230412043300.360803-7-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: K75pWBtJBbBedWry3W6moOFs6zaQ6enf X-Proofpoint-ORIG-GUID: K75pWBtJBbBedWry3W6moOFs6zaQ6enf X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Seems like that extra bpf_capable() check in BPF_MAP_FREEZE handler was unintentionally left when we switched to a model that all BPF map operations should be allowed regardless of CAP_BPF (or any other capabilities), as long as process got BPF map FD somehow. This patch replaces bpf_capable() check in BPF_MAP_FREEZE handler with writeable access check, given conceptually freezing the map is modifying it: map becomes unmodifiable for subsequent updates. Signed-off-by: Andrii Nakryiko --- kernel/bpf/syscall.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 7d1165814efc..42d8473237ab 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2001,6 +2001,11 @@ static int map_freeze(const union bpf_attr *attr) return -ENOTSUPP; } + if (!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) { + err = -EPERM; + goto err_put; + } + mutex_lock(&map->freeze_mutex); if (bpf_map_write_active(map)) { err = -EBUSY; @@ -2010,10 +2015,6 @@ static int map_freeze(const union bpf_attr *attr) err = -EBUSY; goto err_put; } - if (!bpf_capable()) { - err = -EPERM; - goto err_put; - } WRITE_ONCE(map->frozen, true); err_put: From patchwork Wed Apr 12 04:32:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208472 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B0963C7619A for ; Wed, 12 Apr 2023 04:34:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229726AbjDLEeQ convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:34:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54518 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229735AbjDLEdy (ORCPT ); Wed, 12 Apr 2023 00:33:54 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F38259E1 for ; Tue, 11 Apr 2023 21:33:38 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33BNTaoC032111 for ; Tue, 11 Apr 2023 21:33:37 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3pwf17hy9v-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:37 -0700 Received: from twshared58712.02.prn6.facebook.com (2620:10d:c085:208::11) by mail.thefacebook.com (2620:10d:c085:11d::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:36 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 69D702DCF45A2; Tue, 11 Apr 2023 21:33:27 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 7/8] bpf, lsm: implement bpf_btf_load_security LSM hook Date: Tue, 11 Apr 2023 21:32:59 -0700 Message-ID: <20230412043300.360803-8-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: MQTrK9OlEtupdVSKIrBrKufMxqbkSini X-Proofpoint-GUID: MQTrK9OlEtupdVSKIrBrKufMxqbkSini X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Add new LSM hook, bpf_btf_load_security, that allows custom LSM security policies controlling BTF data loading permissions (BPF_BTF_LOAD command of bpf() syscall) granularly and precisely. This complements bpf_map_create_security LSM hook added earlier and follow the same semantics: 0 means perform standard kernel capabilities-based checks, negative error rejects BTF object load, while positive one skips CAP_BPF check and allows BTF data object creation. With this hook, together with bpf_map_create_security, we now can also allow trusted unprivileged process to create BPF maps that require BTF, which we take advantaged in the next patch to improve the coverage of added BPF selftest. Signed-off-by: Andrii Nakryiko --- include/linux/lsm_hook_defs.h | 1 + include/linux/lsm_hooks.h | 13 +++++++++++++ include/linux/security.h | 6 ++++++ kernel/bpf/bpf_lsm.c | 1 + kernel/bpf/syscall.c | 10 ++++++++++ security/security.c | 4 ++++ 6 files changed, 35 insertions(+) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index b4fe9ed7021a..92cb0f95b970 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -396,6 +396,7 @@ LSM_HOOK(void, LSM_RET_VOID, audit_rule_free, void *lsmrule) LSM_HOOK(int, 0, bpf, int cmd, union bpf_attr *attr, unsigned int size) LSM_HOOK(int, 0, bpf_map, struct bpf_map *map, fmode_t fmode) LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog) +LSM_HOOK(int, 0, bpf_btf_load_security, const union bpf_attr *attr) LSM_HOOK(int, 0, bpf_map_create_security, const union bpf_attr *attr) LSM_HOOK(int, 0, bpf_map_alloc_security, struct bpf_map *map) LSM_HOOK(void, LSM_RET_VOID, bpf_map_free_security, struct bpf_map *map) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 42bf7c0aa4d8..cde96b5e15e2 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1598,6 +1598,19 @@ * @prog: bpf prog that userspace want to use. * Return 0 if permission is granted. * + * @bpf_btf_load_security: + * Do a check to determine permission to create BTF data object + * (BPF_BTF_LOAD command of bpf() syscall). + * Implementation can override kernel capabilities checks according to + * the rules below: + * - 0 should be returned to delegate permission checks to other + * installed LSM callbacks and/or hard-wired kernel logic, which + * would enforce CAP_BPF capability; + * - reject BTF data object creation by returning -EPERM or any other + * negative error code; + * - allow BTF data object creation, overriding kernel checks, by + * returning a positive result. + * * @bpf_map_create_security: * Do a check to determine permission to create requested BPF map. * Implementation can override kernel capabilities checks according to diff --git a/include/linux/security.h b/include/linux/security.h index e5374fe92ef6..f3ee1800392d 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2023,6 +2023,7 @@ struct bpf_prog_aux; extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size); extern int security_bpf_map(struct bpf_map *map, fmode_t fmode); extern int security_bpf_prog(struct bpf_prog *prog); +extern int security_bpf_btf_load(const union bpf_attr *attr); extern int security_bpf_map_create(const union bpf_attr *attr); extern int security_bpf_map_alloc(struct bpf_map *map); extern void security_bpf_map_free(struct bpf_map *map); @@ -2045,6 +2046,11 @@ static inline int security_bpf_prog(struct bpf_prog *prog) return 0; } +static inline int security_bpf_btf_load(const union bpf_attr *attr) +{ + return 0; +} + static inline int security_bpf_map_create(const union bpf_attr *attr) { return 0; diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 931d4dda5dac..53c39a18fd2c 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -260,6 +260,7 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) BTF_SET_START(sleepable_lsm_hooks) BTF_ID(func, bpf_lsm_bpf) BTF_ID(func, bpf_lsm_bpf_map) +BTF_ID(func, bpf_lsm_bpf_btf_load_security) BTF_ID(func, bpf_lsm_bpf_map_create_security) BTF_ID(func, bpf_lsm_bpf_map_alloc_security) BTF_ID(func, bpf_lsm_bpf_map_free_security) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 42d8473237ab..bbf70bddc770 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4449,12 +4449,22 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr, static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size) { + int err; + if (CHECK_ATTR(BPF_BTF_LOAD)) return -EINVAL; + /* security checks */ + err = security_bpf_btf_load(attr); + if (err < 0) + return err; + if (err > 0) + goto skip_priv_checks; + if (!bpf_capable()) return -EPERM; +skip_priv_checks: return btf_new_fd(attr, uattr, uattr_size); } diff --git a/security/security.c b/security/security.c index f9b885680966..8869802ef5f5 100644 --- a/security/security.c +++ b/security/security.c @@ -2682,6 +2682,10 @@ int security_bpf_prog(struct bpf_prog *prog) { return call_int_hook(bpf_prog, 0, prog); } +int security_bpf_btf_load(const union bpf_attr *attr) +{ + return call_int_hook(bpf_btf_load_security, 0, attr); +} int security_bpf_map_create(const union bpf_attr *attr) { return call_int_hook(bpf_map_create_security, 0, attr); From patchwork Wed Apr 12 04:33:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13208471 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A1FCBC7619A for ; Wed, 12 Apr 2023 04:34:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229456AbjDLEeM convert rfc822-to-8bit (ORCPT ); Wed, 12 Apr 2023 00:34:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229697AbjDLEdw (ORCPT ); Wed, 12 Apr 2023 00:33:52 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 063EE55B6 for ; Tue, 11 Apr 2023 21:33:35 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.19/8.17.1.19) with ESMTP id 33BNTGTT027786 for ; Tue, 11 Apr 2023 21:33:34 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by m0001303.ppops.net (PPS) with ESMTPS id 3pwf9whthn-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Tue, 11 Apr 2023 21:33:34 -0700 Received: from ash-exhub204.TheFacebook.com (2620:10d:c0a8:83::4) by ash-exhub201.TheFacebook.com (2620:10d:c0a8:83::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:33 -0700 Received: from twshared15216.17.frc2.facebook.com (2620:10d:c0a8:1b::d) by mail.thefacebook.com (2620:10d:c0a8:83::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.17; Tue, 11 Apr 2023 21:33:32 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 783802DCF45B3; Tue, 11 Apr 2023 21:33:29 -0700 (PDT) From: Andrii Nakryiko To: , , , , , CC: , Andrii Nakryiko Subject: [PATCH bpf-next 8/8] selftests/bpf: enhance lsm_map_create test with BTF LSM control Date: Tue, 11 Apr 2023 21:33:00 -0700 Message-ID: <20230412043300.360803-9-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230412043300.360803-1-andrii@kernel.org> References: <20230412043300.360803-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: In8E9wLkiuCIRbCCtHCWnZCXNcKlP8oH X-Proofpoint-GUID: In8E9wLkiuCIRbCCtHCWnZCXNcKlP8oH X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-11_16,2023-04-11_02,2023-02-09_01 Precedence: bulk List-ID: Adjust and augment lsm_map_create selftest with bpf_btf_load_security LSM hook and validate that BPF maps that require custom BTF are succeeding even without privileged, as long as LSM policy allows both BPF map and BTF object creation. Further, add another subtest that uses libbpf's BPF skeleton to create a bunch of maps declaratively. We also add read-only global variable to validate that BPF_MAP_FREEZE command follows LSM policy as well. Signed-off-by: Andrii Nakryiko --- .../selftests/bpf/prog_tests/lsm_map_create.c | 89 ++++++++++++++++--- tools/testing/selftests/bpf/progs/just_maps.c | 56 ++++++++++++ .../selftests/bpf/progs/lsm_map_create.c | 15 ++++ 3 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 tools/testing/selftests/bpf/progs/just_maps.c diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c index fee78b0448c3..497268d6febd 100644 --- a/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c +++ b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c @@ -5,6 +5,7 @@ #include #include "cap_helpers.h" #include "lsm_map_create.skel.h" +#include "just_maps.skel.h" static int drop_priv_caps(__u64 *old_caps) { @@ -19,7 +20,7 @@ static int restore_priv_caps(__u64 old_caps) return cap_enable_effective(old_caps, NULL); } -void test_lsm_map_create(void) +static void subtest_map_create_probes(void) { struct btf *btf = NULL; struct lsm_map_create *skel = NULL; @@ -59,6 +60,7 @@ void test_lsm_map_create(void) if (map_type == BPF_MAP_TYPE_UNSPEC) continue; + map_type = BPF_MAP_TYPE_SK_STORAGE; /* this will show which map type we are working with in verbose log */ map_type_name = btf__str_by_offset(btf, e->name_off); @@ -100,13 +102,6 @@ void test_lsm_map_create(void) ret = libbpf_probe_bpf_map_type(map_type, NULL); ASSERT_EQ(ret, 1, "default_priv_mode"); - /* local storage needs custom BTF to be loaded, which we - * currently can't do once we drop privileges, so skip few - * checks for such maps - */ - if (needs_btf) - goto skip_if_needs_btf; - /* now let's drop privileges, and chech that unpriv maps are * still possible to create */ @@ -114,7 +109,11 @@ void test_lsm_map_create(void) goto cleanup; ret = libbpf_probe_bpf_map_type(map_type, NULL); - ASSERT_EQ(ret, is_map_priv ? 0 : 1, "default_unpriv_mode"); + /* maps that require custom BTF will fail with -EPERM */ + if (needs_btf) + ASSERT_EQ(ret, -EPERM, "default_unpriv_mode"); + else + ASSERT_EQ(ret, is_map_priv ? 0 : 1, "default_unpriv_mode"); /* allow any map creation for our thread */ skel->bss->decision = 1; @@ -124,20 +123,86 @@ void test_lsm_map_create(void) /* reject any map creation for our thread */ skel->bss->decision = -1; ret = libbpf_probe_bpf_map_type(map_type, NULL); - ASSERT_EQ(ret, 0, "lsm_reject_unpriv_mode"); + /* maps that require custom BTF will fail with -EPERM */ + if (needs_btf) + ASSERT_EQ(ret, -EPERM, "lsm_reject_unpriv_mode"); + else + ASSERT_EQ(ret, 0, "lsm_reject_unpriv_mode"); /* restore privileges, but keep reject LSM policy */ if (!ASSERT_OK(restore_priv_caps(orig_caps), "restore_caps")) goto cleanup; -skip_if_needs_btf: /* even with all caps map create will fail */ skel->bss->decision = -1; ret = libbpf_probe_bpf_map_type(map_type, NULL); - ASSERT_EQ(ret, 0, "lsm_reject_priv_mode"); + if (needs_btf) + ASSERT_EQ(ret, -EPERM, "lsm_reject_priv_mode"); + else + ASSERT_EQ(ret, 0, "lsm_reject_priv_mode"); } cleanup: btf__free(btf); lsm_map_create__destroy(skel); } + +static void subtest_map_create_obj(void) +{ + struct lsm_map_create *skel = NULL; + struct just_maps *maps_skel = NULL; + struct bpf_map_info map_info; + __u32 map_info_sz = sizeof(map_info); + __u64 orig_caps; + int err, map_fd; + + skel = lsm_map_create__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + skel->bss->my_tid = syscall(SYS_gettid); + skel->bss->decision = 0; + + err = lsm_map_create__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto cleanup; + + /* now let's drop privileges, and chech that unpriv maps are + * still possible to create and they do have BTF associated with it + */ + if (!ASSERT_OK(drop_priv_caps(&orig_caps), "drop_caps")) + goto cleanup; + + /* allow unprivileged BPF map and BTF obj creation */ + skel->bss->decision = 1; + + maps_skel = just_maps__open_and_load(); + if (!ASSERT_OK_PTR(maps_skel, "maps_skel_open_and_load")) + goto restore_caps; + + ASSERT_GT(bpf_object__btf_fd(maps_skel->obj), 0, "maps_btf_fd"); + + /* check that SK_LOCAL_STORAGE map has BTF info */ + map_fd = bpf_map__fd(maps_skel->maps.sk_msg_netns_cookies); + memset(&map_info, 0, map_info_sz); + err = bpf_map_get_info_by_fd(map_fd, &map_info, &map_info_sz); + ASSERT_OK(err, "get_map_info_by_fd"); + + ASSERT_GT(map_info.btf_id, 0, "map_btf_id"); + ASSERT_GT(map_info.btf_key_type_id, 0, "map_btf_key_type_id"); + ASSERT_GT(map_info.btf_value_type_id, 0, "map_btf_value_type_id"); + +restore_caps: + ASSERT_OK(restore_priv_caps(orig_caps), "restore_caps"); +cleanup: + just_maps__destroy(maps_skel); + lsm_map_create__destroy(skel); +} + +void test_lsm_map_create(void) +{ + if (test__start_subtest("map_create_probes")) + subtest_map_create_probes(); + if (test__start_subtest("map_create_obj")) + subtest_map_create_obj(); +} diff --git a/tools/testing/selftests/bpf/progs/just_maps.c b/tools/testing/selftests/bpf/progs/just_maps.c new file mode 100644 index 000000000000..9073a51da705 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/just_maps.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ +#include +#include + +struct array_map { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} array SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); + __array(values, struct array_map); +} outer_arr SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); + __uint(max_entries, 5); + __type(key, int); + __array(values, struct array_map); +} outer_hash SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} sockarr SEC(".maps"); + +struct hmap_elem { + volatile int cnt; + struct bpf_spin_lock lock; + int test_padding; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct hmap_elem); +} hmap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, int); +} sk_msg_netns_cookies SEC(".maps"); + +/* .rodata to test BPF_MAP_FREEZE as well */ +const volatile int some_read_only_variable = 123; diff --git a/tools/testing/selftests/bpf/progs/lsm_map_create.c b/tools/testing/selftests/bpf/progs/lsm_map_create.c index 093825c68459..f3c8465c1ed0 100644 --- a/tools/testing/selftests/bpf/progs/lsm_map_create.c +++ b/tools/testing/selftests/bpf/progs/lsm_map_create.c @@ -30,3 +30,18 @@ int BPF_PROG(allow_unpriv_maps, union bpf_attr *attr) return -EPERM; } + +SEC("lsm/bpf_btf_load_security") +int BPF_PROG(allow_unpriv_btf, union bpf_attr *attr) +{ + if (!my_tid || (u32)bpf_get_current_pid_tgid() != my_tid) + return 0; /* keep processing LSM hooks */ + + if (decision == 0) + return 0; + + if (decision > 0) + return 1; /* allow */ + + return -EPERM; +}