Skip to content

Commit

Permalink
evm: Store and detect metadata inode attributes changes
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Stefan Berger authored and Mimi Zohar committed Apr 9, 2024
1 parent 309e2b7 commit a652aa5
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 10 deletions.
8 changes: 8 additions & 0 deletions include/linux/evm.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);
extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
int buffer_size, char type,
bool canonical_fmt);
extern bool evm_metadata_changed(struct inode *inode,
struct inode *metadata_inode);
#ifdef CONFIG_FS_POSIX_ACL
extern int posix_xattr_acl(const char *xattrname);
#else
Expand Down Expand Up @@ -76,5 +78,11 @@ static inline int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
return -EOPNOTSUPP;
}

static inline bool evm_metadata_changed(struct inode *inode,
struct inode *metadata_inode)
{
return false;
}

#endif /* CONFIG_EVM */
#endif /* LINUX_EVM_H */
6 changes: 4 additions & 2 deletions security/integrity/evm/evm.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct xattr_list {
struct evm_iint_cache {
unsigned long flags;
enum integrity_status evm_status:4;
struct integrity_inode_attributes metadata_inode;
};

extern struct lsm_blob_sizes evm_blob_sizes;
Expand Down Expand Up @@ -74,11 +75,12 @@ int evm_update_evmxattr(struct dentry *dentry,
size_t req_xattr_value_len);
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, struct evm_digest *data);
size_t req_xattr_value_len, struct evm_digest *data,
struct evm_iint_cache *iint);
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, char type,
struct evm_digest *data);
struct evm_digest *data, struct evm_iint_cache *iint);
int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val);
int evm_init_secfs(void);
Expand Down
23 changes: 17 additions & 6 deletions security/integrity/evm/evm_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len,
uint8_t type, struct evm_digest *data)
uint8_t type, struct evm_digest *data,
struct evm_iint_cache *iint)
{
struct inode *inode = d_inode(d_real(dentry, D_REAL_METADATA));
struct xattr_list *xattr;
Expand All @@ -231,6 +232,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
int error;
int size, user_space_size;
bool ima_present = false;
u64 i_version = 0;

if (!(inode->i_opflags & IOP_XATTR) ||
inode->i_sb->s_user_ns != &init_user_ns)
Expand Down Expand Up @@ -294,6 +296,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
}
hmac_add_misc(desc, inode, type, data->digest);

if (inode != d_backing_inode(dentry) && iint) {
if (IS_I_VERSION(inode))
i_version = inode_query_iversion(inode);
integrity_inode_attrs_store(&iint->metadata_inode, i_version,
inode);
}

/* Portable EVM signatures must include an IMA hash */
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
error = -EPERM;
Expand All @@ -305,18 +314,19 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,

int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
struct evm_digest *data)
struct evm_digest *data, struct evm_iint_cache *iint)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
req_xattr_value_len, EVM_XATTR_HMAC, data);
req_xattr_value_len, EVM_XATTR_HMAC, data,
iint);
}

int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
char type, struct evm_digest *data)
char type, struct evm_digest *data, struct evm_iint_cache *iint)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
req_xattr_value_len, type, data);
req_xattr_value_len, type, data, iint);
}

static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
Expand Down Expand Up @@ -357,6 +367,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len)
{
struct inode *inode = d_backing_inode(dentry);
struct evm_iint_cache *iint = evm_iint_inode(inode);
struct evm_digest data;
int rc = 0;

Expand All @@ -372,7 +383,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,

data.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &data);
xattr_value_len, &data, iint);
if (rc == 0) {
data.hdr.xattr.sha1.type = EVM_XATTR_HMAC;
rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry,
Expand Down
30 changes: 28 additions & 2 deletions security/integrity/evm/evm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,

digest.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &digest);
xattr_value_len, &digest, iint);
if (rc)
break;
rc = crypto_memneq(xattr_data->data, digest.digest,
Expand All @@ -247,7 +247,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
hdr = (struct signature_v2_hdr *)xattr_data;
digest.hdr.algo = hdr->hash_algo;
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
xattr_value_len, xattr_data->type, &digest);
xattr_value_len, xattr_data->type, &digest,
iint);
if (rc)
break;
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
Expand Down Expand Up @@ -733,6 +734,31 @@ static void evm_reset_status(struct inode *inode)
iint->evm_status = INTEGRITY_UNKNOWN;
}

/**
* evm_metadata_changed: Detect changes to the metadata
* @inode: a file's inode
* @metadata_inode: metadata inode
*
* On a stacked filesystem detect whether the metadata has changed. If this is
* the case reset the evm_status associated with the inode that represents the
* file.
*/
bool evm_metadata_changed(struct inode *inode, struct inode *metadata_inode)
{
struct evm_iint_cache *iint = evm_iint_inode(inode);
bool ret = false;

if (iint) {
ret = (!IS_I_VERSION(metadata_inode) ||
integrity_inode_attrs_changed(&iint->metadata_inode,
metadata_inode));
if (ret)
iint->evm_status = INTEGRITY_UNKNOWN;
}

return ret;
}

/**
* evm_revalidate_status - report whether EVM status re-validation is necessary
* @xattr_name: pointer to the affected extended attribute name
Expand Down

0 comments on commit a652aa5

Please sign in to comment.