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@xxxxxxxxxx> --- 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 --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; }; -- 2.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html