Commit 353ad6c0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'integrity-v6.10' of ssh://ra.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity

Pull integrity updates from Mimi Zohar:
 "Two IMA changes, one EVM change, a use after free bug fix, and a code
  cleanup to address "-Wflex-array-member-not-at-end" warnings:

   - The existing IMA {ascii, binary}_runtime_measurements lists include
     a hard coded SHA1 hash. To address this limitation, define per TPM
     enabled hash algorithm {ascii, binary}_runtime_measurements lists

   - Close an IMA integrity init_module syscall measurement gap by
     defining a new critical-data record

   - Enable (partial) EVM support on stacked filesystems (overlayfs).
     Only EVM portable & immutable file signatures are copied up, since
     they do not contain filesystem specific metadata"

* tag 'integrity-v6.10' of ssh://ra.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  ima: add crypto agility support for template-hash algorithm
  evm: Rename is_unsupported_fs to is_unsupported_hmac_fs
  fs: Rename SB_I_EVM_UNSUPPORTED to SB_I_EVM_HMAC_UNSUPPORTED
  evm: Enforce signatures on unsupported filesystem for EVM_INIT_X509
  ima: re-evaluate file integrity on file metadata change
  evm: Store and detect metadata inode attributes changes
  ima: Move file-change detection variables into new structure
  evm: Use the metadata inode to calculate metadata hash
  evm: Implement per signature type decision in security_inode_copy_up_xattr
  security: allow finer granularity in permitting copy-up of security xattrs
  ima: Rename backing_inode to real_inode
  integrity: Avoid -Wflex-array-member-not-at-end warnings
  ima: define an init_module critical data record
  ima: Fix use-after-free on a dentry's dname.name
