From a44ca52ca6bd10ce588102789dde33f9fd6569cf Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: selinux: Remove unused variable in selinux_inode_init_security Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d0cfaa9..714acad 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2753,13 +2753,11 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, void **value, size_t *len) { const struct task_security_struct *tsec = current_security(); - struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid, clen; int rc; char *context; - dsec = dir->i_security; sbsec = dir->i_sb->s_security; sid = tsec->sid; -- cgit v1.1 From ea861dfd9e0e7e044a6e65fa02a14b9159b568da Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: security: Make inode argument of inode_getsecurity non-const Make the inode argument of the inode_getsecurity hook non-const so that we can use it to revalidate invalid security labels. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/security.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index 46f405c..73514c9 100644 --- a/security/security.c +++ b/security/security.c @@ -697,7 +697,7 @@ int security_inode_killpriv(struct dentry *dentry) return call_int_hook(inode_killpriv, 0, dentry); } -int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) +int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { if (unlikely(IS_PRIVATE(inode))) return -EOPNOTSUPP; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 714acad..2e40c9c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3108,7 +3108,7 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name) * * Permission check is handled by selinux_inode_getxattr hook. */ -static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) +static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { u32 size; int error; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ff81026..f0e694b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1465,7 +1465,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) * * Returns the size of the attribute or an error code */ -static int smack_inode_getsecurity(const struct inode *inode, +static int smack_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { -- cgit v1.1 From d6335d77a7622a88380f3f207cc1f727f878dd21 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: security: Make inode argument of inode_getsecid non-const Make the inode argument of the inode_getsecid hook non-const so that we can use it to revalidate invalid security labels. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/security.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index 73514c9..c5beb7e 100644 --- a/security/security.c +++ b/security/security.c @@ -721,7 +721,7 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer } EXPORT_SYMBOL(security_inode_listsecurity); -void security_inode_getsecid(const struct inode *inode, u32 *secid) +void security_inode_getsecid(struct inode *inode, u32 *secid) { call_void_hook(inode_getsecid, inode, secid); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2e40c9c..19a8f15 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3180,7 +3180,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t return len; } -static void selinux_inode_getsecid(const struct inode *inode, u32 *secid) +static void selinux_inode_getsecid(struct inode *inode, u32 *secid) { struct inode_security_struct *isec = inode->i_security; *secid = isec->sid; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index f0e694b..ac7436f 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1538,7 +1538,7 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer, * @inode: inode to extract the info from * @secid: where result will be saved */ -static void smack_inode_getsecid(const struct inode *inode, u32 *secid) +static void smack_inode_getsecid(struct inode *inode, u32 *secid) { struct inode_smack *isp = inode->i_security; -- cgit v1.1 From 83da53c5a34564a0a63b26f84293c6e2a639e1e4 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: selinux: Add accessor functions for inode->i_security Add functions dentry_security and inode_security for accessing inode->i_security. These functions initially don't do much, but they will later be used to revalidate the security labels when necessary. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 97 ++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 41 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 19a8f15..1dc0d79 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -242,6 +242,24 @@ static int inode_alloc_security(struct inode *inode) return 0; } +/* + * Get the security label of an inode. + */ +static struct inode_security_struct *inode_security(struct inode *inode) +{ + return inode->i_security; +} + +/* + * Get the security label of a dentry's backing inode. + */ +static struct inode_security_struct *backing_inode_security(struct dentry *dentry) +{ + struct inode *inode = d_backing_inode(dentry); + + return inode->i_security; +} + static void inode_free_rcu(struct rcu_head *head) { struct inode_security_struct *isec; @@ -565,8 +583,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb, opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT; } if (sbsec->flags & ROOTCONTEXT_MNT) { - struct inode *root = d_backing_inode(sbsec->sb->s_root); - struct inode_security_struct *isec = root->i_security; + struct dentry *root = sbsec->sb->s_root; + struct inode_security_struct *isec = backing_inode_security(root); rc = security_sid_to_context(isec->sid, &context, &len); if (rc) @@ -621,8 +639,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, int rc = 0, i; struct superblock_security_struct *sbsec = sb->s_security; const char *name = sb->s_type->name; - struct inode *inode = d_backing_inode(sbsec->sb->s_root); - struct inode_security_struct *root_isec = inode->i_security; + struct dentry *root = sbsec->sb->s_root; + struct inode_security_struct *root_isec = backing_inode_security(root); u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; u32 defcontext_sid = 0; char **mount_options = opts->mnt_opts; @@ -852,8 +870,8 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb, if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid) goto mismatch; if (oldflags & ROOTCONTEXT_MNT) { - struct inode_security_struct *oldroot = d_backing_inode(oldsb->s_root)->i_security; - struct inode_security_struct *newroot = d_backing_inode(newsb->s_root)->i_security; + struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root); + struct inode_security_struct *newroot = backing_inode_security(newsb->s_root); if (oldroot->sid != newroot->sid) goto mismatch; } @@ -903,17 +921,14 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, if (!set_fscontext) newsbsec->sid = sid; if (!set_rootcontext) { - struct inode *newinode = d_backing_inode(newsb->s_root); - struct inode_security_struct *newisec = newinode->i_security; + struct inode_security_struct *newisec = backing_inode_security(newsb->s_root); newisec->sid = sid; } newsbsec->mntpoint_sid = sid; } if (set_rootcontext) { - const struct inode *oldinode = d_backing_inode(oldsb->s_root); - const struct inode_security_struct *oldisec = oldinode->i_security; - struct inode *newinode = d_backing_inode(newsb->s_root); - struct inode_security_struct *newisec = newinode->i_security; + const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root); + struct inode_security_struct *newisec = backing_inode_security(newsb->s_root); newisec->sid = oldisec->sid; } @@ -1712,13 +1727,13 @@ out: /* * Determine the label for an inode that might be unioned. */ -static int selinux_determine_inode_label(const struct inode *dir, +static int selinux_determine_inode_label(struct inode *dir, const struct qstr *name, u16 tclass, u32 *_new_isid) { const struct superblock_security_struct *sbsec = dir->i_sb->s_security; - const struct inode_security_struct *dsec = dir->i_security; + const struct inode_security_struct *dsec = inode_security(dir); const struct task_security_struct *tsec = current_security(); if ((sbsec->flags & SE_SBINITIALIZED) && @@ -1747,7 +1762,7 @@ static int may_create(struct inode *dir, struct common_audit_data ad; int rc; - dsec = dir->i_security; + dsec = inode_security(dir); sbsec = dir->i_sb->s_security; sid = tsec->sid; @@ -1800,8 +1815,8 @@ static int may_link(struct inode *dir, u32 av; int rc; - dsec = dir->i_security; - isec = d_backing_inode(dentry)->i_security; + dsec = inode_security(dir); + isec = backing_inode_security(dentry); ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; @@ -1844,10 +1859,10 @@ static inline int may_rename(struct inode *old_dir, int old_is_dir, new_is_dir; int rc; - old_dsec = old_dir->i_security; - old_isec = d_backing_inode(old_dentry)->i_security; + old_dsec = inode_security(old_dir); + old_isec = backing_inode_security(old_dentry); old_is_dir = d_is_dir(old_dentry); - new_dsec = new_dir->i_security; + new_dsec = inode_security(new_dir); ad.type = LSM_AUDIT_DATA_DENTRY; @@ -1875,7 +1890,7 @@ static inline int may_rename(struct inode *old_dir, if (rc) return rc; if (d_is_positive(new_dentry)) { - new_isec = d_backing_inode(new_dentry)->i_security; + new_isec = backing_inode_security(new_dentry); new_is_dir = d_is_dir(new_dentry); rc = avc_has_perm(sid, new_isec->sid, new_isec->sclass, @@ -2011,8 +2026,8 @@ static int selinux_binder_transfer_file(struct task_struct *from, { u32 sid = task_sid(to); struct file_security_struct *fsec = file->f_security; - struct inode *inode = d_backing_inode(file->f_path.dentry); - struct inode_security_struct *isec = inode->i_security; + struct dentry *dentry = file->f_path.dentry; + struct inode_security_struct *isec = backing_inode_security(dentry); struct common_audit_data ad; int rc; @@ -2028,7 +2043,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, return rc; } - if (unlikely(IS_PRIVATE(inode))) + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), @@ -2217,7 +2232,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) old_tsec = current_security(); new_tsec = bprm->cred->security; - isec = inode->i_security; + isec = inode_security(inode); /* Default to the current task SID. */ new_tsec->sid = old_tsec->sid; @@ -2639,7 +2654,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data) break; case ROOTCONTEXT_MNT: { struct inode_security_struct *root_isec; - root_isec = d_backing_inode(sb->s_root)->i_security; + root_isec = backing_inode_security(sb->s_root); if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid)) goto out_bad_option; @@ -2856,7 +2871,7 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; sid = cred_sid(cred); - isec = inode->i_security; + isec = inode_security(inode); return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, rcu ? MAY_NOT_BLOCK : 0); @@ -2908,7 +2923,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) perms = file_mask_to_av(inode->i_mode, mask); sid = cred_sid(cred); - isec = inode->i_security; + isec = inode_security(inode); rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); audited = avc_audit_required(perms, &avd, rc, @@ -2978,7 +2993,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = backing_inode_security(dentry); struct superblock_security_struct *sbsec; struct common_audit_data ad; u32 newsid, sid = current_sid(); @@ -3055,7 +3070,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = backing_inode_security(dentry); u32 newsid; int rc; @@ -3113,7 +3128,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void u32 size; int error; char *context = NULL; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; @@ -3152,7 +3167,7 @@ out_nofree: static int selinux_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); u32 newsid; int rc; @@ -3182,7 +3197,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t static void selinux_inode_getsecid(struct inode *inode, u32 *secid) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); *secid = isec->sid; } @@ -3205,7 +3220,7 @@ static int selinux_file_permission(struct file *file, int mask) { struct inode *inode = file_inode(file); struct file_security_struct *fsec = file->f_security; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); u32 sid = current_sid(); if (!mask) @@ -3240,7 +3255,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, struct common_audit_data ad; struct file_security_struct *fsec = file->f_security; struct inode *inode = file_inode(file); - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); struct lsm_ioctlop_audit ioctl; u32 ssid = cred_sid(cred); int rc; @@ -3504,7 +3519,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) struct inode_security_struct *isec; fsec = file->f_security; - isec = file_inode(file)->i_security; + isec = inode_security(file_inode(file)); /* * Save inode label and policy sequence number * at open-time so that selinux_file_permission @@ -3622,7 +3637,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid) */ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); struct task_security_struct *tsec = new->security; u32 sid = current_sid(); int ret; @@ -4063,7 +4078,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { const struct task_security_struct *tsec = current_security(); - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct inode_security_struct *isec = inode_security(SOCK_INODE(sock)); struct sk_security_struct *sksec; int err = 0; @@ -4263,9 +4278,9 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) if (err) return err; - newisec = SOCK_INODE(newsock)->i_security; + newisec = inode_security(SOCK_INODE(newsock)); - isec = SOCK_INODE(sock)->i_security; + isec = inode_security(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; newisec->initialized = 1; @@ -4603,7 +4618,7 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid) static void selinux_sock_graft(struct sock *sk, struct socket *parent) { - struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; + struct inode_security_struct *isec = inode_security(SOCK_INODE(parent)); struct sk_security_struct *sksec = sk->sk_security; if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || -- cgit v1.1 From 6f3be9f562e3027c77bc4482ccf2cea8600a7f74 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:40 -0500 Subject: security: Add hook to invalidate inode security labels Add a hook to invalidate an inode's security label when the cached information becomes invalid. Add the new hook in selinux: set a flag when a security label becomes invalid. Signed-off-by: Andreas Gruenbacher Reviewed-by: James Morris Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/security.c | 8 ++++++++ security/selinux/hooks.c | 30 ++++++++++++++++++++---------- security/selinux/include/objsec.h | 6 ++++++ 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index c5beb7e..e8ffd92 100644 --- a/security/security.c +++ b/security/security.c @@ -1161,6 +1161,12 @@ void security_release_secctx(char *secdata, u32 seclen) } EXPORT_SYMBOL(security_release_secctx); +void security_inode_invalidate_secctx(struct inode *inode) +{ + call_void_hook(inode_invalidate_secctx, inode); +} +EXPORT_SYMBOL(security_inode_invalidate_secctx); + int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) { return call_int_hook(inode_notifysecctx, 0, inode, ctx, ctxlen); @@ -1763,6 +1769,8 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.secctx_to_secid), .release_secctx = LIST_HEAD_INIT(security_hook_heads.release_secctx), + .inode_invalidate_secctx = + LIST_HEAD_INIT(security_hook_heads.inode_invalidate_secctx), .inode_notifysecctx = LIST_HEAD_INIT(security_hook_heads.inode_notifysecctx), .inode_setsecctx = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1dc0d79..ef3bd9d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -820,7 +820,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, goto out; root_isec->sid = rootcontext_sid; - root_isec->initialized = 1; + root_isec->initialized = LABEL_INITIALIZED; } if (defcontext_sid) { @@ -1308,11 +1308,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent unsigned len = 0; int rc = 0; - if (isec->initialized) + if (isec->initialized == LABEL_INITIALIZED) goto out; mutex_lock(&isec->lock); - if (isec->initialized) + if (isec->initialized == LABEL_INITIALIZED) goto out_unlock; sbsec = inode->i_sb->s_security; @@ -1484,7 +1484,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent break; } - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; out_unlock: mutex_unlock(&isec->lock); @@ -2790,7 +2790,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, struct inode_security_struct *isec = inode->i_security; isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; } if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT)) @@ -3089,7 +3089,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; return; } @@ -3183,7 +3183,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; return 0; } @@ -3761,7 +3761,7 @@ static void selinux_task_to_inode(struct task_struct *p, u32 sid = task_sid(p); isec->sid = sid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; } /* Returns error only if unable to parse addresses */ @@ -4092,7 +4092,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, return err; } - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; if (sock->sk) { sksec = sock->sk->sk_security; @@ -4283,7 +4283,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) isec = inode_security(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; - newisec->initialized = 1; + newisec->initialized = LABEL_INITIALIZED; return 0; } @@ -5775,6 +5775,15 @@ static void selinux_release_secctx(char *secdata, u32 seclen) kfree(secdata); } +static void selinux_inode_invalidate_secctx(struct inode *inode) +{ + struct inode_security_struct *isec = inode->i_security; + + mutex_lock(&isec->lock); + isec->initialized = LABEL_INVALID; + mutex_unlock(&isec->lock); +} + /* * called with inode->i_mutex locked */ @@ -6006,6 +6015,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx), LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid), LSM_HOOK_INIT(release_secctx, selinux_release_secctx), + LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx), LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx), LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx), LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx), diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 81fa718..a2ae054 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -37,6 +37,12 @@ struct task_security_struct { u32 sockcreate_sid; /* fscreate SID */ }; +enum label_initialized { + LABEL_MISSING, /* not initialized */ + LABEL_INITIALIZED, /* inizialized */ + LABEL_INVALID /* invalid */ +}; + struct inode_security_struct { struct inode *inode; /* back pointer to inode object */ union { -- cgit v1.1 From 5d226df4edfa0eb1e689e7ac2741cf261ff7cbf1 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:40 -0500 Subject: selinux: Revalidate invalid inode security labels When fetching an inode's security label, check if it is still valid, and try reloading it if it is not. Reloading will fail when we are in RCU context which doesn't allow sleeping, or when we can't find a dentry for the inode. (Reloading happens via iop->getxattr which takes a dentry parameter.) When reloading fails, continue using the old, invalid label. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 76 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ef3bd9d..34e3351 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -242,11 +242,63 @@ static int inode_alloc_security(struct inode *inode) return 0; } +static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); + +/* + * Try reloading inode security labels that have been marked as invalid. The + * @may_sleep parameter indicates when sleeping and thus reloading labels is + * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is + * invalid. The @opt_dentry parameter should be set to a dentry of the inode; + * when no dentry is available, set it to NULL instead. + */ +static int __inode_security_revalidate(struct inode *inode, + struct dentry *opt_dentry, + bool may_sleep) +{ + struct inode_security_struct *isec = inode->i_security; + + might_sleep_if(may_sleep); + + if (isec->initialized == LABEL_INVALID) { + if (!may_sleep) + return -ECHILD; + + /* + * Try reloading the inode security label. This will fail if + * @opt_dentry is NULL and no dentry for this inode can be + * found; in that case, continue using the old label. + */ + inode_doinit_with_dentry(inode, opt_dentry); + } + return 0; +} + +static void inode_security_revalidate(struct inode *inode) +{ + __inode_security_revalidate(inode, NULL, true); +} + +static struct inode_security_struct *inode_security_novalidate(struct inode *inode) +{ + return inode->i_security; +} + +static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) +{ + int error; + + error = __inode_security_revalidate(inode, NULL, !rcu); + if (error) + return ERR_PTR(error); + return inode->i_security; +} + /* * Get the security label of an inode. */ static struct inode_security_struct *inode_security(struct inode *inode) { + __inode_security_revalidate(inode, NULL, true); return inode->i_security; } @@ -257,6 +309,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr { struct inode *inode = d_backing_inode(dentry); + __inode_security_revalidate(inode, dentry, true); return inode->i_security; } @@ -363,8 +416,6 @@ static const char *labeling_behaviors[7] = { "uses native labeling", }; -static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); - static inline int inode_doinit(struct inode *inode) { return inode_doinit_with_dentry(inode, NULL); @@ -1655,6 +1706,7 @@ static inline int dentry_has_perm(const struct cred *cred, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; + __inode_security_revalidate(inode, dentry, true); return inode_has_perm(cred, inode, av, &ad); } @@ -1670,6 +1722,7 @@ static inline int path_has_perm(const struct cred *cred, ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = *path; + __inode_security_revalidate(inode, path->dentry, true); return inode_has_perm(cred, inode, av, &ad); } @@ -2871,7 +2924,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; sid = cred_sid(cred); - isec = inode_security(inode); + isec = inode_security_rcu(inode, rcu); + if (IS_ERR(isec)) + return PTR_ERR(isec); return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, rcu ? MAY_NOT_BLOCK : 0); @@ -2923,7 +2978,9 @@ static int selinux_inode_permission(struct inode *inode, int mask) perms = file_mask_to_av(inode->i_mode, mask); sid = cred_sid(cred); - isec = inode_security(inode); + isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK); + if (IS_ERR(isec)) + return PTR_ERR(isec); rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); audited = avc_audit_required(perms, &avd, rc, @@ -3232,6 +3289,7 @@ static int selinux_file_permission(struct file *file, int mask) /* No change since file_open check. */ return 0; + inode_security_revalidate(inode); return selinux_revalidate_file_permission(file, mask); } @@ -3537,6 +3595,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * new inode label or new policy. * This check is not redundant - do not remove. */ + inode_security_revalidate(file_inode(file)); return file_path_has_perm(cred, file, open_file_to_av(file)); } @@ -4078,7 +4137,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { const struct task_security_struct *tsec = current_security(); - struct inode_security_struct *isec = inode_security(SOCK_INODE(sock)); + struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); struct sk_security_struct *sksec; int err = 0; @@ -4278,9 +4337,9 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) if (err) return err; - newisec = inode_security(SOCK_INODE(newsock)); + newisec = inode_security_novalidate(SOCK_INODE(newsock)); - isec = inode_security(SOCK_INODE(sock)); + isec = inode_security_novalidate(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; newisec->initialized = LABEL_INITIALIZED; @@ -4618,7 +4677,8 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid) static void selinux_sock_graft(struct sock *sk, struct socket *parent) { - struct inode_security_struct *isec = inode_security(SOCK_INODE(parent)); + struct inode_security_struct *isec = + inode_security_novalidate(SOCK_INODE(parent)); struct sk_security_struct *sksec = sk->sk_security; if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || -- cgit v1.1 From f9df6458218f4fe8a1c3bf0af89c1fa9eaf0db39 Mon Sep 17 00:00:00 2001 From: Andrew Perepechko Date: Thu, 24 Dec 2015 11:09:41 -0500 Subject: selinux: export validatetrans decisions Make validatetrans decisions available through selinuxfs. "/validatetrans" is added to selinuxfs for this purpose. This functionality is needed by file system servers implemented in userspace or kernelspace without the VFS layer. Writing "$oldcontext $newcontext $tclass $taskcontext" to /validatetrans is expected to return 0 if the transition is allowed and -EPERM otherwise. Signed-off-by: Andrew Perepechko CC: andrew.perepechko@seagate.com Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/include/classmap.h | 2 +- security/selinux/include/security.h | 3 ++ security/selinux/selinuxfs.c | 80 +++++++++++++++++++++++++++++++++++++ security/selinux/ss/services.c | 34 ++++++++++++---- 4 files changed, 111 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 5a4eef5..ef83c4b 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -21,7 +21,7 @@ struct security_class_mapping secclass_map[] = { { "compute_av", "compute_create", "compute_member", "check_context", "load_policy", "compute_relabel", "compute_user", "setenforce", "setbool", "setsecparam", - "setcheckreqprot", "read_policy", NULL } }, + "setcheckreqprot", "read_policy", "validate_trans", NULL } }, { "process", { "fork", "transition", "sigchld", "sigkill", "sigstop", "signull", "signal", "ptrace", "getsched", "setsched", diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 223e9fd..38feb55 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -187,6 +187,9 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, + u16 tclass); + int security_bounded_transition(u32 oldsid, u32 newsid); int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index c02da25..0dc407d 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -116,6 +116,7 @@ enum sel_inos { SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ SEL_STATUS, /* export current status using mmap() */ SEL_POLICY, /* allow userspace to read the in kernel policy */ + SEL_VALIDATE_TRANS, /* compute validatetrans decision */ SEL_INO_NEXT, /* The next inode number to use */ }; @@ -653,6 +654,83 @@ static const struct file_operations sel_checkreqprot_ops = { .llseek = generic_file_llseek, }; +static ssize_t sel_write_validatetrans(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; + char *req = NULL; + u32 osid, nsid, tsid; + u16 tclass; + int rc; + + rc = task_has_security(current, SECURITY__VALIDATE_TRANS); + if (rc) + goto out; + + rc = -ENOMEM; + if (count >= PAGE_SIZE) + goto out; + + /* No partial writes. */ + rc = -EINVAL; + if (*ppos != 0) + goto out; + + rc = -ENOMEM; + req = kzalloc(count + 1, GFP_KERNEL); + if (!req) + goto out; + + rc = -EFAULT; + if (copy_from_user(req, buf, count)) + goto out; + + rc = -ENOMEM; + oldcon = kzalloc(count + 1, GFP_KERNEL); + if (!oldcon) + goto out; + + newcon = kzalloc(count + 1, GFP_KERNEL); + if (!newcon) + goto out; + + taskcon = kzalloc(count + 1, GFP_KERNEL); + if (!taskcon) + goto out; + + rc = -EINVAL; + if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) + goto out; + + rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL); + if (rc) + goto out; + + rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL); + if (rc) + goto out; + + rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL); + if (rc) + goto out; + + rc = security_validate_transition_user(osid, nsid, tsid, tclass); + if (!rc) + rc = count; +out: + kfree(req); + kfree(oldcon); + kfree(newcon); + kfree(taskcon); + return rc; +} + +static const struct file_operations sel_transition_ops = { + .write = sel_write_validatetrans, + .llseek = generic_file_llseek, +}; + /* * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c */ @@ -1759,6 +1837,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, + [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, + S_IWUGO}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ebb5eb3..ebda973 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -778,8 +778,8 @@ out: return -EPERM; } -int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, - u16 orig_tclass) +static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, + u16 orig_tclass, bool user) { struct context *ocontext; struct context *ncontext; @@ -794,11 +794,12 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, read_lock(&policy_rwlock); - tclass = unmap_class(orig_tclass); + if (!user) + tclass = unmap_class(orig_tclass); + else + tclass = orig_tclass; if (!tclass || tclass > policydb.p_classes.nprim) { - printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", - __func__, tclass); rc = -EINVAL; goto out; } @@ -832,8 +833,13 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, while (constraint) { if (!constraint_expr_eval(ocontext, ncontext, tcontext, constraint->expr)) { - rc = security_validtrans_handle_fail(ocontext, ncontext, - tcontext, tclass); + if (user) + rc = -EPERM; + else + rc = security_validtrans_handle_fail(ocontext, + ncontext, + tcontext, + tclass); goto out; } constraint = constraint->next; @@ -844,6 +850,20 @@ out: return rc; } +int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, + u16 tclass) +{ + return security_compute_validatetrans(oldsid, newsid, tasksid, + tclass, true); +} + +int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, + u16 orig_tclass) +{ + return security_compute_validatetrans(oldsid, newsid, tasksid, + orig_tclass, false); +} + /* * security_bounded_transition - check whether the given * transition is directed to bounded, or not. -- cgit v1.1 From 76319946f321e30872dd72af7de867cb26e7a373 Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Thu, 24 Dec 2015 11:09:41 -0500 Subject: selinux: rate-limit netlink message warnings in selinux_nlmsg_perm() Any process is able to send netlink messages with invalid types. Make the warning rate-limited to prevent too much log spam. The warning is supposed to help to find misbehaving programs, so print the triggering command name and pid. Reported-by: Florian Weimer Signed-off-by: Vladis Dronov [PM: subject line tweak to make checkpatch.pl happy] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 34e3351..40e071a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4858,11 +4858,12 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { - printk(KERN_WARNING - "SELinux: unrecognized netlink message:" - " protocol=%hu nlmsg_type=%hu sclass=%s\n", + pr_warn_ratelimited("SELinux: unrecognized netlink" + " message: protocol=%hu nlmsg_type=%hu sclass=%s" + " pig=%d comm=%s\n", sk->sk_protocol, nlh->nlmsg_type, - secclass_map[sksec->sclass - 1].name); + secclass_map[sksec->sclass - 1].name, + task_pid_nr(current), current->comm); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } -- cgit v1.1