diff mbox

[RFC,10/11] selinux: Allow to invalidate an inode's security label

Message ID 1440094798-1411-11-git-send-email-agruenba@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andreas Grünbacher Aug. 20, 2015, 6:19 p.m. UTC
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(-)
diff mbox

Patch

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 9429f05..9fe99d6 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -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;
diff --git a/include/linux/security.h b/include/linux/security.h
index 79d85dd..f50587d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -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;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 564079c..e80fcda 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -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),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 81fa718..5b13732 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -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;
 };