Commit 5d226df4 authored by Andreas Gruenbacher's avatar Andreas Gruenbacher Committed by Paul Moore

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: default avatarAndreas Gruenbacher <agruenba@redhat.com>
Acked-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarPaul Moore <pmoore@redhat.com>
parent 6f3be9f5
...@@ -242,11 +242,63 @@ static int inode_alloc_security(struct inode *inode) ...@@ -242,11 +242,63 @@ static int inode_alloc_security(struct inode *inode)
return 0; 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. * Get the security label of an inode.
*/ */
static struct inode_security_struct *inode_security(struct inode *inode) static struct inode_security_struct *inode_security(struct inode *inode)
{ {
__inode_security_revalidate(inode, NULL, true);
return inode->i_security; return inode->i_security;
} }
...@@ -257,6 +309,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr ...@@ -257,6 +309,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
__inode_security_revalidate(inode, dentry, true);
return inode->i_security; return inode->i_security;
} }
...@@ -363,8 +416,6 @@ static const char *labeling_behaviors[7] = { ...@@ -363,8 +416,6 @@ static const char *labeling_behaviors[7] = {
"uses native labeling", "uses native labeling",
}; };
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
static inline int inode_doinit(struct inode *inode) static inline int inode_doinit(struct inode *inode)
{ {
return inode_doinit_with_dentry(inode, NULL); return inode_doinit_with_dentry(inode, NULL);
...@@ -1655,6 +1706,7 @@ static inline int dentry_has_perm(const struct cred *cred, ...@@ -1655,6 +1706,7 @@ static inline int dentry_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_DENTRY; ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry; ad.u.dentry = dentry;
__inode_security_revalidate(inode, dentry, true);
return inode_has_perm(cred, inode, av, &ad); return inode_has_perm(cred, inode, av, &ad);
} }
...@@ -1670,6 +1722,7 @@ static inline int path_has_perm(const struct cred *cred, ...@@ -1670,6 +1722,7 @@ static inline int path_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_PATH; ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path; ad.u.path = *path;
__inode_security_revalidate(inode, path->dentry, true);
return inode_has_perm(cred, inode, av, &ad); return inode_has_perm(cred, inode, av, &ad);
} }
...@@ -2871,7 +2924,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, ...@@ -2871,7 +2924,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
ad.type = LSM_AUDIT_DATA_DENTRY; ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry; ad.u.dentry = dentry;
sid = cred_sid(cred); 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, return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
rcu ? MAY_NOT_BLOCK : 0); rcu ? MAY_NOT_BLOCK : 0);
...@@ -2923,7 +2978,9 @@ static int selinux_inode_permission(struct inode *inode, int mask) ...@@ -2923,7 +2978,9 @@ static int selinux_inode_permission(struct inode *inode, int mask)
perms = file_mask_to_av(inode->i_mode, mask); perms = file_mask_to_av(inode->i_mode, mask);
sid = cred_sid(cred); 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); rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
audited = avc_audit_required(perms, &avd, rc, audited = avc_audit_required(perms, &avd, rc,
...@@ -3232,6 +3289,7 @@ static int selinux_file_permission(struct file *file, int mask) ...@@ -3232,6 +3289,7 @@ static int selinux_file_permission(struct file *file, int mask)
/* No change since file_open check. */ /* No change since file_open check. */
return 0; return 0;
inode_security_revalidate(inode);
return selinux_revalidate_file_permission(file, mask); return selinux_revalidate_file_permission(file, mask);
} }
...@@ -3537,6 +3595,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) ...@@ -3537,6 +3595,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
* new inode label or new policy. * new inode label or new policy.
* This check is not redundant - do not remove. * 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)); 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, ...@@ -4078,7 +4137,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern) int type, int protocol, int kern)
{ {
const struct task_security_struct *tsec = current_security(); 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; struct sk_security_struct *sksec;
int err = 0; int err = 0;
...@@ -4278,9 +4337,9 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) ...@@ -4278,9 +4337,9 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
if (err) if (err)
return 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->sclass = isec->sclass;
newisec->sid = isec->sid; newisec->sid = isec->sid;
newisec->initialized = LABEL_INITIALIZED; newisec->initialized = LABEL_INITIALIZED;
...@@ -4618,7 +4677,8 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid) ...@@ -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) 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; struct sk_security_struct *sksec = sk->sk_security;
if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment