diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-27 13:42:11 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-27 13:42:11 -0700 |
commit | f1c921fb70de06c7eda59104470134aecc7a07c4 (patch) | |
tree | 9e4bc137ef8396b8cdb64d7380e3be26d3fbf8b0 /security/selinux | |
parent | fafe1e39ed213221c0bce6b0b31669334368dc97 (diff) | |
parent | e4c82eafb609c2badc56f4e11bc50fcf44b8e9eb (diff) | |
download | linux-f1c921fb70de06c7eda59104470134aecc7a07c4.tar.gz linux-f1c921fb70de06c7eda59104470134aecc7a07c4.tar.bz2 linux-f1c921fb70de06c7eda59104470134aecc7a07c4.zip |
Merge tag 'selinux-pr-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux updates from Paul Moore:
- Add support for measuring the SELinux state and policy capabilities
using IMA.
- A handful of SELinux/NFS patches to compare the SELinux state of one
mount with a set of mount options. Olga goes into more detail in the
patch descriptions, but this is important as it allows more
flexibility when using NFS and SELinux context mounts.
- Properly differentiate between the subjective and objective LSM
credentials; including support for the SELinux and Smack. My clumsy
attempt at a proper fix for AppArmor didn't quite pass muster so John
is working on a proper AppArmor patch, in the meantime this set of
patches shouldn't change the behavior of AppArmor in any way. This
change explains the bulk of the diffstat beyond security/.
- Fix a problem where we were not properly terminating the permission
list for two SELinux object classes.
* tag 'selinux-pr-20210426' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
selinux: add proper NULL termination to the secclass_map permissions
smack: differentiate between subjective and objective task credentials
selinux: clarify task subjective and objective credentials
lsm: separate security_task_getsecid() into subjective and objective variants
nfs: account for selinux security context when deciding to share superblock
nfs: remove unneeded null check in nfs_fill_super()
lsm,selinux: add new hook to compare new mount to an existing mount
selinux: fix misspellings using codespell tool
selinux: fix misspellings using codespell tool
selinux: measure state and policy capabilities
selinux: Allow context mounts for unpriviliged overlayfs
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/hooks.c | 170 | ||||
-rw-r--r-- | security/selinux/ima.c | 87 | ||||
-rw-r--r-- | security/selinux/include/classmap.h | 5 | ||||
-rw-r--r-- | security/selinux/include/ima.h | 6 | ||||
-rw-r--r-- | security/selinux/include/security.h | 2 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 6 | ||||
-rw-r--r-- | security/selinux/ss/hashtab.c | 2 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 2 |
8 files changed, 232 insertions, 48 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ddd097790d47..92f909a2e8f7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -230,9 +230,22 @@ static inline u32 cred_sid(const struct cred *cred) } /* + * get the subjective security ID of a task + */ +static inline u32 task_sid_subj(const struct task_struct *task) +{ + u32 sid; + + rcu_read_lock(); + sid = cred_sid(rcu_dereference(task->cred)); + rcu_read_unlock(); + return sid; +} + +/* * get the objective security ID of a task */ -static inline u32 task_sid(const struct task_struct *task) +static inline u32 task_sid_obj(const struct task_struct *task) { u32 sid; @@ -242,6 +255,29 @@ static inline u32 task_sid(const struct task_struct *task) return sid; } +/* + * get the security ID of a task for use with binder + */ +static inline u32 task_sid_binder(const struct task_struct *task) +{ + /* + * In many case where this function is used we should be using the + * task's subjective SID, but we can't reliably access the subjective + * creds of a task other than our own so we must use the objective + * creds/SID, which are safe to access. The downside is that if a task + * is temporarily overriding it's creds it will not be reflected here; + * however, it isn't clear that binder would handle that case well + * anyway. + * + * If this ever changes and we can safely reference the subjective + * creds/SID of another task, this function will make it easier to + * identify the various places where we make use of the task SIDs in + * the binder code. It is also likely that we will need to adjust + * the main drivers/android binder code as well. + */ + return task_sid_obj(task); +} + static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); /* @@ -760,7 +796,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (sb->s_user_ns != &init_user_ns && strcmp(sb->s_type->name, "tmpfs") && strcmp(sb->s_type->name, "ramfs") && - strcmp(sb->s_type->name, "devpts")) { + strcmp(sb->s_type->name, "devpts") && + strcmp(sb->s_type->name, "overlay")) { if (context_sid || fscontext_sid || rootcontext_sid || defcontext_sid) { rc = -EACCES; @@ -2034,11 +2071,8 @@ static inline u32 open_file_to_av(struct file *file) static int selinux_binder_set_context_mgr(struct task_struct *mgr) { - u32 mysid = current_sid(); - u32 mgrsid = task_sid(mgr); - return avc_has_perm(&selinux_state, - mysid, mgrsid, SECCLASS_BINDER, + current_sid(), task_sid_binder(mgr), SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); } @@ -2046,8 +2080,7 @@ static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to) { u32 mysid = current_sid(); - u32 fromsid = task_sid(from); - u32 tosid = task_sid(to); + u32 fromsid = task_sid_binder(from); int rc; if (mysid != fromsid) { @@ -2058,19 +2091,16 @@ static int selinux_binder_transaction(struct task_struct *from, return rc; } - return avc_has_perm(&selinux_state, - fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, - NULL); + return avc_has_perm(&selinux_state, fromsid, task_sid_binder(to), + SECCLASS_BINDER, BINDER__CALL, NULL); } static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to) { - u32 fromsid = task_sid(from); - u32 tosid = task_sid(to); - return avc_has_perm(&selinux_state, - fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, + task_sid_binder(from), task_sid_binder(to), + SECCLASS_BINDER, BINDER__TRANSFER, NULL); } @@ -2078,7 +2108,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) { - u32 sid = task_sid(to); + u32 sid = task_sid_binder(to); struct file_security_struct *fsec = selinux_file(file); struct dentry *dentry = file->f_path.dentry; struct inode_security_struct *isec; @@ -2114,10 +2144,10 @@ static int selinux_binder_transfer_file(struct task_struct *from, } static int selinux_ptrace_access_check(struct task_struct *child, - unsigned int mode) + unsigned int mode) { u32 sid = current_sid(); - u32 csid = task_sid(child); + u32 csid = task_sid_obj(child); if (mode & PTRACE_MODE_READ) return avc_has_perm(&selinux_state, @@ -2130,15 +2160,15 @@ static int selinux_ptrace_access_check(struct task_struct *child, static int selinux_ptrace_traceme(struct task_struct *parent) { return avc_has_perm(&selinux_state, - task_sid(parent), current_sid(), SECCLASS_PROCESS, - PROCESS__PTRACE, NULL); + task_sid_subj(parent), task_sid_obj(current), + SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(target), SECCLASS_PROCESS, + current_sid(), task_sid_obj(target), SECCLASS_PROCESS, PROCESS__GETCAP, NULL); } @@ -2263,7 +2293,7 @@ static u32 ptrace_parent_sid(void) rcu_read_lock(); tracer = ptrace_parent(current); if (tracer) - sid = task_sid(tracer); + sid = task_sid_obj(tracer); rcu_read_unlock(); return sid; @@ -2684,6 +2714,61 @@ free_opt: return rc; } +static int selinux_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts) +{ + struct selinux_mnt_opts *opts = mnt_opts; + struct superblock_security_struct *sbsec = sb->s_security; + u32 sid; + int rc; + + /* + * Superblock not initialized (i.e. no options) - reject if any + * options specified, otherwise accept. + */ + if (!(sbsec->flags & SE_SBINITIALIZED)) + return opts ? 1 : 0; + + /* + * Superblock initialized and no options specified - reject if + * superblock has any options set, otherwise accept. + */ + if (!opts) + return (sbsec->flags & SE_MNTMASK) ? 1 : 0; + + if (opts->fscontext) { + rc = parse_sid(sb, opts->fscontext, &sid); + if (rc) + return 1; + if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid)) + return 1; + } + if (opts->context) { + rc = parse_sid(sb, opts->context, &sid); + if (rc) + return 1; + if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid)) + return 1; + } + if (opts->rootcontext) { + struct inode_security_struct *root_isec; + + root_isec = backing_inode_security(sb->s_root); + rc = parse_sid(sb, opts->rootcontext, &sid); + if (rc) + return 1; + if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid)) + return 1; + } + if (opts->defcontext) { + rc = parse_sid(sb, opts->defcontext, &sid); + if (rc) + return 1; + if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid)) + return 1; + } + return 0; +} + static int selinux_sb_remount(struct super_block *sb, void *mnt_opts) { struct selinux_mnt_opts *opts = mnt_opts; @@ -3920,7 +4005,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { struct file *file; - u32 sid = task_sid(tsk); + u32 sid = task_sid_obj(tsk); u32 perm; struct file_security_struct *fsec; @@ -4139,47 +4224,52 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents) static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETPGID, NULL); } static int selinux_task_getpgid(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETPGID, NULL); } static int selinux_task_getsid(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETSESSION, NULL); } -static void selinux_task_getsecid(struct task_struct *p, u32 *secid) +static void selinux_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + *secid = task_sid_subj(p); +} + +static void selinux_task_getsecid_obj(struct task_struct *p, u32 *secid) { - *secid = task_sid(p); + *secid = task_sid_obj(p); } static int selinux_task_setnice(struct task_struct *p, int nice) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_setioprio(struct task_struct *p, int ioprio) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getioprio(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } @@ -4210,7 +4300,7 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, upon context transitions. See selinux_bprm_committing_creds. */ if (old_rlim->rlim_max != new_rlim->rlim_max) return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); return 0; @@ -4219,21 +4309,21 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, static int selinux_task_setscheduler(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getscheduler(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } static int selinux_task_movememory(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } @@ -4252,14 +4342,14 @@ static int selinux_task_kill(struct task_struct *p, struct kernel_siginfo *info, else secid = cred_sid(cred); return avc_has_perm(&selinux_state, - secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); + secid, task_sid_obj(p), SECCLASS_PROCESS, perm, NULL); } static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_security_struct *isec = selinux_inode(inode); - u32 sid = task_sid(p); + u32 sid = task_sid_obj(p); spin_lock(&isec->lock); isec->sclass = inode_mode_to_security_class(inode->i_mode); @@ -6152,7 +6242,7 @@ static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *m struct ipc_security_struct *isec; struct msg_security_struct *msec; struct common_audit_data ad; - u32 sid = task_sid(target); + u32 sid = task_sid_subj(target); int rc; isec = selinux_ipc(msq); @@ -7077,6 +7167,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security), LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts), + LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat), LSM_HOOK_INIT(sb_remount, selinux_sb_remount), LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount), LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options), @@ -7148,7 +7239,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), - LSM_HOOK_INIT(task_getsecid, selinux_task_getsecid), + LSM_HOOK_INIT(task_getsecid_subj, selinux_task_getsecid_subj), + LSM_HOOK_INIT(task_getsecid_obj, selinux_task_getsecid_obj), LSM_HOOK_INIT(task_setnice, selinux_task_setnice), LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio), diff --git a/security/selinux/ima.c b/security/selinux/ima.c index 03715893ff97..34d421861bfc 100644 --- a/security/selinux/ima.c +++ b/security/selinux/ima.c @@ -13,18 +13,83 @@ #include "ima.h" /* - * selinux_ima_measure_state - Measure hash of the SELinux policy + * selinux_ima_collect_state - Read selinux configuration settings * - * @state: selinux state struct + * @state: selinux_state * - * NOTE: This function must be called with policy_mutex held. + * On success returns the configuration settings string. + * On error, returns NULL. */ -void selinux_ima_measure_state(struct selinux_state *state) +static char *selinux_ima_collect_state(struct selinux_state *state) { + const char *on = "=1;", *off = "=0;"; + char *buf; + int buf_len, len, i, rc; + + buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1; + + len = strlen(on); + for (i = 0; i < __POLICYDB_CAPABILITY_MAX; i++) + buf_len += strlen(selinux_policycap_names[i]) + len; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return NULL; + + rc = strscpy(buf, "initialized", buf_len); + WARN_ON(rc < 0); + + rc = strlcat(buf, selinux_initialized(state) ? on : off, buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, "enforcing", buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, enforcing_enabled(state) ? on : off, buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, "checkreqprot", buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, checkreqprot_get(state) ? on : off, buf_len); + WARN_ON(rc >= buf_len); + + for (i = 0; i < __POLICYDB_CAPABILITY_MAX; i++) { + rc = strlcat(buf, selinux_policycap_names[i], buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, state->policycap[i] ? on : off, buf_len); + WARN_ON(rc >= buf_len); + } + + return buf; +} + +/* + * selinux_ima_measure_state_locked - Measure SELinux state and hash of policy + * + * @state: selinux state struct + */ +void selinux_ima_measure_state_locked(struct selinux_state *state) +{ + char *state_str = NULL; void *policy = NULL; size_t policy_len; int rc = 0; + WARN_ON(!mutex_is_locked(&state->policy_mutex)); + + state_str = selinux_ima_collect_state(state); + if (!state_str) { + pr_err("SELinux: %s: failed to read state.\n", __func__); + return; + } + + ima_measure_critical_data("selinux", "selinux-state", + state_str, strlen(state_str), false); + + kfree(state_str); + /* * Measure SELinux policy only after initialization is completed. */ @@ -42,3 +107,17 @@ void selinux_ima_measure_state(struct selinux_state *state) vfree(policy); } + +/* + * selinux_ima_measure_state - Measure SELinux state and hash of policy + * + * @state: selinux state struct + */ +void selinux_ima_measure_state(struct selinux_state *state) +{ + WARN_ON(mutex_is_locked(&state->policy_mutex)); + + mutex_lock(&state->policy_mutex); + selinux_ima_measure_state_locked(state); + mutex_unlock(&state->policy_mutex); +} diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index ba2e01a6955c..62d19bccf3de 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -242,11 +242,12 @@ struct security_class_mapping secclass_map[] = { { "infiniband_endport", { "manage_subnet", NULL } }, { "bpf", - {"map_create", "map_read", "map_write", "prog_load", "prog_run"} }, + { "map_create", "map_read", "map_write", "prog_load", "prog_run", + NULL } }, { "xdp_socket", { COMMON_SOCK_PERMS, NULL } }, { "perf_event", - {"open", "cpu", "kernel", "tracepoint", "read", "write"} }, + { "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } }, { "lockdown", { "integrity", "confidentiality", NULL } }, { "anon_inode", diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h index d69c36611423..75ca92b4a462 100644 --- a/security/selinux/include/ima.h +++ b/security/selinux/include/ima.h @@ -15,10 +15,16 @@ #ifdef CONFIG_IMA extern void selinux_ima_measure_state(struct selinux_state *selinux_state); +extern void selinux_ima_measure_state_locked( + struct selinux_state *selinux_state); #else static inline void selinux_ima_measure_state(struct selinux_state *selinux_state) { } +static inline void selinux_ima_measure_state_locked( + struct selinux_state *selinux_state) +{ +} #endif #endif /* _SELINUX_IMA_H_ */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 7650de048570..ac0ece01305a 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -426,7 +426,7 @@ extern struct page *selinux_kernel_status_page(struct selinux_state *state); #define SELINUX_KERNEL_STATUS_VERSION 1 struct selinux_kernel_status { - u32 version; /* version number of thie structure */ + u32 version; /* version number of the structure */ u32 sequence; /* sequence number of seqlock logic */ u32 enforcing; /* current setting of enforcing mode */ u32 policyload; /* times of policy reloaded */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index fff6babeeae6..e4cd7cb856f3 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -41,6 +41,7 @@ #include "security.h" #include "objsec.h" #include "conditional.h" +#include "ima.h" enum sel_inos { SEL_ROOT_INO = 2, @@ -182,6 +183,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, selinux_status_update_setenforce(state, new_value); if (!new_value) call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); + + selinux_ima_measure_state(state); } length = count; out: @@ -758,6 +761,9 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, checkreqprot_set(fsi->state, (new_value ? 1 : 0)); length = count; + + selinux_ima_measure_state(fsi->state); + out: kfree(page); return length; diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 3881787ce492..b8f6b3e0a921 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -13,7 +13,7 @@ static struct kmem_cache *hashtab_node_cachep __ro_after_init; /* * Here we simply round the number of elements up to the nearest power of two. - * I tried also other options like rouding down or rounding to the closest + * I tried also other options like rounding down or rounding to the closest * power of two (up or down based on which is closer), but I was unable to * find any significant difference in lookup/insert performance that would * justify switching to a different (less intuitive) formula. It could be that diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 301633145040..f0ba82611343 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2202,7 +2202,7 @@ static void selinux_notify_policy_change(struct selinux_state *state, selinux_status_update_policyload(state, seqno); selinux_netlbl_cache_invalidate(); selinux_xfrm_notify_policyload(); - selinux_ima_measure_state(state); + selinux_ima_measure_state_locked(state); } void selinux_policy_commit(struct selinux_state *state, |