diff options
29 files changed, 372 insertions, 92 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 63d2c4339689..61d34e1dc59c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2713,7 +2713,16 @@ static void binder_transaction(struct binder_proc *proc, u32 secid; size_t added_size; - security_task_getsecid(proc->tsk, &secid); + /* + * Arguably this should be the task's subjective LSM secid but + * we can't reliably access the subjective creds of a task + * other than our own so we must use the objective creds, 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. + */ + security_task_getsecid_obj(proc->tsk, &secid); ret = security_secid_to_secctx(secid, &secctx, &secctx_sz); if (ret) { return_error = BR_FAILED_REPLY; diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index 971a9251c1d9..a06d213d7689 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -463,6 +463,9 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, if (opt < 0) return ctx->sloppy ? 1 : opt; + if (fc->security) + ctx->has_sec_mnt_opts = 1; + switch (opt) { case Opt_source: if (fc->source) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 7b644d6c09e4..7395d0977b7d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -96,6 +96,7 @@ struct nfs_fs_context { char *fscache_uniq; unsigned short protofamily; unsigned short mountfamily; + bool has_sec_mnt_opts; struct { union { diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 94885c6f8f54..4aaa1f5dd381 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1045,7 +1045,7 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx) sb->s_blocksize = 0; sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr; sb->s_op = server->nfs_client->cl_nfs_mod->sops; - if (ctx && ctx->bsize) + if (ctx->bsize) sb->s_blocksize = nfs_block_size(ctx->bsize, &sb->s_blocksize_bits); if (server->nfs_client->rpc_ops->version != 2) { @@ -1077,6 +1077,7 @@ static void nfs_fill_super(struct super_block *sb, struct nfs_fs_context *ctx) &sb->s_blocksize_bits); nfs_super_set_maxbytes(sb, server->maxfilesize); + server->has_sec_mnt_opts = ctx->has_sec_mnt_opts; } static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, @@ -1193,6 +1194,9 @@ static int nfs_compare_super(struct super_block *sb, struct fs_context *fc) return 0; if (!nfs_compare_userns(old, server)) return 0; + if ((old->has_sec_mnt_opts || fc->security) && + security_sb_mnt_opts_compat(sb, fc->security)) + return 0; return nfs_compare_mount_options(sb, server, fc); } diff --git a/include/linux/cred.h b/include/linux/cred.h index 4c6350503697..ac0e5f97d7d8 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -140,7 +140,7 @@ struct cred { struct key *request_key_auth; /* assumed request_key authority */ #endif #ifdef CONFIG_SECURITY - void *security; /* subjective LSM security */ + void *security; /* LSM security */ #endif struct user_struct *user; /* real user ID subscription */ struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 477a597db013..61f04f7dc1a4 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -62,6 +62,7 @@ LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb) LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb) LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts) LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts) +LSM_HOOK(int, 0, sb_mnt_opts_compat, struct super_block *sb, void *mnt_opts) LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts) LSM_HOOK(int, 0, sb_kern_mount, struct super_block *sb) LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb) @@ -203,7 +204,10 @@ LSM_HOOK(int, 0, task_fix_setgid, struct cred *new, const struct cred * old, LSM_HOOK(int, 0, task_setpgid, struct task_struct *p, pid_t pgid) LSM_HOOK(int, 0, task_getpgid, struct task_struct *p) LSM_HOOK(int, 0, task_getsid, struct task_struct *p) -LSM_HOOK(void, LSM_RET_VOID, task_getsecid, struct task_struct *p, u32 *secid) +LSM_HOOK(void, LSM_RET_VOID, task_getsecid_subj, + struct task_struct *p, u32 *secid) +LSM_HOOK(void, LSM_RET_VOID, task_getsecid_obj, + struct task_struct *p, u32 *secid) LSM_HOOK(int, 0, task_setnice, struct task_struct *p, int nice) LSM_HOOK(int, 0, task_setioprio, struct task_struct *p, int ioprio) LSM_HOOK(int, 0, task_getioprio, struct task_struct *p) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index fb7f3193753d..ba2ccd950833 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -142,6 +142,12 @@ * @orig the original mount data copied from userspace. * @copy copied data which will be passed to the security module. * Returns 0 if the copy was successful. + * @sb_mnt_opts_compat: + * Determine if the new mount options in @mnt_opts are allowed given + * the existing mounted filesystem at @sb. + * @sb superblock being compared + * @mnt_opts new mount options + * Return 0 if options are compatible. * @sb_remount: * Extracts security system specific mount options and verifies no changes * are being made to those options. @@ -707,9 +713,15 @@ * @p. * @p contains the task_struct for the process. * Return 0 if permission is granted. - * @task_getsecid: - * Retrieve the security identifier of the process @p. - * @p contains the task_struct for the process and place is into @secid. + * @task_getsecid_subj: + * Retrieve the subjective security identifier of the task_struct in @p + * and return it in @secid. Special care must be taken to ensure that @p + * is the either the "current" task, or the caller has exclusive access + * to @p. + * In case of failure, @secid will be set to zero. + * @task_getsecid_obj: + * Retrieve the objective security identifier of the task_struct in @p + * and return it in @secid. * In case of failure, @secid will be set to zero. * * @task_setnice: diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 6f76b32a0238..a28d71b45b5f 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -256,6 +256,7 @@ struct nfs_server { /* User namespace info */ const struct cred *cred; + bool has_sec_mnt_opts; }; /* Server capabilities */ diff --git a/include/linux/security.h b/include/linux/security.h index 8aeebd6646dc..9aeda3f9e838 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -294,6 +294,7 @@ int security_sb_alloc(struct super_block *sb); void security_sb_free(struct super_block *sb); void security_free_mnt_opts(void **mnt_opts); int security_sb_eat_lsm_opts(char *options, void **mnt_opts); +int security_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts); int security_sb_remount(struct super_block *sb, void *mnt_opts); int security_sb_kern_mount(struct super_block *sb); int security_sb_show_options(struct seq_file *m, struct super_block *sb); @@ -414,7 +415,8 @@ int security_task_fix_setgid(struct cred *new, const struct cred *old, int security_task_setpgid(struct task_struct *p, pid_t pgid); int security_task_getpgid(struct task_struct *p); int security_task_getsid(struct task_struct *p); -void security_task_getsecid(struct task_struct *p, u32 *secid); +void security_task_getsecid_subj(struct task_struct *p, u32 *secid); +void security_task_getsecid_obj(struct task_struct *p, u32 *secid); int security_task_setnice(struct task_struct *p, int nice); int security_task_setioprio(struct task_struct *p, int ioprio); int security_task_getioprio(struct task_struct *p); @@ -646,6 +648,13 @@ static inline int security_sb_remount(struct super_block *sb, return 0; } +static inline int security_sb_mnt_opts_compat(struct super_block *sb, + void *mnt_opts) +{ + return 0; +} + + static inline int security_sb_kern_mount(struct super_block *sb) { return 0; @@ -1098,7 +1107,12 @@ static inline int security_task_getsid(struct task_struct *p) return 0; } -static inline void security_task_getsecid(struct task_struct *p, u32 *secid) +static inline void security_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + *secid = 0; +} + +static inline void security_task_getsecid_obj(struct task_struct *p, u32 *secid) { *secid = 0; } diff --git a/kernel/audit.c b/kernel/audit.c index 551a394bc8f4..121d37e700a6 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2132,7 +2132,7 @@ int audit_log_task_context(struct audit_buffer *ab) int error; u32 sid; - security_task_getsecid(current, &sid); + security_task_getsecid_subj(current, &sid); if (!sid) return 0; @@ -2353,7 +2353,7 @@ int audit_signal_info(int sig, struct task_struct *t) audit_sig_uid = auid; else audit_sig_uid = uid; - security_task_getsecid(current, &audit_sig_sid); + security_task_getsecid_subj(current, &audit_sig_sid); } return audit_signal_info_syscall(t); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 333b3bcfc545..db2c6b59dfc3 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1359,7 +1359,8 @@ int audit_filter(int msgtype, unsigned int listtype) case AUDIT_SUBJ_SEN: case AUDIT_SUBJ_CLR: if (f->lsm_rule) { - security_task_getsecid(current, &sid); + security_task_getsecid_subj(current, + &sid); result = security_audit_rule_match(sid, f->type, f->op, f->lsm_rule); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 47fb48f42c93..9973865cbf13 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -667,7 +667,7 @@ static int audit_filter_rules(struct task_struct *tsk, logged upon error */ if (f->lsm_rule) { if (need_sid) { - security_task_getsecid(tsk, &sid); + security_task_getsecid_subj(tsk, &sid); need_sid = 0; } result = security_audit_rule_match(sid, f->type, @@ -2400,7 +2400,7 @@ void __audit_ptrace(struct task_struct *t) context->target_auid = audit_get_loginuid(t); context->target_uid = task_uid(t); context->target_sessionid = audit_get_sessionid(t); - security_task_getsecid(t, &context->target_sid); + security_task_getsecid_obj(t, &context->target_sid); memcpy(context->target_comm, t->comm, TASK_COMM_LEN); } @@ -2427,7 +2427,7 @@ int audit_signal_info_syscall(struct task_struct *t) ctx->target_auid = audit_get_loginuid(t); ctx->target_uid = t_uid; ctx->target_sessionid = audit_get_sessionid(t); - security_task_getsecid(t, &ctx->target_sid); + security_task_getsecid_obj(t, &ctx->target_sid); memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN); return 0; } @@ -2448,7 +2448,7 @@ int audit_signal_info_syscall(struct task_struct *t) axp->target_auid[axp->pid_count] = audit_get_loginuid(t); axp->target_uid[axp->pid_count] = t_uid; axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t); - security_task_getsecid(t, &axp->target_sid[axp->pid_count]); + security_task_getsecid_obj(t, &axp->target_sid[axp->pid_count]); memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN); axp->pid_count++; diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 1622a44d1617..0ff58259ccf8 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -209,7 +209,8 @@ BTF_ID(func, bpf_lsm_socket_socketpair) BTF_ID(func, bpf_lsm_syslog) BTF_ID(func, bpf_lsm_task_alloc) -BTF_ID(func, bpf_lsm_task_getsecid) +BTF_ID(func, bpf_lsm_task_getsecid_subj) +BTF_ID(func, bpf_lsm_task_getsecid_obj) BTF_ID(func, bpf_lsm_task_prctl) BTF_ID(func, bpf_lsm_task_setscheduler) BTF_ID(func, bpf_lsm_task_to_inode) diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index ccb491642811..3e6ac9b790b1 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1539,7 +1539,7 @@ int __init netlbl_unlabel_defconf(void) /* Only the kernel is allowed to call this function and the only time * it is called is at bootup before the audit subsystem is reporting * messages so don't worry to much about these values. */ - security_task_getsecid(current, &audit_info.secid); + security_task_getsecid_subj(current, &audit_info.secid); audit_info.loginuid = GLOBAL_ROOT_UID; audit_info.sessionid = 0; diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h index 3c67afce64f1..b9ba8112b3c5 100644 --- a/net/netlabel/netlabel_user.h +++ b/net/netlabel/netlabel_user.h @@ -34,7 +34,7 @@ static inline void netlbl_netlink_auditinfo(struct sk_buff *skb, struct netlbl_audit *audit_info) { - security_task_getsecid(current, &audit_info->secid); + security_task_getsecid_subj(current, &audit_info->secid); audit_info->loginuid = audit_get_loginuid(current); audit_info->sessionid = audit_get_sessionid(current); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 240a53387e6b..f72406fe1bf2 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1252,7 +1252,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_free, apparmor_task_free), LSM_HOOK_INIT(task_alloc, apparmor_task_alloc), - LSM_HOOK_INIT(task_getsecid, apparmor_task_getsecid), + LSM_HOOK_INIT(task_getsecid_subj, apparmor_task_getsecid), + LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_kill, apparmor_task_kill), diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 565e33ff19d0..4e5eb0236278 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -76,7 +76,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode, if (!ima_appraise) return 0; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return ima_match_policy(mnt_userns, inode, current_cred(), secid, func, mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL); } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 9ef748ea829f..b85d9e429426 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -391,7 +391,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) u32 secid; if (file && (prot & PROT_EXEC)) { - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, NULL, 0, MAY_EXEC, MMAP_CHECK); } @@ -429,7 +429,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) return 0; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); inode = file_inode(vma->vm_file); action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode, current_cred(), secid, MAY_EXEC, MMAP_CHECK, @@ -470,7 +470,7 @@ int ima_bprm_check(struct linux_binprm *bprm) int ret; u32 secid; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, MAY_EXEC, BPRM_CHECK); if (ret) @@ -495,7 +495,7 @@ int ima_file_check(struct file *file, int mask) { u32 secid; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); @@ -686,7 +686,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id, /* Read entire file for all partial reads. */ func = read_idmap[read_id] ?: FILE_CHECK; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, NULL, 0, MAY_READ, func); } @@ -729,7 +729,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size, } func = read_idmap[read_id] ?: FILE_CHECK; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, buf, size, MAY_READ, func); } @@ -872,7 +872,7 @@ void process_buffer_measurement(struct user_namespace *mnt_userns, * buffer measurements. */ if (func) { - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); action = ima_get_action(mnt_userns, inode, current_cred(), secid, 0, func, &pcr, &template, func_data); diff --git a/security/security.c b/security/security.c index 5ac96b16f8fa..94383f83ba42 100644 --- a/security/security.c +++ b/security/security.c @@ -890,6 +890,13 @@ int security_sb_eat_lsm_opts(char *options, void **mnt_opts) } EXPORT_SYMBOL(security_sb_eat_lsm_opts); +int security_sb_mnt_opts_compat(struct super_block *sb, + void *mnt_opts) +{ + return call_int_hook(sb_mnt_opts_compat, 0, sb, mnt_opts); +} +EXPORT_SYMBOL(security_sb_mnt_opts_compat); + int security_sb_remount(struct super_block *sb, void *mnt_opts) { @@ -1762,12 +1769,19 @@ int security_task_getsid(struct task_struct *p) return call_int_hook(task_getsid, 0, p); } -void security_task_getsecid(struct task_struct *p, u32 *secid) +void security_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + *secid = 0; + call_void_hook(task_getsecid_subj, p, secid); +} +EXPORT_SYMBOL(security_task_getsecid_subj); + +void security_task_getsecid_obj(struct task_struct *p, u32 *secid) { *secid = 0; - call_void_hook(task_getsecid, p, secid); + call_void_hook(task_getsecid_obj, p, secid); } -EXPORT_SYMBOL(security_task_getsecid); +EXPORT_SYMBOL(security_task_getsecid_obj); int security_task_setnice(struct task_struct *p, int nice) { 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, diff --git a/security/smack/smack.h b/security/smack/smack.h index a9768b12716b..08f9cb80655c 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -383,7 +383,23 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp) return tsp->smk_task; } -static inline struct smack_known *smk_of_task_struct( +static inline struct smack_known *smk_of_task_struct_subj( + const struct task_struct *t) +{ + struct smack_known *skp; + const struct cred *cred; + + rcu_read_lock(); + + cred = rcu_dereference(t->cred); + skp = smk_of_task(smack_cred(cred)); + + rcu_read_unlock(); + + return skp; +} + +static inline struct smack_known *smk_of_task_struct_obj( const struct task_struct *t) { struct smack_known *skp; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 12a45e61c1a5..cd14bec4ad80 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -159,7 +159,7 @@ static int smk_bu_current(char *note, struct smack_known *oskp, static int smk_bu_task(struct task_struct *otp, int mode, int rc) { struct task_smack *tsp = smack_cred(current_cred()); - struct smack_known *smk_task = smk_of_task_struct(otp); + struct smack_known *smk_task = smk_of_task_struct_obj(otp); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (rc <= 0) @@ -479,7 +479,7 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { struct smack_known *skp; - skp = smk_of_task_struct(ctp); + skp = smk_of_task_struct_obj(ctp); return smk_ptrace_rule_check(current, skp, mode, __func__); } @@ -2033,7 +2033,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access, const char *caller) { struct smk_audit_info ad; - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_subj(p); int rc; smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); @@ -2078,15 +2078,29 @@ static int smack_task_getsid(struct task_struct *p) } /** - * smack_task_getsecid - get the secid of the task - * @p: the object task + * smack_task_getsecid_subj - get the subjective secid of the task + * @p: the task * @secid: where to put the result * - * Sets the secid to contain a u32 version of the smack label. + * Sets the secid to contain a u32 version of the task's subjective smack label. + */ +static void smack_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + struct smack_known *skp = smk_of_task_struct_subj(p); + + *secid = skp->smk_secid; +} + +/** + * smack_task_getsecid_obj - get the objective secid of the task + * @p: the task + * @secid: where to put the result + * + * Sets the secid to contain a u32 version of the task's objective smack label. */ -static void smack_task_getsecid(struct task_struct *p, u32 *secid) +static void smack_task_getsecid_obj(struct task_struct *p, u32 *secid) { - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_obj(p); *secid = skp->smk_secid; } @@ -2174,7 +2188,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, { struct smk_audit_info ad; struct smack_known *skp; - struct smack_known *tkp = smk_of_task_struct(p); + struct smack_known *tkp = smk_of_task_struct_obj(p); int rc; if (!sig) @@ -2212,7 +2226,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, static void smack_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_smack *isp = smack_inode(inode); - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_obj(p); isp->smk_inode = skp; isp->smk_flags |= SMK_INODE_INSTANT; @@ -3483,7 +3497,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ static int smack_getprocattr(struct task_struct *p, char *name, char **value) { - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_subj(p); char *cp; int slen; @@ -4759,7 +4773,8 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setpgid, smack_task_setpgid), LSM_HOOK_INIT(task_getpgid, smack_task_getpgid), LSM_HOOK_INIT(task_getsid, smack_task_getsid), - LSM_HOOK_INIT(task_getsecid, smack_task_getsecid), + LSM_HOOK_INIT(task_getsecid_subj, smack_task_getsecid_subj), + LSM_HOOK_INIT(task_getsecid_obj, smack_task_getsecid_obj), LSM_HOOK_INIT(task_setnice, smack_task_setnice), LSM_HOOK_INIT(task_setioprio, smack_task_setioprio), LSM_HOOK_INIT(task_getioprio, smack_task_getioprio), |