From patchwork Wed Oct 18 20:00:22 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chenbo Feng X-Patchwork-Id: 827795 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="mPYa93CG"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHNGX2Fj1z9sBd for ; Thu, 19 Oct 2017 07:00:52 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751521AbdJRUAu (ORCPT ); Wed, 18 Oct 2017 16:00:50 -0400 Received: from mail-io0-f195.google.com ([209.85.223.195]:49236 "EHLO mail-io0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751490AbdJRUAq (ORCPT ); Wed, 18 Oct 2017 16:00:46 -0400 Received: by mail-io0-f195.google.com with SMTP id n137so7545333iod.6; Wed, 18 Oct 2017 13:00:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=gYxL3W3JjXHrMBZJdFbcfySJL7L5x5u3B3qFPRIZbEQ=; b=mPYa93CGPw7sXM26xXasWKGf2LaVxw+t8YMGHIdRiZTbVeM53jI/ZD3gtL4U4Yd1D6 4qvzCJs9KUl7JIXOWOj/g2Cd5o5DErfZGY4Xqj5mLPohUvE0h6TPDVK4hLm0TAWdj8mr nQLvf6YOf5o1NQMw1icaQokTyxon7R7z6OkXVF/jxLQyEFsAF/XuGBcBwXPH8ZYVk2dY LqrRS8vvTR+ZxYgT0DWPMS3EPc4aeCtPLWQZ7d6dtwrB30hzgehVL8Y8+Va+6YIVWnzr NQA75p/ib369LfiFAC59OMBwxdJ5Bt89P2Cq1GhYZJUZOVTuyNh7uc1BYDlUYcogcM1c nIRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=gYxL3W3JjXHrMBZJdFbcfySJL7L5x5u3B3qFPRIZbEQ=; b=FyQJCEfYqLCpQwFVohmxwP7fR9G/sEtX2Fb9Oku/0AaSlQAz5nfpY7rX6sHkqIcvru gDacZ2YLy4PEz1TGRFYzlO9+RtiOq4XfZcXVGcjC4sClgt1M0t6RrYzbP6UYp9rzDTXZ 4wUB/IGV+qK9QqTUVQxgqL1ssuCMQxQeWJI2vRCWm1ou2lNIEV1hDr+fzTFsb8VMLRFm LE+8LP/hxI3/MqX+kJcJBeado72VkTZYYpSLD7WESYLaZkhliBj9jeuTSlb9wd9DG8+2 +tiOKcaiCszu0t25v0CvDAV1+KKuMblECycy8KO41EtGU+7kszrJLkd7WVca13UZ6pxK dWbA== X-Gm-Message-State: AMCzsaWgjBiBkPiREUtNSe8XQuM4eW7EFC6q/rnwv3wG/2l1zD67kt28 WjPVrgEgohc6DurgrIzHQ+yiS3MdowQ= X-Google-Smtp-Source: ABhQp+QRMkBAn0POt80eC+S1AojOqu4uhqKCy6m1S6ws4j3rpeDTC+zf8RDGM0eC4AvVw+vuZNG1JQ== X-Received: by 10.107.150.67 with SMTP id y64mr16270235iod.74.1508356845494; Wed, 18 Oct 2017 13:00:45 -0700 (PDT) Received: from fengc.mtv.corp.google.com ([100.98.120.50]) by smtp.gmail.com with ESMTPSA id 78sm3550047ioo.37.2017.10.18.13.00.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 18 Oct 2017 13:00:44 -0700 (PDT) From: Chenbo Feng To: netdev@vger.kernel.org, SELinux , linux-security-module@vger.kernel.org Cc: Jeffrey Vander Stoep , Alexei Starovoitov , lorenzo@google.com, Daniel Borkmann , Stephen Smalley , James Morris , Paul Moore , Chenbo Feng Subject: [PATCH net-next v7 1/5] bpf: Add file mode configuration into bpf maps Date: Wed, 18 Oct 2017 13:00:22 -0700 Message-Id: <20171018200026.146093-2-chenbofeng.kernel@gmail.com> X-Mailer: git-send-email 2.15.0.rc1.287.g2b38de12cc-goog In-Reply-To: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> References: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Chenbo Feng Introduce the map read/write flags to the eBPF syscalls that returns the map fd. The flags is used to set up the file mode when construct a new file descriptor for bpf maps. To not break the backward capability, the f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise it should be O_RDONLY or O_WRONLY. When the userspace want to modify or read the map content, it will check the file mode to see if it is allowed to make the change. Signed-off-by: Chenbo Feng Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann --- include/linux/bpf.h | 8 +++-- include/uapi/linux/bpf.h | 6 ++++ kernel/bpf/arraymap.c | 6 +++- kernel/bpf/devmap.c | 5 ++- kernel/bpf/hashtab.c | 5 +-- kernel/bpf/inode.c | 15 ++++++--- kernel/bpf/lpm_trie.c | 3 +- kernel/bpf/sockmap.c | 5 ++- kernel/bpf/stackmap.c | 5 ++- kernel/bpf/syscall.c | 88 ++++++++++++++++++++++++++++++++++++++++++------ net/netfilter/xt_bpf.c | 2 +- 11 files changed, 122 insertions(+), 26 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d67ccdc0099f..3e5508f2fa87 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -315,11 +315,11 @@ void bpf_map_area_free(void *base); extern int sysctl_unprivileged_bpf_disabled; -int bpf_map_new_fd(struct bpf_map *map); +int bpf_map_new_fd(struct bpf_map *map, int flags); int bpf_prog_new_fd(struct bpf_prog *prog); int bpf_obj_pin_user(u32 ufd, const char __user *pathname); -int bpf_obj_get_user(const char __user *pathname); +int bpf_obj_get_user(const char __user *pathname, int flags); int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value); int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value); @@ -338,6 +338,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, void *key, void *value, u64 map_flags); int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value); +int bpf_get_file_flag(int flags); + /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and * forced to use 'long' read/writes to try to atomically copy long counters. * Best-effort only. No barriers here, since it _will_ race with concurrent @@ -421,7 +423,7 @@ static inline void __bpf_prog_uncharge(struct user_struct *user, u32 pages) { } -static inline int bpf_obj_get_user(const char __user *pathname) +static inline int bpf_obj_get_user(const char __user *pathname, int flags) { return -EOPNOTSUPP; } diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 4303fb6c3817..d83f95ea6a1b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -218,6 +218,10 @@ enum bpf_attach_type { #define BPF_OBJ_NAME_LEN 16U +/* Flags for accessing BPF object */ +#define BPF_F_RDONLY (1U << 3) +#define BPF_F_WRONLY (1U << 4) + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -260,6 +264,7 @@ union bpf_attr { struct { /* anonymous struct used by BPF_OBJ_* commands */ __aligned_u64 pathname; __u32 bpf_fd; + __u32 file_flags; }; struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ @@ -287,6 +292,7 @@ union bpf_attr { __u32 map_id; }; __u32 next_id; + __u32 open_flags; }; struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 68d866628be0..988c04c91e10 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -19,6 +19,9 @@ #include "map_in_map.h" +#define ARRAY_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + static void bpf_array_free_percpu(struct bpf_array *array) { int i; @@ -56,7 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE || + attr->value_size == 0 || + attr->map_flags & ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE)) return ERR_PTR(-EINVAL); diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index e093d9a2c4dd..e5d3de7cff2e 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -50,6 +50,9 @@ #include #include +#define DEV_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + struct bpf_dtab_netdev { struct net_device *dev; struct bpf_dtab *dtab; @@ -80,7 +83,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE) + attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); dtab = kzalloc(sizeof(*dtab), GFP_USER); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 431126f31ea3..919955236e63 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -18,8 +18,9 @@ #include "bpf_lru_list.h" #include "map_in_map.h" -#define HTAB_CREATE_FLAG_MASK \ - (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE) +#define HTAB_CREATE_FLAG_MASK \ + (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \ + BPF_F_RDONLY | BPF_F_WRONLY) struct bucket { struct hlist_nulls_head head; diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index be1dde967208..01aaef1a77c5 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -295,7 +295,7 @@ int bpf_obj_pin_user(u32 ufd, const char __user *pathname) } static void *bpf_obj_do_get(const struct filename *pathname, - enum bpf_type *type) + enum bpf_type *type, int flags) { struct inode *inode; struct path path; @@ -307,7 +307,7 @@ static void *bpf_obj_do_get(const struct filename *pathname, return ERR_PTR(ret); inode = d_backing_inode(path.dentry); - ret = inode_permission(inode, MAY_WRITE); + ret = inode_permission(inode, ACC_MODE(flags)); if (ret) goto out; @@ -326,18 +326,23 @@ static void *bpf_obj_do_get(const struct filename *pathname, return ERR_PTR(ret); } -int bpf_obj_get_user(const char __user *pathname) +int bpf_obj_get_user(const char __user *pathname, int flags) { enum bpf_type type = BPF_TYPE_UNSPEC; struct filename *pname; int ret = -ENOENT; + int f_flags; void *raw; + f_flags = bpf_get_file_flag(flags); + if (f_flags < 0) + return f_flags; + pname = getname(pathname); if (IS_ERR(pname)) return PTR_ERR(pname); - raw = bpf_obj_do_get(pname, &type); + raw = bpf_obj_do_get(pname, &type, f_flags); if (IS_ERR(raw)) { ret = PTR_ERR(raw); goto out; @@ -346,7 +351,7 @@ int bpf_obj_get_user(const char __user *pathname) if (type == BPF_TYPE_PROG) ret = bpf_prog_new_fd(raw); else if (type == BPF_TYPE_MAP) - ret = bpf_map_new_fd(raw); + ret = bpf_map_new_fd(raw, f_flags); else goto out; diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index 34d8a690ea05..885e45479680 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -495,7 +495,8 @@ static int trie_delete_elem(struct bpf_map *map, void *_key) #define LPM_KEY_SIZE_MAX LPM_KEY_SIZE(LPM_DATA_SIZE_MAX) #define LPM_KEY_SIZE_MIN LPM_KEY_SIZE(LPM_DATA_SIZE_MIN) -#define LPM_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE) +#define LPM_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE | \ + BPF_F_RDONLY | BPF_F_WRONLY) static struct bpf_map *trie_alloc(union bpf_attr *attr) { diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c index a298d6666698..86ec846f2d5e 100644 --- a/kernel/bpf/sockmap.c +++ b/kernel/bpf/sockmap.c @@ -40,6 +40,9 @@ #include #include +#define SOCK_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + struct bpf_stab { struct bpf_map map; struct sock **sock_map; @@ -489,7 +492,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE) + attr->value_size != 4 || attr->map_flags & ~SOCK_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); if (attr->value_size > KMALLOC_MAX_SIZE) diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 135be433e9a0..a15bc636cc98 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -11,6 +11,9 @@ #include #include "percpu_freelist.h" +#define STACK_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + struct stack_map_bucket { struct pcpu_freelist_node fnode; u32 hash; @@ -60,7 +63,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr) if (!capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); - if (attr->map_flags & ~BPF_F_NUMA_NODE) + if (attr->map_flags & ~STACK_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); /* check sanity of attributes */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 0e893cac6795..676a06e6b322 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -34,6 +34,8 @@ #define IS_FD_HASH(map) ((map)->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) #define IS_FD_MAP(map) (IS_FD_ARRAY(map) || IS_FD_HASH(map)) +#define BPF_OBJ_FLAG_MASK (BPF_F_RDONLY | BPF_F_WRONLY) + DEFINE_PER_CPU(int, bpf_prog_active); static DEFINE_IDR(prog_idr); static DEFINE_SPINLOCK(prog_idr_lock); @@ -294,17 +296,48 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) } #endif +static ssize_t bpf_dummy_read(struct file *filp, char __user *buf, size_t siz, + loff_t *ppos) +{ + /* We need this handler such that alloc_file() enables + * f_mode with FMODE_CAN_READ. + */ + return -EINVAL; +} + +static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf, + size_t siz, loff_t *ppos) +{ + /* We need this handler such that alloc_file() enables + * f_mode with FMODE_CAN_WRITE. + */ + return -EINVAL; +} + static const struct file_operations bpf_map_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_map_show_fdinfo, #endif .release = bpf_map_release, + .read = bpf_dummy_read, + .write = bpf_dummy_write, }; -int bpf_map_new_fd(struct bpf_map *map) +int bpf_map_new_fd(struct bpf_map *map, int flags) { return anon_inode_getfd("bpf-map", &bpf_map_fops, map, - O_RDWR | O_CLOEXEC); + flags | O_CLOEXEC); +} + +int bpf_get_file_flag(int flags) +{ + if ((flags & BPF_F_RDONLY) && (flags & BPF_F_WRONLY)) + return -EINVAL; + if (flags & BPF_F_RDONLY) + return O_RDONLY; + if (flags & BPF_F_WRONLY) + return O_WRONLY; + return O_RDWR; } /* helper macro to check that unused fields 'union bpf_attr' are zero */ @@ -344,12 +377,17 @@ static int map_create(union bpf_attr *attr) { int numa_node = bpf_map_attr_numa_node(attr); struct bpf_map *map; + int f_flags; int err; err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; + f_flags = bpf_get_file_flag(attr->map_flags); + if (f_flags < 0) + return f_flags; + if (numa_node != NUMA_NO_NODE && ((unsigned int)numa_node >= nr_node_ids || !node_online(numa_node))) @@ -375,7 +413,7 @@ static int map_create(union bpf_attr *attr) if (err) goto free_map; - err = bpf_map_new_fd(map); + err = bpf_map_new_fd(map, f_flags); if (err < 0) { /* failed to allocate fd. * bpf_map_put() is needed because the above @@ -490,6 +528,11 @@ static int map_lookup_elem(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_READ)) { + err = -EPERM; + goto err_put; + } + key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); @@ -570,6 +613,11 @@ static int map_update_elem(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_WRITE)) { + err = -EPERM; + goto err_put; + } + key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); @@ -659,6 +707,11 @@ static int map_delete_elem(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_WRITE)) { + err = -EPERM; + goto err_put; + } + key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); @@ -702,6 +755,11 @@ static int map_get_next_key(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_READ)) { + err = -EPERM; + goto err_put; + } + if (ukey) { key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { @@ -908,6 +966,8 @@ static const struct file_operations bpf_prog_fops = { .show_fdinfo = bpf_prog_show_fdinfo, #endif .release = bpf_prog_release, + .read = bpf_dummy_read, + .write = bpf_dummy_write, }; int bpf_prog_new_fd(struct bpf_prog *prog) @@ -1117,11 +1177,11 @@ static int bpf_prog_load(union bpf_attr *attr) return err; } -#define BPF_OBJ_LAST_FIELD bpf_fd +#define BPF_OBJ_LAST_FIELD file_flags static int bpf_obj_pin(const union bpf_attr *attr) { - if (CHECK_ATTR(BPF_OBJ)) + if (CHECK_ATTR(BPF_OBJ) || attr->file_flags != 0) return -EINVAL; return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname)); @@ -1129,10 +1189,12 @@ static int bpf_obj_pin(const union bpf_attr *attr) static int bpf_obj_get(const union bpf_attr *attr) { - if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0) + if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0 || + attr->file_flags & ~BPF_OBJ_FLAG_MASK) return -EINVAL; - return bpf_obj_get_user(u64_to_user_ptr(attr->pathname)); + return bpf_obj_get_user(u64_to_user_ptr(attr->pathname), + attr->file_flags); } #ifdef CONFIG_CGROUP_BPF @@ -1392,20 +1454,26 @@ static int bpf_prog_get_fd_by_id(const union bpf_attr *attr) return fd; } -#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD map_id +#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD open_flags static int bpf_map_get_fd_by_id(const union bpf_attr *attr) { struct bpf_map *map; u32 id = attr->map_id; + int f_flags; int fd; - if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID)) + if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID) || + attr->open_flags & ~BPF_OBJ_FLAG_MASK) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + f_flags = bpf_get_file_flag(attr->open_flags); + if (f_flags < 0) + return f_flags; + spin_lock_bh(&map_idr_lock); map = idr_find(&map_idr, id); if (map) @@ -1417,7 +1485,7 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); - fd = bpf_map_new_fd(map); + fd = bpf_map_new_fd(map, f_flags); if (fd < 0) bpf_map_put(map); diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c index 29123934887b..041da0d9c06f 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -56,7 +56,7 @@ static int __bpf_mt_check_path(const char *path, struct bpf_prog **ret) int retval, fd; set_fs(KERNEL_DS); - fd = bpf_obj_get_user(path); + fd = bpf_obj_get_user(path, 0); set_fs(oldfs); if (fd < 0) return fd; From patchwork Wed Oct 18 20:00:23 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chenbo Feng X-Patchwork-Id: 827799 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="tzWkBfYf"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHNH321GYz9sBd for ; Thu, 19 Oct 2017 07:01:19 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751618AbdJRUBP (ORCPT ); Wed, 18 Oct 2017 16:01:15 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:55665 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751498AbdJRUAs (ORCPT ); Wed, 18 Oct 2017 16:00:48 -0400 Received: by mail-io0-f193.google.com with SMTP id p186so7505118ioe.12; Wed, 18 Oct 2017 13:00:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AGlUi+Zy8nfIdSF7g7JVdXyyOtu/d7d6ElXhYKXxv9M=; b=tzWkBfYfC3nVw/ZIVef/MxB/eRAyD1q06N9TxwmD5gGkxT10Mzco6JOrl2+izqJbkU UDxfVV18reRTO3UMa/U074WgdxgGoOhHhpI79zt3CwhQHEi6jJe1zcVPRhL9sIj5FZn3 qzymmFsoX3G0gRQM5WgiLwfRs7THEgrHRtG/YFtModwX8uBoCuG8WaWY0Rrq/V22002w g/Pfl+VAIUbnBWDhrF7t2waZXfevXkpnXTVBG2jdwFhzxJmgOnfIh4NcuH2ChQk1kucJ 6gEsOeDjSl2kaMNoeuGrA7k5ymZ/mEO4u8rkeT7KP7LzTyA/u5e6ss/qcnVTrtpx9b8B 9ToQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AGlUi+Zy8nfIdSF7g7JVdXyyOtu/d7d6ElXhYKXxv9M=; b=Jb/9BC+R6Qs5uFBQs564hBjGZvoYYnZvDwgdTkZ8MfpRGyOX/FG9mw45MDLRgisRWr lXvNQcFt/fvnX/xtmGdfByrcZLSedgmQND2i5aqcanS1uy2hMy/bDoj2Mwcc7cbWYGoy Eb6dcWExGT6aetAkXh37KPSHenuUOPNLzlyaJd2vrQjB/1HAINv4Og8xzpbAgLVm5/yV dVue3sbj0ct8LfRxCbDzucOsNct0rWWST8qQ21pttRDCXQD5KUgTY3p3pDothcGmcviS XEVXLBVJKo82DCwuM8MZKOi31plonK0fULfzKZubMj43UyA43GQFPegak0RT6w25OSLq zOAA== X-Gm-Message-State: AMCzsaXUa0S0Qu6AM3Dm9sk1DOWVAFGRIEqh5x/2mJzjpHaMtSRhCQZW Du2ArrmQaP1H+InQSYMEOq+QWxy33gY= X-Google-Smtp-Source: ABhQp+T2O6EZSqas3nFBIW4MFxXrg+tuJWRnCJgyR4OVfWMNJbFTnKY/ubW7n7K2yShEGXaqR6ueGQ== X-Received: by 10.107.27.144 with SMTP id b138mr6629054iob.9.1508356847422; Wed, 18 Oct 2017 13:00:47 -0700 (PDT) Received: from fengc.mtv.corp.google.com ([100.98.120.50]) by smtp.gmail.com with ESMTPSA id 78sm3550047ioo.37.2017.10.18.13.00.46 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 18 Oct 2017 13:00:46 -0700 (PDT) From: Chenbo Feng To: netdev@vger.kernel.org, SELinux , linux-security-module@vger.kernel.org Cc: Jeffrey Vander Stoep , Alexei Starovoitov , lorenzo@google.com, Daniel Borkmann , Stephen Smalley , James Morris , Paul Moore , Chenbo Feng Subject: [PATCH net-next v7 2/5] bpf: Add tests for eBPF file mode Date: Wed, 18 Oct 2017 13:00:23 -0700 Message-Id: <20171018200026.146093-3-chenbofeng.kernel@gmail.com> X-Mailer: git-send-email 2.15.0.rc1.287.g2b38de12cc-goog In-Reply-To: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> References: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Chenbo Feng Two related tests are added into bpf selftest to test read only map and write only map. The tests verified the read only and write only flags are working on hash maps. Signed-off-by: Chenbo Feng Acked-by: Daniel Borkmann --- tools/testing/selftests/bpf/test_maps.c | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index fe3a443a1102..896f23cfe918 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -1033,6 +1033,51 @@ static void test_map_parallel(void) assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); } +static void test_map_rdonly(void) +{ + int i, fd, key = 0, value = 0; + + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + MAP_SIZE, map_flags | BPF_F_RDONLY); + if (fd < 0) { + printf("Failed to create map for read only test '%s'!\n", + strerror(errno)); + exit(1); + } + + key = 1; + value = 1234; + /* Insert key=1 element. */ + assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == -1 && + errno == EPERM); + + /* Check that key=2 is not found. */ + assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); + assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == ENOENT); +} + +static void test_map_wronly(void) +{ + int i, fd, key = 0, value = 0; + + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + MAP_SIZE, map_flags | BPF_F_WRONLY); + if (fd < 0) { + printf("Failed to create map for read only test '%s'!\n", + strerror(errno)); + exit(1); + } + + key = 1; + value = 1234; + /* Insert key=1 element. */ + assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0) + + /* Check that key=2 is not found. */ + assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == EPERM); + assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM); +} + static void run_all_tests(void) { test_hashmap(0, NULL); @@ -1050,6 +1095,9 @@ static void run_all_tests(void) test_map_large(); test_map_parallel(); test_map_stress(); + + test_map_rdonly(); + test_map_wronly(); } int main(void) From patchwork Wed Oct 18 20:00:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chenbo Feng X-Patchwork-Id: 827796 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="DHCNb0eM"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHNGZ1Xw8z9sBd for ; Thu, 19 Oct 2017 07:00:54 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751544AbdJRUAv (ORCPT ); Wed, 18 Oct 2017 16:00:51 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:45467 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751504AbdJRUAu (ORCPT ); Wed, 18 Oct 2017 16:00:50 -0400 Received: by mail-io0-f194.google.com with SMTP id i38so7528120iod.2; Wed, 18 Oct 2017 13:00:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=TL539HwD8RQbBtOYjwUdOH0fIAAR9rBDrngc3GkKcmw=; b=DHCNb0eMcmWmteJoEtuq9rf7x71/efUd06n3lp8ZS/ec90/LeTDvbEyQwBPrzONxTX uDEa8nMuuxz1MYmUCoVzieIWjAY/dLcvmY9wGbbqRkz32HHhI9AnAvdKFlaXZBo/COA0 evATHLE9gJZXoQx0AdCQHDtQxBGlWcmWMvBZ+Jq4ZIxwt3ef+s18t8peyOGwzu6kLAJe uYF7AHatnLHYZ1QAAgV7Rlx+qIWcbUzfZnGZ16XeUPiN11U3wWJ6Cwchjkq60fs4Zk4S EaRljopRkityBbjm0GuoBMWZ/Fv/aAfGe/W5HU9qJpbQG7cLbgwnEnMQiPf4pZkWzc3n 1EUA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=TL539HwD8RQbBtOYjwUdOH0fIAAR9rBDrngc3GkKcmw=; b=tkNMl72cRiMJjeLXWkuuH6CfQoUSWmuyiKTosA7dou0VkXHYSrWpPoXRYXkhIMdbmh FpbU2T8ZP5JmFKFwVF2RawOK7v/yd5BKdFrwP0HPb6U3fyCcb/1dH5fErCBZ3UfibYiF 06aZxS2Onkr7w8/cRoiSiAyfyq8if8WB4UWZmEXdacv/VAJAzQjzWWL7oeYkA+CSHU70 tM5kr8+NPNYkhGx87qovms3TSV8fAtbSGTLNNSggQgNsuL2owbkGeS3GPhwD5iReWTT9 5K00plBj941hQgWT7dVUBMU1m9VTeWrkFnp6lYR1XLxjA8ZL/2rxtXlqNluAXkRbeq5h 6xMw== X-Gm-Message-State: AMCzsaXBEE4H3VFwDdLz1wRGaKJ127aN/E53URbmrPhVpnjYvr5uG2MS I/k5VulzeMq3DTs4FaYBp8qWH5AvpUY= X-Google-Smtp-Source: ABhQp+SzRevZTYu12tzF5PaNiiDjugwyO0EeztzF8KLnIuhekYTgyKeJ9Y1bM3kppd7nGJlkBogsgg== X-Received: by 10.107.181.138 with SMTP id e132mr16919314iof.125.1508356849072; Wed, 18 Oct 2017 13:00:49 -0700 (PDT) Received: from fengc.mtv.corp.google.com ([100.98.120.50]) by smtp.gmail.com with ESMTPSA id 78sm3550047ioo.37.2017.10.18.13.00.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 18 Oct 2017 13:00:48 -0700 (PDT) From: Chenbo Feng To: netdev@vger.kernel.org, SELinux , linux-security-module@vger.kernel.org Cc: Jeffrey Vander Stoep , Alexei Starovoitov , lorenzo@google.com, Daniel Borkmann , Stephen Smalley , James Morris , Paul Moore , Chenbo Feng Subject: [PATCH net-next v7 3/5] security: bpf: Add LSM hooks for bpf object related syscall Date: Wed, 18 Oct 2017 13:00:24 -0700 Message-Id: <20171018200026.146093-4-chenbofeng.kernel@gmail.com> X-Mailer: git-send-email 2.15.0.rc1.287.g2b38de12cc-goog In-Reply-To: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> References: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Chenbo Feng Introduce several LSM hooks for the syscalls that will allow the userspace to access to eBPF object such as eBPF programs and eBPF maps. The security check is aimed to enforce a per object security protection for eBPF object so only processes with the right priviliges can read/write to a specific map or use a specific eBPF program. Besides that, a general security hook is added before the multiplexer of bpf syscall to check the cmd and the attribute used for the command. The actual security module can decide which command need to be checked and how the cmd should be checked. Signed-off-by: Chenbo Feng Acked-by: James Morris --- include/linux/bpf.h | 6 ++++++ include/linux/lsm_hooks.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/security.h | 45 +++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 34 +++++++++++++++++++++++++++-- security/security.c | 32 ++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3e5508f2fa87..84c192da3e0b 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -57,6 +57,9 @@ struct bpf_map { atomic_t usercnt; struct bpf_map *inner_map_meta; char name[BPF_OBJ_NAME_LEN]; +#ifdef CONFIG_SECURITY + void *security; +#endif }; /* function argument constraints */ @@ -193,6 +196,9 @@ struct bpf_prog_aux { struct user_struct *user; u64 load_time; /* ns since boottime */ char name[BPF_OBJ_NAME_LEN]; +#ifdef CONFIG_SECURITY + void *security; +#endif union { struct work_struct work; struct rcu_head rcu; diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index c9258124e417..7161d8e7ee79 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1351,6 +1351,40 @@ * @inode we wish to get the security context of. * @ctx is a pointer in which to place the allocated security context. * @ctxlen points to the place to put the length of @ctx. + * + * Security hooks for using the eBPF maps and programs functionalities through + * eBPF syscalls. + * + * @bpf: + * Do a initial check for all bpf syscalls after the attribute is copied + * into the kernel. The actual security module can implement their own + * rules to check the specific cmd they need. + * + * @bpf_map: + * Do a check when the kernel generate and return a file descriptor for + * eBPF maps. + * + * @map: bpf map that we want to access + * @mask: the access flags + * + * @bpf_prog: + * Do a check when the kernel generate and return a file descriptor for + * eBPF programs. + * + * @prog: bpf prog that userspace want to use. + * + * @bpf_map_alloc_security: + * Initialize the security field inside bpf map. + * + * @bpf_map_free_security: + * Clean up the security information stored inside bpf map. + * + * @bpf_prog_alloc_security: + * Initialize the security field inside bpf program. + * + * @bpf_prog_free_security: + * Clean up the security information stored inside bpf prog. + * */ union security_list_options { int (*binder_set_context_mgr)(struct task_struct *mgr); @@ -1682,6 +1716,17 @@ union security_list_options { struct audit_context *actx); void (*audit_rule_free)(void *lsmrule); #endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_BPF_SYSCALL + int (*bpf)(int cmd, union bpf_attr *attr, + unsigned int size); + int (*bpf_map)(struct bpf_map *map, fmode_t fmode); + int (*bpf_prog)(struct bpf_prog *prog); + int (*bpf_map_alloc_security)(struct bpf_map *map); + void (*bpf_map_free_security)(struct bpf_map *map); + int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux); + void (*bpf_prog_free_security)(struct bpf_prog_aux *aux); +#endif /* CONFIG_BPF_SYSCALL */ }; struct security_hook_heads { @@ -1901,6 +1946,15 @@ struct security_hook_heads { struct list_head audit_rule_match; struct list_head audit_rule_free; #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_BPF_SYSCALL + struct list_head bpf; + struct list_head bpf_map; + struct list_head bpf_prog; + struct list_head bpf_map_alloc_security; + struct list_head bpf_map_free_security; + struct list_head bpf_prog_alloc_security; + struct list_head bpf_prog_free_security; +#endif /* CONFIG_BPF_SYSCALL */ } __randomize_layout; /* diff --git a/include/linux/security.h b/include/linux/security.h index ce6265960d6c..18800b0911e5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -31,6 +31,7 @@ #include #include #include +#include struct linux_binprm; struct cred; @@ -1730,6 +1731,50 @@ static inline void securityfs_remove(struct dentry *dentry) #endif +#ifdef CONFIG_BPF_SYSCALL +#ifdef CONFIG_SECURITY +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_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); +extern void security_bpf_prog_free(struct bpf_prog_aux *aux); +#else +static inline int security_bpf(int cmd, union bpf_attr *attr, + unsigned int size) +{ + return 0; +} + +static inline int security_bpf_map(struct bpf_map *map, fmode_t fmode) +{ + return 0; +} + +static inline int security_bpf_prog(struct bpf_prog *prog) +{ + return 0; +} + +static inline int security_bpf_map_alloc(struct bpf_map *map) +{ + return 0; +} + +static inline void security_bpf_map_free(struct bpf_map *map) +{ } + +static inline int security_bpf_prog_alloc(struct bpf_prog_aux *aux) +{ + return 0; +} + +static inline void security_bpf_prog_free(struct bpf_prog_aux *aux) +{ } +#endif /* CONFIG_SECURITY */ +#endif /* CONFIG_BPF_SYSCALL */ + #ifdef CONFIG_SECURITY static inline char *alloc_secdata(void) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 676a06e6b322..5cb56d06b48d 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -212,6 +212,7 @@ static void bpf_map_free_deferred(struct work_struct *work) struct bpf_map *map = container_of(work, struct bpf_map, work); bpf_map_uncharge_memlock(map); + security_bpf_map_free(map); /* implementation dependent freeing */ map->ops->map_free(map); } @@ -325,6 +326,12 @@ static const struct file_operations bpf_map_fops = { int bpf_map_new_fd(struct bpf_map *map, int flags) { + int ret; + + ret = security_bpf_map(map, OPEN_FMODE(flags)); + if (ret < 0) + return ret; + return anon_inode_getfd("bpf-map", &bpf_map_fops, map, flags | O_CLOEXEC); } @@ -405,10 +412,14 @@ static int map_create(union bpf_attr *attr) atomic_set(&map->refcnt, 1); atomic_set(&map->usercnt, 1); - err = bpf_map_charge_memlock(map); + err = security_bpf_map_alloc(map); if (err) goto free_map_nouncharge; + err = bpf_map_charge_memlock(map); + if (err) + goto free_map_sec; + err = bpf_map_alloc_id(map); if (err) goto free_map; @@ -430,6 +441,8 @@ static int map_create(union bpf_attr *attr) free_map: bpf_map_uncharge_memlock(map); +free_map_sec: + security_bpf_map_free(map); free_map_nouncharge: map->ops->map_free(map); return err; @@ -914,6 +927,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu) free_used_maps(aux); bpf_prog_uncharge_memlock(aux->prog); + security_bpf_prog_free(aux); bpf_prog_free(aux->prog); } @@ -972,6 +986,12 @@ static const struct file_operations bpf_prog_fops = { int bpf_prog_new_fd(struct bpf_prog *prog) { + int ret; + + ret = security_bpf_prog(prog); + if (ret < 0) + return ret; + return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog, O_RDWR | O_CLOEXEC); } @@ -1111,10 +1131,14 @@ static int bpf_prog_load(union bpf_attr *attr) if (!prog) return -ENOMEM; - err = bpf_prog_charge_memlock(prog); + err = security_bpf_prog_alloc(prog->aux); if (err) goto free_prog_nouncharge; + err = bpf_prog_charge_memlock(prog); + if (err) + goto free_prog_sec; + prog->len = attr->insn_cnt; err = -EFAULT; @@ -1172,6 +1196,8 @@ static int bpf_prog_load(union bpf_attr *attr) free_used_maps(prog->aux); free_prog: bpf_prog_uncharge_memlock(prog); +free_prog_sec: + security_bpf_prog_free(prog->aux); free_prog_nouncharge: bpf_prog_free(prog); return err; @@ -1640,6 +1666,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz if (copy_from_user(&attr, uattr, size) != 0) return -EFAULT; + err = security_bpf(cmd, &attr, size); + if (err < 0) + return err; + switch (cmd) { case BPF_MAP_CREATE: err = map_create(&attr); diff --git a/security/security.c b/security/security.c index 4bf0f571b4ef..1cd8526cb0b7 100644 --- a/security/security.c +++ b/security/security.c @@ -12,6 +12,7 @@ * (at your option) any later version. */ +#include #include #include #include @@ -1703,3 +1704,34 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, actx); } #endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_BPF_SYSCALL +int security_bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ + return call_int_hook(bpf, 0, cmd, attr, size); +} +int security_bpf_map(struct bpf_map *map, fmode_t fmode) +{ + return call_int_hook(bpf_map, 0, map, fmode); +} +int security_bpf_prog(struct bpf_prog *prog) +{ + return call_int_hook(bpf_prog, 0, prog); +} +int security_bpf_map_alloc(struct bpf_map *map) +{ + return call_int_hook(bpf_map_alloc_security, 0, map); +} +int security_bpf_prog_alloc(struct bpf_prog_aux *aux) +{ + return call_int_hook(bpf_prog_alloc_security, 0, aux); +} +void security_bpf_map_free(struct bpf_map *map) +{ + call_void_hook(bpf_map_free_security, map); +} +void security_bpf_prog_free(struct bpf_prog_aux *aux) +{ + call_void_hook(bpf_prog_free_security, aux); +} +#endif /* CONFIG_BPF_SYSCALL */ From patchwork Wed Oct 18 20:00:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chenbo Feng X-Patchwork-Id: 827797 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qVMMCFkg"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHNGh2sZlz9sBd for ; Thu, 19 Oct 2017 07:01:00 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751577AbdJRUA6 (ORCPT ); Wed, 18 Oct 2017 16:00:58 -0400 Received: from mail-io0-f196.google.com ([209.85.223.196]:55671 "EHLO mail-io0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751504AbdJRUAw (ORCPT ); Wed, 18 Oct 2017 16:00:52 -0400 Received: by mail-io0-f196.google.com with SMTP id p186so7505377ioe.12; Wed, 18 Oct 2017 13:00:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=xGmstFmirsJRkbpE+6/KzifbhLe8ppo41yh8uS/SIIY=; b=qVMMCFkgLBTLqOv3wj4sCjhDsgv9Q1BelPGVfzQUg1WrOrt9+WwxOqhBOd1OD9DWLa E0zvcyyI6CJKlHX5VZYyonGBzdVaAEEIO4h5XBAbIeV9IDYV7NiuRjHsdZPfBzAQEinH YEKyNMoAi/4DkQRPXioXPKPmCTfxcyXIbrBl1dLhz/s1yPqeQh85dhaWjhoergoq6enR 6BgUNvaGborJbK7HfJSG7OpHuH85pP1V5UUJD9hed/YfUi5gKDoQrRijvRCvAD25e74K H3Qa8R3c4gwy3oWhurxV38g9RWNGqHjAzKWHr+KXQwE9W22zg1f1WYvtUvDqjgVim9OQ HSGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=xGmstFmirsJRkbpE+6/KzifbhLe8ppo41yh8uS/SIIY=; b=Phjb4Sn+HI9x3ZqP4bcVi24fXZSoC+ews2TWAH43D6IK31gVjZED2iLUx3/WmG2JzQ iIItG1Yun+Ayjai59D9HCIz1FpUU5LDoghBRZTxhLC+/i+KFKXURzP2izu3LoOvU/Y0G cvd5R8faWgFTR+FHU4d3BXoKQ93QSn47JYrNoFv6fqVWExU06L/n1/pc1/I8pdkOygFn Ek4Z7N0IA5U5WqyE+48O4JTDvR3Th6OaK9yYrBwEcxUA6m4AyyIocIFd4Q5y/cwVj6QU GPtE4k4PdrAZCjEZkB3SOf0tMGOPGzHFlTAJ2gfhUHm/q1i41C4Xki7khse5FZU61gor nVHw== X-Gm-Message-State: AMCzsaVgM/r+5JbQQ7764WanDA1dcGXCVBtulNM4egMsVXOkcAxVROIE s/CVYFYh7L9S0Wx6NsLPI0hTK/ZZ/tg= X-Google-Smtp-Source: AOwi7QAtMSVJGMMno2AlqhA5KfFkb8CjZtRZaodsOxRMn1LXG+3C+36839u2Kx+1AC9N1UP91nQYYA== X-Received: by 10.107.42.10 with SMTP id q10mr24563526ioq.196.1508356851378; Wed, 18 Oct 2017 13:00:51 -0700 (PDT) Received: from fengc.mtv.corp.google.com ([100.98.120.50]) by smtp.gmail.com with ESMTPSA id 78sm3550047ioo.37.2017.10.18.13.00.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 18 Oct 2017 13:00:50 -0700 (PDT) From: Chenbo Feng To: netdev@vger.kernel.org, SELinux , linux-security-module@vger.kernel.org Cc: Jeffrey Vander Stoep , Alexei Starovoitov , lorenzo@google.com, Daniel Borkmann , Stephen Smalley , James Morris , Paul Moore , Chenbo Feng Subject: [PATCH net-next v7 4/5] selinux: bpf: Add selinux check for eBPF syscall operations Date: Wed, 18 Oct 2017 13:00:25 -0700 Message-Id: <20171018200026.146093-5-chenbofeng.kernel@gmail.com> X-Mailer: git-send-email 2.15.0.rc1.287.g2b38de12cc-goog In-Reply-To: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> References: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Chenbo Feng Implement the actual checks introduced to eBPF related syscalls. This implementation use the security field inside bpf object to store a sid that identify the bpf object. And when processes try to access the object, selinux will check if processes have the right privileges. The creation of eBPF object are also checked at the general bpf check hook and new cmd introduced to eBPF domain can also be checked there. Signed-off-by: Chenbo Feng Acked-by: Alexei Starovoitov Reviewed-by: James Morris Acked-by: Paul Moore --- security/selinux/hooks.c | 111 ++++++++++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 + security/selinux/include/objsec.h | 4 ++ 3 files changed, 117 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f5d304736852..12cf7de8cbed 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -85,6 +85,7 @@ #include #include #include +#include #include "avc.h" #include "objsec.h" @@ -6252,6 +6253,106 @@ static void selinux_ib_free_security(void *ib_sec) } #endif +#ifdef CONFIG_BPF_SYSCALL +static int selinux_bpf(int cmd, union bpf_attr *attr, + unsigned int size) +{ + u32 sid = current_sid(); + int ret; + + switch (cmd) { + case BPF_MAP_CREATE: + ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE, + NULL); + break; + case BPF_PROG_LOAD: + ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, + NULL); + break; + default: + ret = 0; + break; + } + + return ret; +} + +static u32 bpf_map_fmode_to_av(fmode_t fmode) +{ + u32 av = 0; + + if (fmode & FMODE_READ) + av |= BPF__MAP_READ; + if (fmode & FMODE_WRITE) + av |= BPF__MAP_WRITE; + return av; +} + +static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode) +{ + u32 sid = current_sid(); + struct bpf_security_struct *bpfsec; + + bpfsec = map->security; + return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + bpf_map_fmode_to_av(fmode), NULL); +} + +static int selinux_bpf_prog(struct bpf_prog *prog) +{ + u32 sid = current_sid(); + struct bpf_security_struct *bpfsec; + + bpfsec = prog->aux->security; + return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + BPF__PROG_RUN, NULL); +} + +static int selinux_bpf_map_alloc(struct bpf_map *map) +{ + struct bpf_security_struct *bpfsec; + + bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL); + if (!bpfsec) + return -ENOMEM; + + bpfsec->sid = current_sid(); + map->security = bpfsec; + + return 0; +} + +static void selinux_bpf_map_free(struct bpf_map *map) +{ + struct bpf_security_struct *bpfsec = map->security; + + map->security = NULL; + kfree(bpfsec); +} + +static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux) +{ + struct bpf_security_struct *bpfsec; + + bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL); + if (!bpfsec) + return -ENOMEM; + + bpfsec->sid = current_sid(); + aux->security = bpfsec; + + return 0; +} + +static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) +{ + struct bpf_security_struct *bpfsec = aux->security; + + aux->security = NULL; + kfree(bpfsec); +} +#endif + static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr), LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction), @@ -6471,6 +6572,16 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match), LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free), #endif + +#ifdef CONFIG_BPF_SYSCALL + LSM_HOOK_INIT(bpf, selinux_bpf), + LSM_HOOK_INIT(bpf_map, selinux_bpf_map), + LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog), + LSM_HOOK_INIT(bpf_map_alloc_security, selinux_bpf_map_alloc), + LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc), + LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free), + LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free), +#endif }; static __init int selinux_init(void) diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 35ffb29a69cb..0a7023b5f000 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -237,6 +237,8 @@ struct security_class_mapping secclass_map[] = { { "access", NULL } }, { "infiniband_endport", { "manage_subnet", NULL } }, + { "bpf", + {"map_create", "map_read", "map_write", "prog_load", "prog_run"} }, { NULL } }; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 1649cd18eb0b..3d54468ce334 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -150,6 +150,10 @@ struct pkey_security_struct { u32 sid; /* SID of pkey */ }; +struct bpf_security_struct { + u32 sid; /*SID of bpf obj creater*/ +}; + extern unsigned int selinux_checkreqprot; #endif /* _SELINUX_OBJSEC_H_ */ From patchwork Wed Oct 18 20:00:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chenbo Feng X-Patchwork-Id: 827798 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Mp3yOe+l"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHNGr6CnDz9t48 for ; Thu, 19 Oct 2017 07:01:08 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751608AbdJRUBG (ORCPT ); Wed, 18 Oct 2017 16:01:06 -0400 Received: from mail-it0-f68.google.com ([209.85.214.68]:47106 "EHLO mail-it0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751555AbdJRUAy (ORCPT ); Wed, 18 Oct 2017 16:00:54 -0400 Received: by mail-it0-f68.google.com with SMTP id p138so7732139itp.2; Wed, 18 Oct 2017 13:00:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=LoV90sjoawQiQnUbdCpfe29Sbzn9PbeTbVVFpcaxPz0=; b=Mp3yOe+lOXNZXjNF9K4IcEq2whDFtXxl5sCGARMghrJvQ9Sj7lq61tSS24xAS6ZUb/ Eh/IsSMTbsSjpIWOTuTzGl8PKvnrgh6u1DjRngjkwHy1MJBB+Z6ymzlK9O2MbpaSSJBV ANC80gtLOQUbeaxpTbPdnah8fa2V4dmQ2B1+n7psaSxWyJQlo3gJRAiPudA1dOIduFdW qShpjF1vdSZ26fNB605E2RNXteGXazgc2pHk9kLUUDwH8ktBF1u4ryy/PwQ4GhkoHzPR MSGxgRC7g3znQc3cq0qRCSdpyt+Oavw3zvr7zpZixOSwQu/sCuoHintXjiTDBQbUPWEt hd2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=LoV90sjoawQiQnUbdCpfe29Sbzn9PbeTbVVFpcaxPz0=; b=chyWQnNhscuB5V0E0UN74FkhL39VoIzKANlTVjMNCzkKbRHobYRVUFCcvTuXQML6Na 8EHAKBWyenxaZHER8xdKp2Q/TqtkB3F1NNfb+0AR0uDu/EfM/CsYv3WgiDshhPRbHdkj P5037lvS+TGS9oyYMa+Uph6hON2/Iu6crcK8Q9ulhKvASxq4W9BHNyFnwOXU7hpfv4GK f74pNvFPn7t7y3aJPewa1UBCoAAUHb8eOxf4hSmTHo5RzNrVnXcgj7Cnnxip2fjMZ8YE BMW+kTjgZ/Ym8Q1ddHWibp5bhWx0jA3dYtS+90cM+62lil6WAbOoUb5A44hAosxGzZ0i Mwug== X-Gm-Message-State: AMCzsaWVFNpPAFx12FrbFnyxqL4thE30D41sEgnqRa5Nn8PtV0lvAxsQ Umn7g3IzQ6tXIbTFbkXJSmVkieiZ1Gk= X-Google-Smtp-Source: ABhQp+SZCLRfkQhRVjlMjzGq84z3daarUL0NofeOJt6LAalDfoFUIA3myUFS/McNm/NVgfztgJW++g== X-Received: by 10.36.44.200 with SMTP id i191mr5843543iti.120.1508356853013; Wed, 18 Oct 2017 13:00:53 -0700 (PDT) Received: from fengc.mtv.corp.google.com ([100.98.120.50]) by smtp.gmail.com with ESMTPSA id 78sm3550047ioo.37.2017.10.18.13.00.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 18 Oct 2017 13:00:52 -0700 (PDT) From: Chenbo Feng To: netdev@vger.kernel.org, SELinux , linux-security-module@vger.kernel.org Cc: Jeffrey Vander Stoep , Alexei Starovoitov , lorenzo@google.com, Daniel Borkmann , Stephen Smalley , James Morris , Paul Moore , Chenbo Feng Subject: [PATCH net-next v7 5/5] selinux: bpf: Add addtional check for bpf object file receive Date: Wed, 18 Oct 2017 13:00:26 -0700 Message-Id: <20171018200026.146093-6-chenbofeng.kernel@gmail.com> X-Mailer: git-send-email 2.15.0.rc1.287.g2b38de12cc-goog In-Reply-To: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> References: <20171018200026.146093-1-chenbofeng.kernel@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Chenbo Feng Introduce a bpf object related check when sending and receiving files through unix domain socket as well as binder. It checks if the receiving process have privilege to read/write the bpf map or use the bpf program. This check is necessary because the bpf maps and programs are using a anonymous inode as their shared inode so the normal way of checking the files and sockets when passing between processes cannot work properly on eBPF object. This check only works when the BPF_SYSCALL is configured. Signed-off-by: Chenbo Feng Acked-by: Stephen Smalley Reviewed-by: James Morris Acked-by: Paul Moore --- include/linux/bpf.h | 3 +++ kernel/bpf/syscall.c | 4 ++-- security/selinux/hooks.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 84c192da3e0b..1e334b248ff6 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -288,6 +288,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); +extern const struct file_operations bpf_map_fops; +extern const struct file_operations bpf_prog_fops; + #define BPF_PROG_TYPE(_id, _name) \ extern const struct bpf_prog_ops _name ## _prog_ops; \ extern const struct bpf_verifier_ops _name ## _verifier_ops; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 5cb56d06b48d..323be2473c4b 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -315,7 +315,7 @@ static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf, return -EINVAL; } -static const struct file_operations bpf_map_fops = { +const struct file_operations bpf_map_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_map_show_fdinfo, #endif @@ -975,7 +975,7 @@ static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp) } #endif -static const struct file_operations bpf_prog_fops = { +const struct file_operations bpf_prog_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_prog_show_fdinfo, #endif diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 12cf7de8cbed..2e3a627fc0b1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const struct cred *cred, return inode_has_perm(cred, file_inode(file), av, &ad); } +#ifdef CONFIG_BPF_SYSCALL +static int bpf_fd_pass(struct file *file, u32 sid); +#endif + /* Check whether a task can use an open file descriptor to access an inode in a given way. Check access to the descriptor itself, and then use dentry_has_perm to @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred *cred, goto out; } +#ifdef CONFIG_BPF_SYSCALL + rc = bpf_fd_pass(file, cred_sid(cred)); + if (rc) + return rc; +#endif + /* av is zero if only checking access to the descriptor. */ rc = 0; if (av) @@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct task_struct *from, return rc; } +#ifdef CONFIG_BPF_SYSCALL + rc = bpf_fd_pass(file, sid); + if (rc) + return rc; +#endif + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; @@ -6288,6 +6304,39 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode) return av; } +/* This function will check the file pass through unix socket or binder to see + * if it is a bpf related object. And apply correspinding checks on the bpf + * object based on the type. The bpf maps and programs, not like other files and + * socket, are using a shared anonymous inode inside the kernel as their inode. + * So checking that inode cannot identify if the process have privilege to + * access the bpf object and that's why we have to add this additional check in + * selinux_file_receive and selinux_binder_transfer_files. + */ +static int bpf_fd_pass(struct file *file, u32 sid) +{ + struct bpf_security_struct *bpfsec; + struct bpf_prog *prog; + struct bpf_map *map; + int ret; + + if (file->f_op == &bpf_map_fops) { + map = file->private_data; + bpfsec = map->security; + ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + bpf_map_fmode_to_av(file->f_mode), NULL); + if (ret) + return ret; + } else if (file->f_op == &bpf_prog_fops) { + prog = file->private_data; + bpfsec = prog->aux->security; + ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + BPF__PROG_RUN, NULL); + if (ret) + return ret; + } + return 0; +} + static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode) { u32 sid = current_sid();