Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

evm: Store and detect metadata inode attributes changes

On stacked filesystem the metadata inode may be different than the one
file data inode and therefore changes to it need to be detected
independently. Therefore, store the i_version, device number, and inode
number associated with the file metadata inode.

Implement a function to detect changes to the inode and if a change is
detected reset the evm_status. This function will be called by IMA when
IMA detects that the metadata inode is different from the file's inode.

Co-developed-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>

authored by

Stefan Berger and committed by
Mimi Zohar
a652aa59 309e2b77

+57 -10
+8
include/linux/evm.h
··· 26 26 extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, 27 27 int buffer_size, char type, 28 28 bool canonical_fmt); 29 + extern bool evm_metadata_changed(struct inode *inode, 30 + struct inode *metadata_inode); 29 31 #ifdef CONFIG_FS_POSIX_ACL 30 32 extern int posix_xattr_acl(const char *xattrname); 31 33 #else ··· 76 74 bool canonical_fmt) 77 75 { 78 76 return -EOPNOTSUPP; 77 + } 78 + 79 + static inline bool evm_metadata_changed(struct inode *inode, 80 + struct inode *metadata_inode) 81 + { 82 + return false; 79 83 } 80 84 81 85 #endif /* CONFIG_EVM */
+4 -2
security/integrity/evm/evm.h
··· 39 39 struct evm_iint_cache { 40 40 unsigned long flags; 41 41 enum integrity_status evm_status:4; 42 + struct integrity_inode_attributes metadata_inode; 42 43 }; 43 44 44 45 extern struct lsm_blob_sizes evm_blob_sizes; ··· 75 74 size_t req_xattr_value_len); 76 75 int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, 77 76 const char *req_xattr_value, 78 - size_t req_xattr_value_len, struct evm_digest *data); 77 + size_t req_xattr_value_len, struct evm_digest *data, 78 + struct evm_iint_cache *iint); 79 79 int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, 80 80 const char *req_xattr_value, 81 81 size_t req_xattr_value_len, char type, 82 - struct evm_digest *data); 82 + struct evm_digest *data, struct evm_iint_cache *iint); 83 83 int evm_init_hmac(struct inode *inode, const struct xattr *xattrs, 84 84 char *hmac_val); 85 85 int evm_init_secfs(void);
+17 -6
security/integrity/evm/evm_crypto.c
··· 221 221 const char *req_xattr_name, 222 222 const char *req_xattr_value, 223 223 size_t req_xattr_value_len, 224 - uint8_t type, struct evm_digest *data) 224 + uint8_t type, struct evm_digest *data, 225 + struct evm_iint_cache *iint) 225 226 { 226 227 struct inode *inode = d_inode(d_real(dentry, D_REAL_METADATA)); 227 228 struct xattr_list *xattr; ··· 232 231 int error; 233 232 int size, user_space_size; 234 233 bool ima_present = false; 234 + u64 i_version = 0; 235 235 236 236 if (!(inode->i_opflags & IOP_XATTR) || 237 237 inode->i_sb->s_user_ns != &init_user_ns) ··· 296 294 } 297 295 hmac_add_misc(desc, inode, type, data->digest); 298 296 297 + if (inode != d_backing_inode(dentry) && iint) { 298 + if (IS_I_VERSION(inode)) 299 + i_version = inode_query_iversion(inode); 300 + integrity_inode_attrs_store(&iint->metadata_inode, i_version, 301 + inode); 302 + } 303 + 299 304 /* Portable EVM signatures must include an IMA hash */ 300 305 if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present) 301 306 error = -EPERM; ··· 314 305 315 306 int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, 316 307 const char *req_xattr_value, size_t req_xattr_value_len, 317 - struct evm_digest *data) 308 + struct evm_digest *data, struct evm_iint_cache *iint) 318 309 { 319 310 return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, 320 - req_xattr_value_len, EVM_XATTR_HMAC, data); 311 + req_xattr_value_len, EVM_XATTR_HMAC, data, 312 + iint); 321 313 } 322 314 323 315 int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, 324 316 const char *req_xattr_value, size_t req_xattr_value_len, 325 - char type, struct evm_digest *data) 317 + char type, struct evm_digest *data, struct evm_iint_cache *iint) 326 318 { 327 319 return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, 328 - req_xattr_value_len, type, data); 320 + req_xattr_value_len, type, data, iint); 329 321 } 330 322 331 323 static int evm_is_immutable(struct dentry *dentry, struct inode *inode) ··· 367 357 const char *xattr_value, size_t xattr_value_len) 368 358 { 369 359 struct inode *inode = d_backing_inode(dentry); 360 + struct evm_iint_cache *iint = evm_iint_inode(inode); 370 361 struct evm_digest data; 371 362 int rc = 0; 372 363 ··· 383 372 384 373 data.hdr.algo = HASH_ALGO_SHA1; 385 374 rc = evm_calc_hmac(dentry, xattr_name, xattr_value, 386 - xattr_value_len, &data); 375 + xattr_value_len, &data, iint); 387 376 if (rc == 0) { 388 377 data.hdr.xattr.sha1.type = EVM_XATTR_HMAC; 389 378 rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry,
+28 -2
security/integrity/evm/evm_main.c
··· 226 226 227 227 digest.hdr.algo = HASH_ALGO_SHA1; 228 228 rc = evm_calc_hmac(dentry, xattr_name, xattr_value, 229 - xattr_value_len, &digest); 229 + xattr_value_len, &digest, iint); 230 230 if (rc) 231 231 break; 232 232 rc = crypto_memneq(xattr_data->data, digest.digest, ··· 247 247 hdr = (struct signature_v2_hdr *)xattr_data; 248 248 digest.hdr.algo = hdr->hash_algo; 249 249 rc = evm_calc_hash(dentry, xattr_name, xattr_value, 250 - xattr_value_len, xattr_data->type, &digest); 250 + xattr_value_len, xattr_data->type, &digest, 251 + iint); 251 252 if (rc) 252 253 break; 253 254 rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, ··· 732 731 iint = evm_iint_inode(inode); 733 732 if (iint) 734 733 iint->evm_status = INTEGRITY_UNKNOWN; 734 + } 735 + 736 + /** 737 + * evm_metadata_changed: Detect changes to the metadata 738 + * @inode: a file's inode 739 + * @metadata_inode: metadata inode 740 + * 741 + * On a stacked filesystem detect whether the metadata has changed. If this is 742 + * the case reset the evm_status associated with the inode that represents the 743 + * file. 744 + */ 745 + bool evm_metadata_changed(struct inode *inode, struct inode *metadata_inode) 746 + { 747 + struct evm_iint_cache *iint = evm_iint_inode(inode); 748 + bool ret = false; 749 + 750 + if (iint) { 751 + ret = (!IS_I_VERSION(metadata_inode) || 752 + integrity_inode_attrs_changed(&iint->metadata_inode, 753 + metadata_inode)); 754 + if (ret) 755 + iint->evm_status = INTEGRITY_UNKNOWN; 756 + } 757 + 758 + return ret; 735 759 } 736 760 737 761 /**