From: Mikhail K. <vie...@vi...> - 2017-03-21 00:02:17
|
This patch provide additional directory tree protections routines: 1. Symlink's target path will be used for parent directory label calculation; 2. readlink()/follow_link() calls will be covered by EVM directory tree protections. Signed-off-by: Mikhail Kurinnoi <vie...@vi...> include/linux/evm.h | 14 +++++++++++ security/integrity/evm/evm_crypto.c | 42 +++++++++++++++++++++++++++++++++ security/integrity/evm/evm_main.c | 47 +++++++++++++++++++++++++++++++++++++ security/security.c | 14 +++++++++-- 4 files changed, 115 insertions(+), 2 deletions(-) diff --git a/include/linux/evm.h b/include/linux/evm.h index ee6102b..9aa19df 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -63,6 +63,9 @@ extern void evm_inode_post_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); +extern int evm_inode_readlink(struct dentry *dentry); +extern int evm_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu); extern int evm_inode_init_security(struct inode *inode, const struct xattr *xattr_array, struct xattr *evm); @@ -228,6 +231,17 @@ evm_inode_post_rename(struct inode *old_dir, struct dentry *old_dentry, return; } +static int evm_inode_readlink(struct dentry *dentry) +{ + return 0; +} + +static int evm_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu) +{ + return 0; +} + static inline int evm_inode_init_security(struct inode *inode, const struct xattr *xattr_array, struct xattr *evm) diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 608852f..db1e35a 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -172,6 +172,44 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, crypto_shash_final(desc, digest); } +/* + * Add symlink target path to message digest. + * This function expects the caller to not lock the symlink inode's i_mutex. + */ +static int hmac_add_misc_lnk(struct shash_desc *desc, const char *lnkname, + int lnknamelen, struct dentry *dir) +{ + struct dentry *dentry; + struct inode *inode; + int ret = 0; + + dentry = lookup_one_len(lnkname, dir, lnknamelen); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + inode = d_backing_inode(dentry); + inode_lock(inode); + + if (inode->i_link) { + crypto_shash_update(desc, inode->i_link, + strlen(inode->i_link)); + } else if (d_is_symlink(dentry)) { + DEFINE_DELAYED_CALL(done); + const char *link = + inode->i_op->get_link(dentry, inode, &done); + if (IS_ERR(link)) { + ret = PTR_ERR(link); + goto out; + } + crypto_shash_update(desc, link, strlen(link)); + do_delayed_call(&done); + } +out: + inode_unlock(inode); + dput(dentry); + return ret; +} + struct evm_dir_ctx { struct dir_context ctx; struct shash_desc *desc; @@ -201,6 +239,10 @@ static int evm_dir_list(struct dir_context *__ctx, const char *name, crypto_shash_update(ctx->desc, (const u8 *)&hmac_misc_child, sizeof(hmac_misc_child)); + /* Add symlink target path to message digest. */ + if (d_type == DT_LNK) + return hmac_add_misc_lnk(ctx->desc, name, namelen, ctx->dir); + return 0; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 87b7b85..a151c40 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -830,6 +830,53 @@ void evm_inode_post_rename(struct inode *old_dir, struct dentry *old_dentry, } } +/** + * evm_inode_readlink - verify parents integrity + * @dentry: pointer to the affected dentry + * + * This function expects the caller to not lock the affected or parent + * dir inode's i_mutex. + */ +int evm_inode_readlink(struct dentry *dentry) +{ + struct inode *parent_inode = d_backing_inode(dentry->d_parent); + int ret; + + /* early check, in order to reduce useless inodes locks */ + if (!evm_must_protectfs(dentry)) + return 0; + + inode_lock(parent_inode); + ret = evm_verify_parent(dentry); + inode_unlock(parent_inode); + + return ret; +} + +/** + * evm_inode_follow_link - verify parents integrity + * @dentry: pointer to the affected dentry + * + * This function expects the caller to not lock the affected or parent + * dir inode's i_mutex. + */ +int evm_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu) +{ + struct inode *parent_inode = d_backing_inode(dentry->d_parent); + int ret; + + /* early check, in order to reduce useless inodes locks */ + if (!evm_must_protectfs(dentry)) + return 0; + + inode_lock(parent_inode); + ret = evm_verify_parent(dentry); + inode_unlock(parent_inode); + + return ret; +} + /* * evm_inode_init_security - initializes security.evm */ diff --git a/security/security.c b/security/security.c index 5eeed4c..463f71c 100644 --- a/security/security.c +++ b/security/security.c @@ -663,17 +663,27 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, int security_inode_readlink(struct dentry *dentry) { + int ret; + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; - return call_int_hook(inode_readlink, 0, dentry); + ret = call_int_hook(inode_readlink, 0, dentry); + if (ret) + return ret; + return evm_inode_readlink(dentry); } int security_inode_follow_link(struct dentry *dentry, struct inode *inode, bool rcu) { + int ret; + if (unlikely(IS_PRIVATE(inode))) return 0; - return call_int_hook(inode_follow_link, 0, dentry, inode, rcu); + ret = call_int_hook(inode_follow_link, 0, dentry, inode, rcu); + if (ret) + return ret; + return evm_inode_follow_link(dentry, inode, rcu); } int security_inode_permission(struct inode *inode, int mask) |