@@ -1261,6 +1261,10 @@
* audit_rule_init.
* @rule contains the allocated rule
*
+ * @inode_invalidate_secctx:
+ * Notify the security module that it must revalidate the security context
+ * of an inode before the next access.
+ *
* @inode_notifysecctx:
* Notify the security module of what the security context of an inode
* should be. Initializes the incore security context managed by the
@@ -1516,6 +1520,7 @@ union security_list_options {
int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid);
void (*release_secctx)(char *secdata, u32 seclen);
+ void (*inode_invalidate_secctx)(struct inode *inode);
int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
@@ -1757,6 +1762,7 @@ struct security_hook_heads {
struct list_head secid_to_secctx;
struct list_head secctx_to_secid;
struct list_head release_secctx;
+ struct list_head inode_invalidate_secctx;
struct list_head inode_notifysecctx;
struct list_head inode_setsecctx;
struct list_head inode_getsecctx;
@@ -353,6 +353,7 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
void security_release_secctx(char *secdata, u32 seclen);
+int security_inode_invalidate_secctx(struct inode *inode);
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
@@ -1093,6 +1094,10 @@ static inline void security_release_secctx(char *secdata, u32 seclen)
{
}
+static inline void security_inode_invalidate_secctx(struct inode *inode)
+{
+}
+
static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
return -EOPNOTSUPP;
@@ -430,7 +430,7 @@ static int sb_finish_set_opts(struct super_block *sb)
rc = -EOPNOTSUPP;
goto out;
}
- rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+ rc = selinux_getxattr(root_inode, root, NULL, 0);
if (rc < 0 && rc != -ENODATA) {
if (rc == -EOPNOTSUPP)
printk(KERN_WARNING "SELinux: (dev %s, type "
@@ -1270,6 +1270,19 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
return rc;
}
+static int selinux_getxattr(struct inode *inode, struct dentry *dentry,
+ char *context, unsigned int len)
+{
+ if (dentry)
+ return inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
+ context, len);
+ else if (inode && inode->i_op->igetxattr)
+ return inode->i_op->igetxattr(inode, XATTR_NAME_SELINUX,
+ context, len);
+ else
+ return -EOPNOTSUPP;
+}
+
/* The inode's security attributes must be initialized before first use. */
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
{
@@ -1282,11 +1295,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 == 1)
goto out;
mutex_lock(&isec->lock);
- if (isec->initialized)
+ if (isec->initialized == 1)
goto out_unlock;
sbsec = inode->i_sb->s_security;
@@ -1319,7 +1332,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/* Called from selinux_complete_init, try to find a dentry. */
dentry = d_find_alias(inode);
}
- if (!dentry) {
+ if (!dentry && !inode->i_op->igetxattr) {
/*
* this is can be hit on boot when a file is accessed
* before the policy is loaded. When we load policy we
@@ -1340,14 +1353,12 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock;
}
context[len] = '\0';
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
- context, len);
+ rc = selinux_getxattr(inode, dentry, context, len);
if (rc == -ERANGE) {
kfree(context);
/* Need a larger buffer. Query for the right size. */
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
- NULL, 0);
+ rc = selinux_getxattr(inode, dentry, NULL, 0);
if (rc < 0) {
dput(dentry);
goto out_unlock;
@@ -1360,9 +1371,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock;
}
context[len] = '\0';
- rc = inode->i_op->getxattr(dentry,
- XATTR_NAME_SELINUX,
- context, len);
+ rc = selinux_getxattr(inode, dentry, context, len);
}
dput(dentry);
if (rc < 0) {
@@ -1614,6 +1623,12 @@ static int inode_has_perm(const struct cred *cred,
sid = cred_sid(cred);
isec = inode->i_security;
+ if (isec->initialized == 2) {
+ inode_doinit(inode);
+ if (isec->initialized == 2)
+ return -EACCES;
+ }
+
return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
}
@@ -5715,6 +5730,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 = 2;
+ mutex_unlock(&isec->lock);
+}
+
/*
* called with inode->i_mutex locked
*/
@@ -5946,6 +5970,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),
@@ -46,7 +46,8 @@ struct inode_security_struct {
u32 task_sid; /* SID of creating task */
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
- unsigned char initialized; /* initialization flag */
+ unsigned char initialized; /* 0: not initialized, 1: initialized,
+ 2: needs revalidation */
struct mutex lock;
};
Add the LSM and SELinux infrastructure for invalidating inode->i_security and for re-initializing it from inode_has_perm when necessary. In inode_has_perm, we don't have access to a dentry, so file systems must implement iop->igetxattr in order to be able to invalidate security labels. Alternatively, we could add an inode operation called by inode_has_perm to revalidate the security label of the inode on each call, but inode_has_perm is called so frequently that the overhead seems excessive. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> --- include/linux/lsm_hooks.h | 6 +++++ include/linux/security.h | 5 +++++ security/selinux/hooks.c | 47 ++++++++++++++++++++++++++++++--------- security/selinux/include/objsec.h | 3 ++- 4 files changed, 49 insertions(+), 12 deletions(-)