diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/device_cgroup.c | 91 | ||||
-rw-r--r-- | security/security.c | 32 | ||||
-rw-r--r-- | security/selinux/hooks.c | 160 | ||||
-rw-r--r-- | security/selinux/include/classmap.h | 2 | ||||
-rw-r--r-- | security/selinux/include/objsec.h | 4 |
5 files changed, 222 insertions, 67 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 5ef7e5240563..c65b39bafdfe 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -15,15 +15,6 @@ #include <linux/rcupdate.h> #include <linux/mutex.h> -#define ACC_MKNOD 1 -#define ACC_READ 2 -#define ACC_WRITE 4 -#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE) - -#define DEV_BLOCK 1 -#define DEV_CHAR 2 -#define DEV_ALL 4 /* this represents all devices */ - static DEFINE_MUTEX(devcgroup_mutex); enum devcg_behavior { @@ -246,21 +237,21 @@ static void set_access(char *acc, short access) { int idx = 0; memset(acc, 0, ACCLEN); - if (access & ACC_READ) + if (access & DEVCG_ACC_READ) acc[idx++] = 'r'; - if (access & ACC_WRITE) + if (access & DEVCG_ACC_WRITE) acc[idx++] = 'w'; - if (access & ACC_MKNOD) + if (access & DEVCG_ACC_MKNOD) acc[idx++] = 'm'; } static char type_to_char(short type) { - if (type == DEV_ALL) + if (type == DEVCG_DEV_ALL) return 'a'; - if (type == DEV_CHAR) + if (type == DEVCG_DEV_CHAR) return 'c'; - if (type == DEV_BLOCK) + if (type == DEVCG_DEV_BLOCK) return 'b'; return 'X'; } @@ -287,10 +278,10 @@ static int devcgroup_seq_show(struct seq_file *m, void *v) * This way, the file remains as a "whitelist of devices" */ if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { - set_access(acc, ACC_MASK); + set_access(acc, DEVCG_ACC_MASK); set_majmin(maj, ~0); set_majmin(min, ~0); - seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL), + seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL), maj, min, acc); } else { list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) { @@ -309,10 +300,10 @@ static int devcgroup_seq_show(struct seq_file *m, void *v) /** * match_exception - iterates the exception list trying to find a complete match * @exceptions: list of exceptions - * @type: device type (DEV_BLOCK or DEV_CHAR) + * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR) * @major: device file major number, ~0 to match all * @minor: device file minor number, ~0 to match all - * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD) + * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD) * * It is considered a complete match if an exception is found that will * contain the entire range of provided parameters. @@ -325,9 +316,9 @@ static bool match_exception(struct list_head *exceptions, short type, struct dev_exception_item *ex; list_for_each_entry_rcu(ex, exceptions, list) { - if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) + if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK)) continue; - if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR)) + if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR)) continue; if (ex->major != ~0 && ex->major != major) continue; @@ -344,10 +335,10 @@ static bool match_exception(struct list_head *exceptions, short type, /** * match_exception_partial - iterates the exception list trying to find a partial match * @exceptions: list of exceptions - * @type: device type (DEV_BLOCK or DEV_CHAR) + * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR) * @major: device file major number, ~0 to match all * @minor: device file minor number, ~0 to match all - * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD) + * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD) * * It is considered a partial match if an exception's range is found to * contain *any* of the devices specified by provided parameters. This is @@ -362,9 +353,9 @@ static bool match_exception_partial(struct list_head *exceptions, short type, struct dev_exception_item *ex; list_for_each_entry_rcu(ex, exceptions, list) { - if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) + if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK)) continue; - if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR)) + if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR)) continue; /* * We must be sure that both the exception and the provided @@ -647,10 +638,10 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, } return 0; case 'b': - ex.type = DEV_BLOCK; + ex.type = DEVCG_DEV_BLOCK; break; case 'c': - ex.type = DEV_CHAR; + ex.type = DEVCG_DEV_CHAR; break; default: return -EINVAL; @@ -703,13 +694,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, for (b++, count = 0; count < 3; count++, b++) { switch (*b) { case 'r': - ex.access |= ACC_READ; + ex.access |= DEVCG_ACC_READ; break; case 'w': - ex.access |= ACC_WRITE; + ex.access |= DEVCG_ACC_WRITE; break; case 'm': - ex.access |= ACC_MKNOD; + ex.access |= DEVCG_ACC_MKNOD; break; case '\n': case '\0': @@ -806,12 +797,12 @@ struct cgroup_subsys devices_cgrp_subsys = { * @type: device type * @major: device major number * @minor: device minor number - * @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD + * @access: combination of DEVCG_ACC_WRITE, DEVCG_ACC_READ and DEVCG_ACC_MKNOD * * returns 0 on success, -EPERM case the operation is not permitted */ -static int __devcgroup_check_permission(short type, u32 major, u32 minor, - short access) +int __devcgroup_check_permission(short type, u32 major, u32 minor, + short access) { struct dev_cgroup *dev_cgroup; bool rc; @@ -833,37 +824,3 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor, return 0; } - -int __devcgroup_inode_permission(struct inode *inode, int mask) -{ - short type, access = 0; - - if (S_ISBLK(inode->i_mode)) - type = DEV_BLOCK; - if (S_ISCHR(inode->i_mode)) - type = DEV_CHAR; - if (mask & MAY_WRITE) - access |= ACC_WRITE; - if (mask & MAY_READ) - access |= ACC_READ; - - return __devcgroup_check_permission(type, imajor(inode), iminor(inode), - access); -} - -int devcgroup_inode_mknod(int mode, dev_t dev) -{ - short type; - - if (!S_ISBLK(mode) && !S_ISCHR(mode)) - return 0; - - if (S_ISBLK(mode)) - type = DEV_BLOCK; - else - type = DEV_CHAR; - - return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev), - ACC_MKNOD); - -} 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 <linux/bpf.h> #include <linux/capability.h> #include <linux/dcache.h> #include <linux/module.h> @@ -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 */ diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f5d304736852..2e3a627fc0b1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -85,6 +85,7 @@ #include <linux/export.h> #include <linux/msg.h> #include <linux/shm.h> +#include <linux/bpf.h> #include "avc.h" #include "objsec.h" @@ -1814,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 @@ -1844,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) @@ -2164,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; @@ -6252,6 +6269,139 @@ 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; +} + +/* 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(); + 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 +6621,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 cc35695d97b4..acdee7795297 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -238,6 +238,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_ */ |