parents ccae19c6 9fa8e762
...@@ -114,7 +114,7 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct de ...@@ -114,7 +114,7 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct de
if (ovl_is_private_xattr(sb, name)) if (ovl_is_private_xattr(sb, name))
continue; continue;
error = security_inode_copy_up_xattr(name); error = security_inode_copy_up_xattr(old, name);
if (error < 0 && error != -EOPNOTSUPP) if (error < 0 && error != -EOPNOTSUPP)
break; break;
if (error == 1) { if (error == 1) {
......
...@@ -1460,7 +1460,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) ...@@ -1460,7 +1460,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
* lead to unexpected results. * lead to unexpected results.
*/ */
sb->s_iflags |= SB_I_NOUMASK; sb->s_iflags |= SB_I_NOUMASK;
sb->s_iflags |= SB_I_EVM_UNSUPPORTED; sb->s_iflags |= SB_I_EVM_HMAC_UNSUPPORTED;
err = -ENOMEM; err = -ENOMEM;
root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe); root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe);
......
...@@ -26,6 +26,8 @@ extern int evm_protected_xattr_if_enabled(const char *req_xattr_name); ...@@ -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, extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
int buffer_size, char type, int buffer_size, char type,
bool canonical_fmt); bool canonical_fmt);
extern bool evm_metadata_changed(struct inode *inode,
struct inode *metadata_inode);
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
extern int posix_xattr_acl(const char *xattrname); extern int posix_xattr_acl(const char *xattrname);
#else #else
...@@ -76,5 +78,11 @@ static inline int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, ...@@ -76,5 +78,11 @@ static inline int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline bool evm_metadata_changed(struct inode *inode,
struct inode *metadata_inode)
{
return false;
}
#endif /* CONFIG_EVM */ #endif /* CONFIG_EVM */
#endif /* LINUX_EVM_H */ #endif /* LINUX_EVM_H */
...@@ -1174,7 +1174,7 @@ extern int send_sigurg(struct fown_struct *fown); ...@@ -1174,7 +1174,7 @@ extern int send_sigurg(struct fown_struct *fown);
#define SB_I_USERNS_VISIBLE 0x00000010 /* fstype already mounted */ #define SB_I_USERNS_VISIBLE 0x00000010 /* fstype already mounted */
#define SB_I_IMA_UNVERIFIABLE_SIGNATURE 0x00000020 #define SB_I_IMA_UNVERIFIABLE_SIGNATURE 0x00000020
#define SB_I_UNTRUSTED_MOUNTER 0x00000040 #define SB_I_UNTRUSTED_MOUNTER 0x00000040
#define SB_I_EVM_UNSUPPORTED 0x00000080 #define SB_I_EVM_HMAC_UNSUPPORTED 0x00000080
#define SB_I_SKIP_SYNC 0x00000100 /* Skip superblock at global sync */ #define SB_I_SKIP_SYNC 0x00000100 /* Skip superblock at global sync */
#define SB_I_PERSB_BDI 0x00000200 /* has a per-sb bdi */ #define SB_I_PERSB_BDI 0x00000200 /* has a per-sb bdi */
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define _LINUX_INTEGRITY_H #define _LINUX_INTEGRITY_H
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/iversion.h>
enum integrity_status { enum integrity_status {
INTEGRITY_PASS = 0, INTEGRITY_PASS = 0,
...@@ -28,4 +29,37 @@ static inline void integrity_load_keys(void) ...@@ -28,4 +29,37 @@ static inline void integrity_load_keys(void)
} }
#endif /* CONFIG_INTEGRITY */ #endif /* CONFIG_INTEGRITY */
/* An inode's attributes for detection of changes */
struct integrity_inode_attributes {
u64 version; /* track inode changes */
unsigned long ino;
dev_t dev;
};
/*
* On stacked filesystems the i_version alone is not enough to detect file data
* or metadata change. Additional metadata is required.
*/
static inline void
integrity_inode_attrs_store(struct integrity_inode_attributes *attrs,
u64 i_version, const struct inode *inode)
{
attrs->version = i_version;
attrs->dev = inode->i_sb->s_dev;
attrs->ino = inode->i_ino;
}
/*
* On stacked filesystems detect whether the inode or its content has changed.
*/
static inline bool
integrity_inode_attrs_changed(const struct integrity_inode_attributes *attrs,
const struct inode *inode)
{
return (inode->i_sb->s_dev != attrs->dev ||
inode->i_ino != attrs->ino ||
!inode_eq_iversion(inode, attrs->version));
}
#endif /* _LINUX_INTEGRITY_H */ #endif /* _LINUX_INTEGRITY_H */
...@@ -176,7 +176,8 @@ LSM_HOOK(int, 0, inode_listsecurity, struct inode *inode, char *buffer, ...@@ -176,7 +176,8 @@ LSM_HOOK(int, 0, inode_listsecurity, struct inode *inode, char *buffer,
size_t buffer_size) size_t buffer_size)
LSM_HOOK(void, LSM_RET_VOID, inode_getsecid, struct inode *inode, u32 *secid) LSM_HOOK(void, LSM_RET_VOID, inode_getsecid, struct inode *inode, u32 *secid)
LSM_HOOK(int, 0, inode_copy_up, struct dentry *src, struct cred **new) LSM_HOOK(int, 0, inode_copy_up, struct dentry *src, struct cred **new)
LSM_HOOK(int, -EOPNOTSUPP, inode_copy_up_xattr, const char *name) LSM_HOOK(int, -EOPNOTSUPP, inode_copy_up_xattr, struct dentry *src,
const char *name)
LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir, LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
struct kernfs_node *kn) struct kernfs_node *kn)
LSM_HOOK(int, 0, file_permission, struct file *file, int mask) LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
......
...@@ -398,7 +398,7 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void ...@@ -398,7 +398,7 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void
int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size); int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size);
void security_inode_getsecid(struct inode *inode, u32 *secid); void security_inode_getsecid(struct inode *inode, u32 *secid);
int security_inode_copy_up(struct dentry *src, struct cred **new); int security_inode_copy_up(struct dentry *src, struct cred **new);
int security_inode_copy_up_xattr(const char *name); int security_inode_copy_up_xattr(struct dentry *src, const char *name);
int security_kernfs_init_security(struct kernfs_node *kn_dir, int security_kernfs_init_security(struct kernfs_node *kn_dir,
struct kernfs_node *kn); struct kernfs_node *kn);
int security_file_permission(struct file *file, int mask); int security_file_permission(struct file *file, int mask);
...@@ -1016,7 +1016,7 @@ static inline int security_kernfs_init_security(struct kernfs_node *kn_dir, ...@@ -1016,7 +1016,7 @@ static inline int security_kernfs_init_security(struct kernfs_node *kn_dir,
return 0; return 0;
} }
static inline int security_inode_copy_up_xattr(const char *name) static inline int security_inode_copy_up_xattr(struct dentry *src, const char *name)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -39,6 +39,7 @@ struct xattr_list { ...@@ -39,6 +39,7 @@ struct xattr_list {
struct evm_iint_cache { struct evm_iint_cache {
unsigned long flags; unsigned long flags;
enum integrity_status evm_status:4; enum integrity_status evm_status:4;
struct integrity_inode_attributes metadata_inode;
}; };
extern struct lsm_blob_sizes evm_blob_sizes; extern struct lsm_blob_sizes evm_blob_sizes;
...@@ -61,7 +62,7 @@ extern int evm_hmac_attrs; ...@@ -61,7 +62,7 @@ extern int evm_hmac_attrs;
extern struct list_head evm_config_xattrnames; extern struct list_head evm_config_xattrnames;
struct evm_digest { struct evm_digest {
struct ima_digest_data hdr; struct ima_digest_data_hdr hdr;
char digest[IMA_MAX_DIGEST_SIZE]; char digest[IMA_MAX_DIGEST_SIZE];
} __packed; } __packed;
...@@ -74,11 +75,12 @@ int evm_update_evmxattr(struct dentry *dentry, ...@@ -74,11 +75,12 @@ int evm_update_evmxattr(struct dentry *dentry,
size_t req_xattr_value_len); size_t req_xattr_value_len);
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, 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, int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, const char *req_xattr_value,
size_t req_xattr_value_len, char type, 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, int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val); char *hmac_val);
int evm_init_secfs(void); int evm_init_secfs(void);
......
...@@ -221,9 +221,10 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -221,9 +221,10 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
const char *req_xattr_name, const char *req_xattr_name,
const char *req_xattr_value, const char *req_xattr_value,
size_t req_xattr_value_len, 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_backing_inode(dentry); struct inode *inode = d_inode(d_real(dentry, D_REAL_METADATA));
struct xattr_list *xattr; struct xattr_list *xattr;
struct shash_desc *desc; struct shash_desc *desc;
size_t xattr_size = 0; size_t xattr_size = 0;
...@@ -231,6 +232,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -231,6 +232,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
int error; int error;
int size, user_space_size; int size, user_space_size;
bool ima_present = false; bool ima_present = false;
u64 i_version = 0;
if (!(inode->i_opflags & IOP_XATTR) || if (!(inode->i_opflags & IOP_XATTR) ||
inode->i_sb->s_user_ns != &init_user_ns) inode->i_sb->s_user_ns != &init_user_ns)
...@@ -294,6 +296,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -294,6 +296,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
} }
hmac_add_misc(desc, inode, type, data->digest); 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 */ /* Portable EVM signatures must include an IMA hash */
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present) if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
error = -EPERM; error = -EPERM;
...@@ -305,18 +314,19 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ...@@ -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, int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len, 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, 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, int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len, 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, 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) static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
...@@ -357,6 +367,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, ...@@ -357,6 +367,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len) const char *xattr_value, size_t xattr_value_len)
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
struct evm_iint_cache *iint = evm_iint_inode(inode);
struct evm_digest data; struct evm_digest data;
int rc = 0; int rc = 0;
...@@ -372,7 +383,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, ...@@ -372,7 +383,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
data.hdr.algo = HASH_ALGO_SHA1; data.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value, rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &data); xattr_value_len, &data, iint);
if (rc == 0) { if (rc == 0) {
data.hdr.xattr.sha1.type = EVM_XATTR_HMAC; data.hdr.xattr.sha1.type = EVM_XATTR_HMAC;
rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry,
......
...@@ -151,11 +151,11 @@ static int evm_find_protected_xattrs(struct dentry *dentry) ...@@ -151,11 +151,11 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
return count; return count;
} }
static int is_unsupported_fs(struct dentry *dentry) static int is_unsupported_hmac_fs(struct dentry *dentry)
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
if (inode->i_sb->s_iflags & SB_I_EVM_UNSUPPORTED) { if (inode->i_sb->s_iflags & SB_I_EVM_HMAC_UNSUPPORTED) {
pr_info_once("%s not supported\n", inode->i_sb->s_type->name); pr_info_once("%s not supported\n", inode->i_sb->s_type->name);
return 1; return 1;
} }
...@@ -192,7 +192,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -192,7 +192,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
return iint->evm_status; return iint->evm_status;
if (is_unsupported_fs(dentry)) /*
* On unsupported filesystems without EVM_INIT_X509 enabled, skip
* signature verification.
*/
if (!(evm_initialized & EVM_INIT_X509) &&
is_unsupported_hmac_fs(dentry))
return INTEGRITY_UNKNOWN; return INTEGRITY_UNKNOWN;
/* if status is not PASS, try to check again - against -ENOMEM */ /* if status is not PASS, try to check again - against -ENOMEM */
...@@ -226,7 +231,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -226,7 +231,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
digest.hdr.algo = HASH_ALGO_SHA1; digest.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value, rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, &digest); xattr_value_len, &digest, iint);
if (rc) if (rc)
break; break;
rc = crypto_memneq(xattr_data->data, digest.digest, rc = crypto_memneq(xattr_data->data, digest.digest,
...@@ -247,7 +252,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -247,7 +252,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
hdr = (struct signature_v2_hdr *)xattr_data; hdr = (struct signature_v2_hdr *)xattr_data;
digest.hdr.algo = hdr->hash_algo; digest.hdr.algo = hdr->hash_algo;
rc = evm_calc_hash(dentry, xattr_name, xattr_value, 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) if (rc)
break; break;
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
...@@ -260,7 +266,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -260,7 +266,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
evm_status = INTEGRITY_PASS_IMMUTABLE; evm_status = INTEGRITY_PASS_IMMUTABLE;
} else if (!IS_RDONLY(inode) && } else if (!IS_RDONLY(inode) &&
!(inode->i_sb->s_readonly_remount) && !(inode->i_sb->s_readonly_remount) &&
!IS_IMMUTABLE(inode)) { !IS_IMMUTABLE(inode) &&
!is_unsupported_hmac_fs(dentry)) {
evm_update_evmxattr(dentry, xattr_name, evm_update_evmxattr(dentry, xattr_name,
xattr_value, xattr_value,
xattr_value_len); xattr_value_len);
...@@ -418,9 +425,6 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry, ...@@ -418,9 +425,6 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
return INTEGRITY_UNKNOWN; return INTEGRITY_UNKNOWN;
if (is_unsupported_fs(dentry))
return INTEGRITY_UNKNOWN;
return evm_verify_hmac(dentry, xattr_name, xattr_value, return evm_verify_hmac(dentry, xattr_name, xattr_value,
xattr_value_len); xattr_value_len);
} }
...@@ -499,12 +503,12 @@ static int evm_protect_xattr(struct mnt_idmap *idmap, ...@@ -499,12 +503,12 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) { if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
if (is_unsupported_fs(dentry)) if (is_unsupported_hmac_fs(dentry))
return -EPERM; return -EPERM;
} else if (!evm_protected_xattr(xattr_name)) { } else if (!evm_protected_xattr(xattr_name)) {
if (!posix_xattr_acl(xattr_name)) if (!posix_xattr_acl(xattr_name))
return 0; return 0;
if (is_unsupported_fs(dentry)) if (is_unsupported_hmac_fs(dentry))
return 0; return 0;
evm_status = evm_verify_current_integrity(dentry); evm_status = evm_verify_current_integrity(dentry);
...@@ -512,7 +516,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap, ...@@ -512,7 +516,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
(evm_status == INTEGRITY_NOXATTRS)) (evm_status == INTEGRITY_NOXATTRS))
return 0; return 0;
goto out; goto out;
} else if (is_unsupported_fs(dentry)) } else if (is_unsupported_hmac_fs(dentry))
return 0; return 0;
evm_status = evm_verify_current_integrity(dentry); evm_status = evm_verify_current_integrity(dentry);
...@@ -733,6 +737,31 @@ static void evm_reset_status(struct inode *inode) ...@@ -733,6 +737,31 @@ static void evm_reset_status(struct inode *inode)
iint->evm_status = INTEGRITY_UNKNOWN; 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 * evm_revalidate_status - report whether EVM status re-validation is necessary
* @xattr_name: pointer to the affected extended attribute name * @xattr_name: pointer to the affected extended attribute name
...@@ -789,7 +818,7 @@ static void evm_inode_post_setxattr(struct dentry *dentry, ...@@ -789,7 +818,7 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
if (!(evm_initialized & EVM_INIT_HMAC)) if (!(evm_initialized & EVM_INIT_HMAC))
return; return;
if (is_unsupported_fs(dentry)) if (is_unsupported_hmac_fs(dentry))
return; return;
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
...@@ -888,7 +917,7 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -888,7 +917,7 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (evm_initialized & EVM_ALLOW_METADATA_WRITES) if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
return 0; return 0;
if (is_unsupported_fs(dentry)) if (is_unsupported_hmac_fs(dentry))
return 0; return 0;
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
...@@ -939,18 +968,43 @@ static void evm_inode_post_setattr(struct mnt_idmap *idmap, ...@@ -939,18 +968,43 @@ static void evm_inode_post_setattr(struct mnt_idmap *idmap,
if (!(evm_initialized & EVM_INIT_HMAC)) if (!(evm_initialized & EVM_INIT_HMAC))
return; return;
if (is_unsupported_fs(dentry)) if (is_unsupported_hmac_fs(dentry))
return; return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
evm_update_evmxattr(dentry, NULL, NULL, 0); evm_update_evmxattr(dentry, NULL, NULL, 0);
} }
static int evm_inode_copy_up_xattr(const char *name) static int evm_inode_copy_up_xattr(struct dentry *src, const char *name)
{ {
if (strcmp(name, XATTR_NAME_EVM) == 0) struct evm_ima_xattr_data *xattr_data = NULL;
return 1; /* Discard */ int rc;
if (strcmp(name, XATTR_NAME_EVM) != 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* first need to know the sig type */
rc = vfs_getxattr_alloc(&nop_mnt_idmap, src, XATTR_NAME_EVM,
(char **)&xattr_data, 0, GFP_NOFS);
if (rc <= 0)
return -EPERM;
if (rc < offsetof(struct evm_ima_xattr_data, type) +
sizeof(xattr_data->type))
return -EPERM;
switch (xattr_data->type) {
case EVM_XATTR_PORTABLE_DIGSIG:
rc = 0; /* allow copy-up */
break;
case EVM_XATTR_HMAC:
case EVM_IMA_XATTR_DIGSIG:
default:
rc = 1; /* discard */
}
kfree(xattr_data);
return rc;
} }
/* /*
......
...@@ -49,11 +49,19 @@ extern int ima_policy_flag; ...@@ -49,11 +49,19 @@ extern int ima_policy_flag;
/* bitset of digests algorithms allowed in the setxattr hook */ /* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms; extern atomic_t ima_setxattr_allowed_hash_algorithms;
/* IMA hash algorithm description */
struct ima_algo_desc {
struct crypto_shash *tfm;
enum hash_algo algo;
};
/* set during initialization */ /* set during initialization */
extern int ima_hash_algo __ro_after_init; extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init; extern int ima_sha1_idx __ro_after_init;
extern int ima_hash_algo_idx __ro_after_init; extern int ima_hash_algo_idx __ro_after_init;
extern int ima_extra_slots __ro_after_init; extern int ima_extra_slots __ro_after_init;
extern struct ima_algo_desc *ima_algo_array __ro_after_init;
extern int ima_appraise; extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip; extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[]; extern const char boot_aggregate_name[];
...@@ -175,12 +183,10 @@ struct ima_kexec_hdr { ...@@ -175,12 +183,10 @@ struct ima_kexec_hdr {
/* IMA integrity metadata associated with an inode */ /* IMA integrity metadata associated with an inode */
struct ima_iint_cache { struct ima_iint_cache {
struct mutex mutex; /* protects: version, flags, digest */ struct mutex mutex; /* protects: version, flags, digest */
u64 version; /* track inode changes */ struct integrity_inode_attributes real_inode;
unsigned long flags; unsigned long flags;
unsigned long measured_pcrs; unsigned long measured_pcrs;
unsigned long atomic_flags; unsigned long atomic_flags;
unsigned long real_ino;
dev_t real_dev;
enum integrity_status ima_file_status:4; enum integrity_status ima_file_status:4;
enum integrity_status ima_mmap_status:4; enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4; enum integrity_status ima_bprm_status:4;
......
...@@ -245,8 +245,10 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, ...@@ -245,8 +245,10 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
const char *audit_cause = "failed"; const char *audit_cause = "failed";
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct inode *real_inode = d_real_inode(file_dentry(file)); struct inode *real_inode = d_real_inode(file_dentry(file));
const char *filename = file->f_path.dentry->d_name.name;
struct ima_max_digest_data hash; struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
struct name_snapshot filename;
struct kstat stat; struct kstat stat;
int result = 0; int result = 0;
int length; int length;
...@@ -286,9 +288,9 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, ...@@ -286,9 +288,9 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
result = -ENODATA; result = -ENODATA;
} }
} else if (buf) { } else if (buf) {
result = ima_calc_buffer_hash(buf, size, &hash.hdr); result = ima_calc_buffer_hash(buf, size, hash_hdr);
} else { } else {
result = ima_calc_file_hash(file, &hash.hdr); result = ima_calc_file_hash(file, hash_hdr);
} }
if (result && result != -EBADF && result != -EINVAL) if (result && result != -EBADF && result != -EINVAL)
...@@ -303,11 +305,11 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, ...@@ -303,11 +305,11 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
iint->ima_hash = tmpbuf; iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length); memcpy(iint->ima_hash, &hash, length);
iint->version = i_version; if (real_inode == inode)
if (real_inode != inode) { iint->real_inode.version = i_version;
iint->real_ino = real_inode->i_ino; else
iint->real_dev = real_inode->i_sb->s_dev; integrity_inode_attrs_store(&iint->real_inode, i_version,
} real_inode);
/* Possibly temporary failure due to type of read (eg. O_DIRECT) */ /* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result) if (!result)
...@@ -317,9 +319,13 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, ...@@ -317,9 +319,13 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
if (file->f_flags & O_DIRECT) if (file->f_flags & O_DIRECT)
audit_cause = "failed(directio)"; audit_cause = "failed(directio)";
take_dentry_name_snapshot(&filename, file->f_path.dentry);
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
filename, "collect_data", audit_cause, filename.name.name, "collect_data",
result, 0); audit_cause, result, 0);
release_dentry_name_snapshot(&filename);
} }
return result; return result;
} }
...@@ -432,6 +438,7 @@ void ima_audit_measurement(struct ima_iint_cache *iint, ...@@ -432,6 +438,7 @@ void ima_audit_measurement(struct ima_iint_cache *iint,
*/ */
const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
{ {
struct name_snapshot filename;
char *pathname = NULL; char *pathname = NULL;
*pathbuf = __getname(); *pathbuf = __getname();
...@@ -445,7 +452,10 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) ...@@ -445,7 +452,10 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
} }
if (!pathname) { if (!pathname) {
strscpy(namebuf, path->dentry->d_name.name, NAME_MAX); take_dentry_name_snapshot(&filename, path->dentry);
strscpy(namebuf, filename.name.name, NAME_MAX);
release_dentry_name_snapshot(&filename);
pathname = namebuf; pathname = namebuf;
} }
......
...@@ -378,7 +378,9 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, ...@@ -378,7 +378,9 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
} }
rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo, rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
iint->ima_hash->digest, &hash.hdr); iint->ima_hash->digest,
container_of(&hash.hdr,
struct ima_digest_data, hdr));
if (rc) { if (rc) {
*cause = "sigv3-hashing-error"; *cause = "sigv3-hashing-error";
*status = INTEGRITY_FAIL; *status = INTEGRITY_FAIL;
......
...@@ -57,11 +57,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); ...@@ -57,11 +57,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
static struct crypto_shash *ima_shash_tfm; static struct crypto_shash *ima_shash_tfm;
static struct crypto_ahash *ima_ahash_tfm; static struct crypto_ahash *ima_ahash_tfm;
struct ima_algo_desc {
struct crypto_shash *tfm;
enum hash_algo algo;
};
int ima_sha1_idx __ro_after_init; int ima_sha1_idx __ro_after_init;
int ima_hash_algo_idx __ro_after_init; int ima_hash_algo_idx __ro_after_init;
/* /*
...@@ -70,7 +65,7 @@ int ima_hash_algo_idx __ro_after_init; ...@@ -70,7 +65,7 @@ int ima_hash_algo_idx __ro_after_init;
*/ */
int ima_extra_slots __ro_after_init; int ima_extra_slots __ro_after_init;
static struct ima_algo_desc *ima_algo_array; struct ima_algo_desc *ima_algo_array __ro_after_init;
static int __init ima_init_ima_crypto(void) static int __init ima_init_ima_crypto(void)
{ {
......
...@@ -116,9 +116,31 @@ void ima_putc(struct seq_file *m, void *data, int datalen) ...@@ -116,9 +116,31 @@ void ima_putc(struct seq_file *m, void *data, int datalen)
seq_putc(m, *(char *)data++); seq_putc(m, *(char *)data++);
} }
static struct dentry **ascii_securityfs_measurement_lists __ro_after_init;
static struct dentry **binary_securityfs_measurement_lists __ro_after_init;
static int securityfs_measurement_list_count __ro_after_init;
static void lookup_template_data_hash_algo(int *algo_idx, enum hash_algo *algo,
struct seq_file *m,
struct dentry **lists)
{
struct dentry *dentry;
int i;
dentry = file_dentry(m->file);
for (i = 0; i < securityfs_measurement_list_count; i++) {
if (dentry == lists[i]) {
*algo_idx = i;
*algo = ima_algo_array[i].algo;
break;
}
}
}
/* print format: /* print format:
* 32bit-le=pcr# * 32bit-le=pcr#
* char[20]=template digest * char[n]=template digest
* 32bit-le=template name size * 32bit-le=template name size
* char[n]=template name * char[n]=template name
* [eventdata length] * [eventdata length]
...@@ -132,7 +154,15 @@ int ima_measurements_show(struct seq_file *m, void *v) ...@@ -132,7 +154,15 @@ int ima_measurements_show(struct seq_file *m, void *v)
char *template_name; char *template_name;
u32 pcr, namelen, template_data_len; /* temporary fields */ u32 pcr, namelen, template_data_len; /* temporary fields */
bool is_ima_template = false; bool is_ima_template = false;
int i; enum hash_algo algo;
int i, algo_idx;
algo_idx = ima_sha1_idx;
algo = HASH_ALGO_SHA1;
if (m->file != NULL)
lookup_template_data_hash_algo(&algo_idx, &algo, m,
binary_securityfs_measurement_lists);
/* get entry */ /* get entry */
e = qe->entry; e = qe->entry;
...@@ -151,7 +181,7 @@ int ima_measurements_show(struct seq_file *m, void *v) ...@@ -151,7 +181,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, &pcr, sizeof(e->pcr)); ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */ /* 2nd: template digest */
ima_putc(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE); ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
/* 3rd: template name size */ /* 3rd: template name size */
namelen = !ima_canonical_fmt ? strlen(template_name) : namelen = !ima_canonical_fmt ? strlen(template_name) :
...@@ -220,7 +250,15 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ...@@ -220,7 +250,15 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
struct ima_queue_entry *qe = v; struct ima_queue_entry *qe = v;
struct ima_template_entry *e; struct ima_template_entry *e;
char *template_name; char *template_name;
int i; enum hash_algo algo;
int i, algo_idx;
algo_idx = ima_sha1_idx;
algo = HASH_ALGO_SHA1;
if (m->file != NULL)
lookup_template_data_hash_algo(&algo_idx, &algo, m,
ascii_securityfs_measurement_lists);
/* get entry */ /* get entry */
e = qe->entry; e = qe->entry;
...@@ -233,8 +271,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ...@@ -233,8 +271,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
/* 1st: PCR used (config option) */ /* 1st: PCR used (config option) */
seq_printf(m, "%2d ", e->pcr); seq_printf(m, "%2d ", e->pcr);
/* 2nd: SHA1 template hash */ /* 2nd: template hash */
ima_print_digest(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE); ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
/* 3th: template name */ /* 3th: template name */
seq_printf(m, " %s", template_name); seq_printf(m, " %s", template_name);
...@@ -379,6 +417,71 @@ static const struct seq_operations ima_policy_seqops = { ...@@ -379,6 +417,71 @@ static const struct seq_operations ima_policy_seqops = {
}; };
#endif #endif
static void __init remove_securityfs_measurement_lists(struct dentry **lists)
{
int i;
if (lists) {
for (i = 0; i < securityfs_measurement_list_count; i++)
securityfs_remove(lists[i]);
kfree(lists);
}
securityfs_measurement_list_count = 0;
}
static int __init create_securityfs_measurement_lists(void)
{
char file_name[NAME_MAX + 1];
struct dentry *dentry;
u16 algo;
int i;
securityfs_measurement_list_count = NR_BANKS(ima_tpm_chip);
if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip))
securityfs_measurement_list_count++;
ascii_securityfs_measurement_lists =
kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *),
GFP_KERNEL);
if (!ascii_securityfs_measurement_lists)
return -ENOMEM;
binary_securityfs_measurement_lists =
kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *),
GFP_KERNEL);
if (!binary_securityfs_measurement_lists)
return -ENOMEM;
for (i = 0; i < securityfs_measurement_list_count; i++) {
algo = ima_algo_array[i].algo;
sprintf(file_name, "ascii_runtime_measurements_%s",
hash_algo_name[algo]);
dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
ima_dir, NULL,
&ima_ascii_measurements_ops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
ascii_securityfs_measurement_lists[i] = dentry;
sprintf(file_name, "binary_runtime_measurements_%s",
hash_algo_name[algo]);
dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
ima_dir, NULL,
&ima_measurements_ops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
binary_securityfs_measurement_lists[i] = dentry;
}
return 0;
}
/* /*
* ima_open_policy: sequentialize access to the policy file * ima_open_policy: sequentialize access to the policy file
*/ */
...@@ -454,6 +557,9 @@ int __init ima_fs_init(void) ...@@ -454,6 +557,9 @@ int __init ima_fs_init(void)
{ {
int ret; int ret;
ascii_securityfs_measurement_lists = NULL;
binary_securityfs_measurement_lists = NULL;
ima_dir = securityfs_create_dir("ima", integrity_dir); ima_dir = securityfs_create_dir("ima", integrity_dir);
if (IS_ERR(ima_dir)) if (IS_ERR(ima_dir))
return PTR_ERR(ima_dir); return PTR_ERR(ima_dir);
...@@ -465,19 +571,21 @@ int __init ima_fs_init(void) ...@@ -465,19 +571,21 @@ int __init ima_fs_init(void)
goto out; goto out;
} }
ret = create_securityfs_measurement_lists();
if (ret != 0)
goto out;
binary_runtime_measurements = binary_runtime_measurements =
securityfs_create_file("binary_runtime_measurements", securityfs_create_symlink("binary_runtime_measurements", ima_dir,
S_IRUSR | S_IRGRP, ima_dir, NULL, "binary_runtime_measurements_sha1", NULL);
&ima_measurements_ops);
if (IS_ERR(binary_runtime_measurements)) { if (IS_ERR(binary_runtime_measurements)) {
ret = PTR_ERR(binary_runtime_measurements); ret = PTR_ERR(binary_runtime_measurements);
goto out; goto out;
} }
ascii_runtime_measurements = ascii_runtime_measurements =
securityfs_create_file("ascii_runtime_measurements", securityfs_create_symlink("ascii_runtime_measurements", ima_dir,
S_IRUSR | S_IRGRP, ima_dir, NULL, "ascii_runtime_measurements_sha1", NULL);
&ima_ascii_measurements_ops);
if (IS_ERR(ascii_runtime_measurements)) { if (IS_ERR(ascii_runtime_measurements)) {
ret = PTR_ERR(ascii_runtime_measurements); ret = PTR_ERR(ascii_runtime_measurements);
goto out; goto out;
...@@ -515,6 +623,8 @@ int __init ima_fs_init(void) ...@@ -515,6 +623,8 @@ int __init ima_fs_init(void)
securityfs_remove(runtime_measurements_count); securityfs_remove(runtime_measurements_count);
securityfs_remove(ascii_runtime_measurements); securityfs_remove(ascii_runtime_measurements);
securityfs_remove(binary_runtime_measurements); securityfs_remove(binary_runtime_measurements);
remove_securityfs_measurement_lists(ascii_securityfs_measurement_lists);
remove_securityfs_measurement_lists(binary_securityfs_measurement_lists);
securityfs_remove(ima_symlink); securityfs_remove(ima_symlink);
securityfs_remove(ima_dir); securityfs_remove(ima_dir);
......
...@@ -59,7 +59,7 @@ static void ima_iint_init_always(struct ima_iint_cache *iint, ...@@ -59,7 +59,7 @@ static void ima_iint_init_always(struct ima_iint_cache *iint,
struct inode *inode) struct inode *inode)
{ {
iint->ima_hash = NULL; iint->ima_hash = NULL;
iint->version = 0; iint->real_inode.version = 0;
iint->flags = 0UL; iint->flags = 0UL;
iint->atomic_flags = 0UL; iint->atomic_flags = 0UL;
iint->ima_file_status = INTEGRITY_UNKNOWN; iint->ima_file_status = INTEGRITY_UNKNOWN;
......
...@@ -48,12 +48,14 @@ static int __init ima_add_boot_aggregate(void) ...@@ -48,12 +48,14 @@ static int __init ima_add_boot_aggregate(void)
struct ima_event_data event_data = { .iint = iint, struct ima_event_data event_data = { .iint = iint,
.filename = boot_aggregate_name }; .filename = boot_aggregate_name };
struct ima_max_digest_data hash; struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
int result = -ENOMEM; int result = -ENOMEM;
int violation = 0; int violation = 0;
memset(iint, 0, sizeof(*iint)); memset(iint, 0, sizeof(*iint));
memset(&hash, 0, sizeof(hash)); memset(&hash, 0, sizeof(hash));
iint->ima_hash = &hash.hdr; iint->ima_hash = hash_hdr;
iint->ima_hash->algo = ima_hash_algo; iint->ima_hash->algo = ima_hash_algo;
iint->ima_hash->length = hash_digest_size[ima_hash_algo]; iint->ima_hash->length = hash_digest_size[ima_hash_algo];
...@@ -70,7 +72,7 @@ static int __init ima_add_boot_aggregate(void) ...@@ -70,7 +72,7 @@ static int __init ima_add_boot_aggregate(void)
* is not found. * is not found.
*/ */
if (ima_tpm_chip) { if (ima_tpm_chip) {
result = ima_calc_boot_aggregate(&hash.hdr); result = ima_calc_boot_aggregate(hash_hdr);
if (result < 0) { if (result < 0) {
audit_cause = "hashing_error"; audit_cause = "hashing_error";
goto err_out; goto err_out;
......
...@@ -30,6 +30,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, ...@@ -30,6 +30,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
goto out; goto out;
} }
file.file = NULL;
file.size = segment_size; file.size = segment_size;
file.read_pos = 0; file.read_pos = 0;
file.count = sizeof(khdr); /* reserved space */ file.count = sizeof(khdr); /* reserved space */
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/ima.h> #include <linux/ima.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <linux/evm.h>
#include "ima.h" #include "ima.h"
...@@ -173,7 +174,7 @@ static void ima_check_last_writer(struct ima_iint_cache *iint, ...@@ -173,7 +174,7 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
STATX_CHANGE_COOKIE, STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT) || AT_STATX_SYNC_AS_STAT) ||
!(stat.result_mask & STATX_CHANGE_COOKIE) || !(stat.result_mask & STATX_CHANGE_COOKIE) ||
stat.change_cookie != iint->version) { stat.change_cookie != iint->real_inode.version) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0; iint->measured_pcrs = 0;
if (update) if (update)
...@@ -208,9 +209,10 @@ static int process_measurement(struct file *file, const struct cred *cred, ...@@ -208,9 +209,10 @@ static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask, u32 secid, char *buf, loff_t size, int mask,
enum ima_hooks func) enum ima_hooks func)
{ {
struct inode *backing_inode, *inode = file_inode(file); struct inode *real_inode, *inode = file_inode(file);
struct ima_iint_cache *iint = NULL; struct ima_iint_cache *iint = NULL;
struct ima_template_desc *template_desc = NULL; struct ima_template_desc *template_desc = NULL;
struct inode *metadata_inode;
char *pathbuf = NULL; char *pathbuf = NULL;
char filename[NAME_MAX]; char filename[NAME_MAX];
const char *pathname = NULL; const char *pathname = NULL;
...@@ -285,17 +287,28 @@ static int process_measurement(struct file *file, const struct cred *cred, ...@@ -285,17 +287,28 @@ static int process_measurement(struct file *file, const struct cred *cred,
iint->measured_pcrs = 0; iint->measured_pcrs = 0;
} }
/* Detect and re-evaluate changes made to the backing file. */ /*
backing_inode = d_real_inode(file_dentry(file)); * On stacked filesystems, detect and re-evaluate file data and
if (backing_inode != inode && * metadata changes.
*/
real_inode = d_real_inode(file_dentry(file));
if (real_inode != inode &&
(action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) { (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
if (!IS_I_VERSION(backing_inode) || if (!IS_I_VERSION(real_inode) ||
backing_inode->i_sb->s_dev != iint->real_dev || integrity_inode_attrs_changed(&iint->real_inode,
backing_inode->i_ino != iint->real_ino || real_inode)) {
!inode_eq_iversion(backing_inode, iint->version)) {
iint->flags &= ~IMA_DONE_MASK; iint->flags &= ~IMA_DONE_MASK;
iint->measured_pcrs = 0; iint->measured_pcrs = 0;
} }
/*
* Reset the EVM status when metadata changed.
*/
metadata_inode = d_inode(d_real(file_dentry(file),
D_REAL_METADATA));
if (evm_metadata_changed(inode, metadata_inode))
iint->flags &= ~(IMA_APPRAISED |
IMA_APPRAISED_SUBMASK);
} }
/* Determine if already appraised/measured based on bitmask /* Determine if already appraised/measured based on bitmask
...@@ -902,6 +915,13 @@ static int ima_post_load_data(char *buf, loff_t size, ...@@ -902,6 +915,13 @@ static int ima_post_load_data(char *buf, loff_t size,
return 0; return 0;
} }
/*
* Measure the init_module syscall buffer containing the ELF image.
*/
if (load_id == LOADING_MODULE)
ima_measure_critical_data("modules", "init_module",
buf, size, true, NULL, 0);
return 0; return 0;
} }
...@@ -941,6 +961,8 @@ int process_buffer_measurement(struct mnt_idmap *idmap, ...@@ -941,6 +961,8 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
.buf_len = size}; .buf_len = size};
struct ima_template_desc *template; struct ima_template_desc *template;
struct ima_max_digest_data hash; struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
char digest_hash[IMA_MAX_DIGEST_SIZE]; char digest_hash[IMA_MAX_DIGEST_SIZE];
int digest_hash_len = hash_digest_size[ima_hash_algo]; int digest_hash_len = hash_digest_size[ima_hash_algo];
int violation = 0; int violation = 0;
...@@ -979,7 +1001,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap, ...@@ -979,7 +1001,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
if (!pcr) if (!pcr)
pcr = CONFIG_IMA_MEASURE_PCR_IDX; pcr = CONFIG_IMA_MEASURE_PCR_IDX;
iint.ima_hash = &hash.hdr; iint.ima_hash = hash_hdr;
iint.ima_hash->algo = ima_hash_algo; iint.ima_hash->algo = ima_hash_algo;
iint.ima_hash->length = hash_digest_size[ima_hash_algo]; iint.ima_hash->length = hash_digest_size[ima_hash_algo];
...@@ -990,7 +1012,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap, ...@@ -990,7 +1012,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
} }
if (buf_hash) { if (buf_hash) {
memcpy(digest_hash, hash.hdr.digest, digest_hash_len); memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
ret = ima_calc_buffer_hash(digest_hash, digest_hash_len, ret = ima_calc_buffer_hash(digest_hash, digest_hash_len,
iint.ima_hash); iint.ima_hash);
......
...@@ -339,6 +339,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data, ...@@ -339,6 +339,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data) struct ima_field_data *field_data)
{ {
struct ima_max_digest_data hash; struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
u8 *cur_digest = NULL; u8 *cur_digest = NULL;
u32 cur_digestsize = 0; u32 cur_digestsize = 0;
struct inode *inode; struct inode *inode;
...@@ -358,7 +360,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data, ...@@ -358,7 +360,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
if ((const char *)event_data->filename == boot_aggregate_name) { if ((const char *)event_data->filename == boot_aggregate_name) {
if (ima_tpm_chip) { if (ima_tpm_chip) {
hash.hdr.algo = HASH_ALGO_SHA1; hash.hdr.algo = HASH_ALGO_SHA1;
result = ima_calc_boot_aggregate(&hash.hdr); result = ima_calc_boot_aggregate(hash_hdr);
/* algo can change depending on available PCR banks */ /* algo can change depending on available PCR banks */
if (!result && hash.hdr.algo != HASH_ALGO_SHA1) if (!result && hash.hdr.algo != HASH_ALGO_SHA1)
...@@ -368,7 +370,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data, ...@@ -368,7 +370,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
memset(&hash, 0, sizeof(hash)); memset(&hash, 0, sizeof(hash));
} }
cur_digest = hash.hdr.digest; cur_digest = hash_hdr->digest;
cur_digestsize = hash_digest_size[HASH_ALGO_SHA1]; cur_digestsize = hash_digest_size[HASH_ALGO_SHA1];
goto out; goto out;
} }
...@@ -379,14 +381,14 @@ int ima_eventdigest_init(struct ima_event_data *event_data, ...@@ -379,14 +381,14 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
inode = file_inode(event_data->file); inode = file_inode(event_data->file);
hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
ima_hash_algo : HASH_ALGO_SHA1; ima_hash_algo : HASH_ALGO_SHA1;
result = ima_calc_file_hash(event_data->file, &hash.hdr); result = ima_calc_file_hash(event_data->file, hash_hdr);
if (result) { if (result) {
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
event_data->filename, "collect_data", event_data->filename, "collect_data",
"failed", result, 0); "failed", result, 0);
return result; return result;
} }
cur_digest = hash.hdr.digest; cur_digest = hash_hdr->digest;
cur_digestsize = hash.hdr.length; cur_digestsize = hash.hdr.length;
out: out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize, return ima_eventdigest_init_common(cur_digest, cur_digestsize,
...@@ -483,7 +485,10 @@ static int ima_eventname_init_common(struct ima_event_data *event_data, ...@@ -483,7 +485,10 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
bool size_limit) bool size_limit)
{ {
const char *cur_filename = NULL; const char *cur_filename = NULL;
struct name_snapshot filename;
u32 cur_filename_len = 0; u32 cur_filename_len = 0;
bool snapshot = false;
int ret;
BUG_ON(event_data->filename == NULL && event_data->file == NULL); BUG_ON(event_data->filename == NULL && event_data->file == NULL);
...@@ -496,7 +501,10 @@ static int ima_eventname_init_common(struct ima_event_data *event_data, ...@@ -496,7 +501,10 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
} }
if (event_data->file) { if (event_data->file) {
cur_filename = event_data->file->f_path.dentry->d_name.name; take_dentry_name_snapshot(&filename,
event_data->file->f_path.dentry);
snapshot = true;
cur_filename = filename.name.name;
cur_filename_len = strlen(cur_filename); cur_filename_len = strlen(cur_filename);
} else } else
/* /*
...@@ -505,8 +513,13 @@ static int ima_eventname_init_common(struct ima_event_data *event_data, ...@@ -505,8 +513,13 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
*/ */
cur_filename_len = IMA_EVENT_NAME_LEN_MAX; cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
out: out:
return ima_write_template_field_data(cur_filename, cur_filename_len, ret = ima_write_template_field_data(cur_filename, cur_filename_len,
DATA_FMT_STRING, field_data); DATA_FMT_STRING, field_data);
if (snapshot)
release_dentry_name_snapshot(&filename);
return ret;
} }
/* /*
......
...@@ -31,19 +31,24 @@ enum evm_ima_xattr_type { ...@@ -31,19 +31,24 @@ enum evm_ima_xattr_type {
}; };
struct evm_ima_xattr_data { struct evm_ima_xattr_data {
/* New members must be added within the __struct_group() macro below. */
__struct_group(evm_ima_xattr_data_hdr, hdr, __packed,
u8 type; u8 type;
);
u8 data[]; u8 data[];
} __packed; } __packed;
/* Only used in the EVM HMAC code. */ /* Only used in the EVM HMAC code. */
struct evm_xattr { struct evm_xattr {
struct evm_ima_xattr_data data; struct evm_ima_xattr_data_hdr data;
u8 digest[SHA1_DIGEST_SIZE]; u8 digest[SHA1_DIGEST_SIZE];
} __packed; } __packed;
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE #define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
struct ima_digest_data { struct ima_digest_data {
/* New members must be added within the __struct_group() macro below. */
__struct_group(ima_digest_data_hdr, hdr, __packed,
u8 algo; u8 algo;
u8 length; u8 length;
union { union {
...@@ -57,6 +62,7 @@ struct ima_digest_data { ...@@ -57,6 +62,7 @@ struct ima_digest_data {
} ng; } ng;
u8 data[2]; u8 data[2];
} xattr; } xattr;
);
u8 digest[]; u8 digest[];
} __packed; } __packed;
...@@ -65,7 +71,7 @@ struct ima_digest_data { ...@@ -65,7 +71,7 @@ struct ima_digest_data {
* with the maximum hash size, define ima_max_digest_data struct. * with the maximum hash size, define ima_max_digest_data struct.
*/ */
struct ima_max_digest_data { struct ima_max_digest_data {
struct ima_digest_data hdr; struct ima_digest_data_hdr hdr;
u8 digest[HASH_MAX_DIGESTSIZE]; u8 digest[HASH_MAX_DIGESTSIZE];
} __packed; } __packed;
......
...@@ -2628,6 +2628,7 @@ EXPORT_SYMBOL(security_inode_copy_up); ...@@ -2628,6 +2628,7 @@ EXPORT_SYMBOL(security_inode_copy_up);
/** /**
* security_inode_copy_up_xattr() - Filter xattrs in an overlayfs copy-up op * security_inode_copy_up_xattr() - Filter xattrs in an overlayfs copy-up op
* @src: union dentry of copy-up file
* @name: xattr name * @name: xattr name
* *
* Filter the xattrs being copied up when a unioned file is copied up from a * Filter the xattrs being copied up when a unioned file is copied up from a
...@@ -2638,7 +2639,7 @@ EXPORT_SYMBOL(security_inode_copy_up); ...@@ -2638,7 +2639,7 @@ EXPORT_SYMBOL(security_inode_copy_up);
* if the security module does not know about attribute, or a negative * if the security module does not know about attribute, or a negative
* error code to abort the copy up. * error code to abort the copy up.
*/ */
int security_inode_copy_up_xattr(const char *name) int security_inode_copy_up_xattr(struct dentry *src, const char *name)
{ {
int rc; int rc;
...@@ -2647,7 +2648,7 @@ int security_inode_copy_up_xattr(const char *name) ...@@ -2647,7 +2648,7 @@ int security_inode_copy_up_xattr(const char *name)
* xattr), -EOPNOTSUPP if it does not know anything about the xattr or * xattr), -EOPNOTSUPP if it does not know anything about the xattr or
* any other error code in case of an error. * any other error code in case of an error.
*/ */
rc = call_int_hook(inode_copy_up_xattr, name); rc = call_int_hook(inode_copy_up_xattr, src, name);
if (rc != LSM_RET_DEFAULT(inode_copy_up_xattr)) if (rc != LSM_RET_DEFAULT(inode_copy_up_xattr))
return rc; return rc;
......
...@@ -3526,7 +3526,7 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new) ...@@ -3526,7 +3526,7 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
return 0; return 0;
} }
static int selinux_inode_copy_up_xattr(const char *name) static int selinux_inode_copy_up_xattr(struct dentry *dentry, const char *name)
{ {
/* The copy_up hook above sets the initial context on an inode, but we /* The copy_up hook above sets the initial context on an inode, but we
* don't then want to overwrite it by blindly copying all the lower * don't then want to overwrite it by blindly copying all the lower
......
...@@ -4886,7 +4886,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new) ...@@ -4886,7 +4886,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
return 0; return 0;
} }
static int smack_inode_copy_up_xattr(const char *name) static int smack_inode_copy_up_xattr(struct dentry *src, const char *name)
{ {
/* /*
* Return 1 if this is the smack access Smack attribute. * Return 1 if this is the smack access Smack attribute.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment