diff --git a/fs/attr.c b/fs/attr.c index 322e5e887ecea2c2e5bc85705e91cb9375a2ba00..9a632198c07c4ba74085dc68c1c8c7f457e6fb8d 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -341,7 +341,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de if (!error) { fsnotify_change(dentry, ia_valid); ima_inode_post_setattr(dentry); - evm_inode_post_setattr(dentry, ia_valid); + evm_inode_post_setattr(dentry, ia_valid, evm_error); } return error; diff --git a/fs/xattr.c b/fs/xattr.c index a0e4e77f78b1764328a14d9585fe7992b13abed6..64ebf7b7fe8972c7a8e2f2a56fc00bd7fecf0e06 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -150,24 +150,8 @@ __vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, } EXPORT_SYMBOL(__vfs_setxattr); -/** - * __vfs_setxattr_noperm - perform setxattr operation without performing - * permission checks. - * - * @dentry - object to perform setxattr on - * @name - xattr name to set - * @value - value to set @name to - * @size - size of @value - * @flags - flags to pass into filesystem operations - * - * returns the result of the internal setxattr or setsecurity operations. - * - * This function requires the caller to lock the inode's i_mutex before it - * is executed. It also assumes that the caller will make the appropriate - * permission checks. - */ -int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) +static int __vfs_setxattr_noperm_evm(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int evm_error) { struct inode *inode = dentry->d_inode; int error = -EAGAIN; @@ -182,7 +166,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, fsnotify_xattr(dentry); security_inode_post_setxattr(dentry, name, value, size, flags); - evm_inode_post_setxattr(dentry, name, value, size); + evm_inode_post_setxattr(dentry, name, value, size, + evm_error); } } else { if (unlikely(is_bad_inode(inode))) @@ -204,6 +189,28 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, return error; } +/** + * __vfs_setxattr_noperm - perform setxattr operation without performing + * permission checks. + * + * @dentry - object to perform setxattr on + * @name - xattr name to set + * @value - value to set @name to + * @size - size of @value + * @flags - flags to pass into filesystem operations + * + * returns the result of the internal setxattr or setsecurity operations. + * + * This function requires the caller to lock the inode's i_mutex before it + * is executed. It also assumes that the caller will make the appropriate + * permission checks. + */ +int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + return __vfs_setxattr_noperm_evm(dentry, name, value, size, flags, 0); +} + /** * __vfs_setxattr_locked: set an extended attribute while holding the inode * lock @@ -242,7 +249,8 @@ __vfs_setxattr_locked(struct dentry *dentry, const char *name, if (error) goto out; - error = __vfs_setxattr_noperm(dentry, name, value, size, flags); + error = __vfs_setxattr_noperm_evm(dentry, name, value, size, flags, + evm_error); out: return error; @@ -459,7 +467,7 @@ __vfs_removexattr_locked(struct dentry *dentry, const char *name, if (!error) { fsnotify_xattr(dentry); - evm_inode_post_removexattr(dentry, name); + evm_inode_post_removexattr(dentry, name, evm_error); } out: diff --git a/include/linux/evm.h b/include/linux/evm.h index 8302bc29bb358abca818d7b16ba0e1170b8924d6..cc23d2248d3edb0bca9f58b96867292e6ceeac3e 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -22,16 +22,19 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry, size_t xattr_value_len, struct integrity_iint_cache *iint); extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr); -extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid); +extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid, + int evm_pre_error); extern int evm_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size); extern void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, - size_t xattr_value_len); + size_t xattr_value_len, + int evm_pre_error); extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name); extern void evm_inode_post_removexattr(struct dentry *dentry, - const char *xattr_name); + const char *xattr_name, + int evm_pre_error); extern int evm_inode_init_security(struct inode *inode, const struct xattr *xattr_array, struct xattr *evm); @@ -66,7 +69,8 @@ static inline int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) return 0; } -static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) +static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid, + int evm_pre_error) { return; } @@ -80,7 +84,8 @@ static inline int evm_inode_setxattr(struct dentry *dentry, const char *name, static inline void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, - size_t xattr_value_len) + size_t xattr_value_len, + int evm_pre_error) { return; } @@ -92,7 +97,8 @@ static inline int evm_inode_removexattr(struct dentry *dentry, } static inline void evm_inode_post_removexattr(struct dentry *dentry, - const char *xattr_name) + const char *xattr_name, + int evm_pre_error) { return; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 138c0ab419faa3732704a145709297a4262aa6e3..f0b5153341714c3314d3a45c9907fb29af182b71 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -576,6 +576,7 @@ static void evm_reset_status(struct inode *inode, int bit) * @xattr_name: pointer to the affected extended attribute name * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length + * @evm_pre_error: error returned by evm_inode_setxattr() * * Update the HMAC stored in 'security.evm' to reflect the change. * @@ -584,7 +585,8 @@ static void evm_reset_status(struct inode *inode, int bit) * i_mutex lock. */ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, - const void *xattr_value, size_t xattr_value_len) + const void *xattr_value, size_t xattr_value_len, + int evm_pre_error) { int is_evm = !strcmp(xattr_name, XATTR_NAME_EVM); @@ -597,6 +599,9 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, if (is_evm) return; + if (evm_pre_error) + return; + evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); } @@ -604,13 +609,15 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, * evm_inode_post_removexattr - update 'security.evm' after removing the xattr * @dentry: pointer to the affected dentry * @xattr_name: pointer to the affected extended attribute name + * @evm_pre_error: error returned by evm_inode_removexattr() * * Update the HMAC stored in 'security.evm' to reflect removal of the xattr. * * No need to take the i_mutex lock here, as this function is called from * vfs_removexattr() which takes the i_mutex. */ -void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) +void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name, + int evm_pre_error) { int is_evm = !strcmp(xattr_name, XATTR_NAME_EVM); @@ -622,6 +629,9 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) if (is_evm) return; + if (evm_pre_error) + return; + evm_update_evmxattr(dentry, xattr_name, NULL, 0); } @@ -681,6 +691,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) * evm_inode_post_setattr - update 'security.evm' after modifying metadata * @dentry: pointer to the affected dentry * @ia_valid: for the UID and GID status + * @evm_pre_error: error returned by evm_inode_setattr() * * For now, update the HMAC stored in 'security.evm' to reflect UID/GID * changes. @@ -688,13 +699,17 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) * This function is called from notify_change(), which expects the caller * to lock the inode's i_mutex. */ -void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) +void evm_inode_post_setattr(struct dentry *dentry, int ia_valid, + int evm_pre_error) { if (!evm_key_loaded()) return; evm_reset_status(dentry->d_inode, IMA_CHANGE_ATTR); + if (evm_pre_error) + return; + if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) evm_update_evmxattr(dentry, NULL, NULL, 0); }