summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-05-15 18:43:02 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2024-05-15 18:43:02 +0300
commit353ad6c0839431146fdee3ff16f9dd17a2809ee4 (patch)
tree5509e6bab7847132990755796bafc7611a779593 /security
parentccae19c6239ae810242d2edc03b02bdcc12fc5ab (diff)
parent9fa8e76250082a45d0d3dad525419ab98bd01658 (diff)
downloadlinux-353ad6c0839431146fdee3ff16f9dd17a2809ee4.tar.xz
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
Diffstat (limited to 'security')
-rw-r--r--security/integrity/evm/evm.h8
-rw-r--r--security/integrity/evm/evm_crypto.c25
-rw-r--r--security/integrity/evm/evm_main.c92
-rw-r--r--security/integrity/ima/ima.h12
-rw-r--r--security/integrity/ima/ima_api.c32
-rw-r--r--security/integrity/ima/ima_appraise.c4
-rw-r--r--security/integrity/ima/ima_crypto.c7
-rw-r--r--security/integrity/ima/ima_fs.c134
-rw-r--r--security/integrity/ima/ima_iint.c2
-rw-r--r--security/integrity/ima/ima_init.c6
-rw-r--r--security/integrity/ima/ima_kexec.c1
-rw-r--r--security/integrity/ima/ima_main.c44
-rw-r--r--security/integrity/ima/ima_template_lib.c27
-rw-r--r--security/integrity/integrity.h12
-rw-r--r--security/security.c5
-rw-r--r--security/selinux/hooks.c2
-rw-r--r--security/smack/smack_lsm.c2
17 files changed, 325 insertions, 90 deletions
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index eb1a2c343bd7..51aba5a54275 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -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;
@@ -61,7 +62,7 @@ extern int evm_hmac_attrs;
extern struct list_head evm_config_xattrnames;
struct evm_digest {
- struct ima_digest_data hdr;
+ struct ima_digest_data_hdr hdr;
char digest[IMA_MAX_DIGEST_SIZE];
} __packed;
@@ -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);
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 7552d49d0725..7c06ffd633d2 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -221,9 +221,10 @@ 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_backing_inode(dentry);
+ struct inode *inode = d_inode(d_real(dentry, D_REAL_METADATA));
struct xattr_list *xattr;
struct shash_desc *desc;
size_t xattr_size = 0;
@@ -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)
@@ -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;
@@ -305,18 +314,19 @@ out:
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)
@@ -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;
@@ -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,
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 81dbade5b9b3..62fe66dd53ce 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -151,11 +151,11 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
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);
- 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);
return 1;
}
@@ -192,7 +192,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
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;
/* 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,
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,
@@ -247,7 +252,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,
@@ -260,7 +266,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
evm_status = INTEGRITY_PASS_IMMUTABLE;
} else if (!IS_RDONLY(inode) &&
!(inode->i_sb->s_readonly_remount) &&
- !IS_IMMUTABLE(inode)) {
+ !IS_IMMUTABLE(inode) &&
+ !is_unsupported_hmac_fs(dentry)) {
evm_update_evmxattr(dentry, xattr_name,
xattr_value,
xattr_value_len);
@@ -418,9 +425,6 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
return INTEGRITY_UNKNOWN;
- if (is_unsupported_fs(dentry))
- return INTEGRITY_UNKNOWN;
-
return evm_verify_hmac(dentry, xattr_name, xattr_value,
xattr_value_len);
}
@@ -499,12 +503,12 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return -EPERM;
} else if (!evm_protected_xattr(xattr_name)) {
if (!posix_xattr_acl(xattr_name))
return 0;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return 0;
evm_status = evm_verify_current_integrity(dentry);
@@ -512,7 +516,7 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
(evm_status == INTEGRITY_NOXATTRS))
return 0;
goto out;
- } else if (is_unsupported_fs(dentry))
+ } else if (is_unsupported_hmac_fs(dentry))
return 0;
evm_status = evm_verify_current_integrity(dentry);
@@ -734,6 +738,31 @@ static void evm_reset_status(struct inode *inode)
}
/**
+ * 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
*
@@ -789,7 +818,7 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
if (!(evm_initialized & EVM_INIT_HMAC))
return;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return;
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,
if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
return 0;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return 0;
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
@@ -939,18 +968,43 @@ static void evm_inode_post_setattr(struct mnt_idmap *idmap,
if (!(evm_initialized & EVM_INIT_HMAC))
return;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
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)
- return 1; /* Discard */
- return -EOPNOTSUPP;
+ struct evm_ima_xattr_data *xattr_data = NULL;
+ int rc;
+
+ if (strcmp(name, XATTR_NAME_EVM) != 0)
+ 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;
}
/*
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 11d7c0332207..3e568126cd48 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -49,11 +49,19 @@ extern int ima_policy_flag;
/* bitset of digests algorithms allowed in the setxattr hook */
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 */
extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init;
extern int ima_hash_algo_idx __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 struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
@@ -175,12 +183,10 @@ struct ima_kexec_hdr {
/* IMA integrity metadata associated with an inode */
struct ima_iint_cache {
struct mutex mutex; /* protects: version, flags, digest */
- u64 version; /* track inode changes */
+ struct integrity_inode_attributes real_inode;
unsigned long flags;
unsigned long measured_pcrs;
unsigned long atomic_flags;
- unsigned long real_ino;
- dev_t real_dev;
enum integrity_status ima_file_status:4;
enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4;
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index b37d043d5748..984e861f6e33 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -245,8 +245,10 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
const char *audit_cause = "failed";
struct inode *inode = file_inode(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_digest_data *hash_hdr = container_of(&hash.hdr,
+ struct ima_digest_data, hdr);
+ struct name_snapshot filename;
struct kstat stat;
int result = 0;
int length;
@@ -286,9 +288,9 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
result = -ENODATA;
}
} else if (buf) {
- result = ima_calc_buffer_hash(buf, size, &hash.hdr);
+ result = ima_calc_buffer_hash(buf, size, hash_hdr);
} else {
- result = ima_calc_file_hash(file, &hash.hdr);
+ result = ima_calc_file_hash(file, hash_hdr);
}
if (result && result != -EBADF && result != -EINVAL)
@@ -303,11 +305,11 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
- iint->version = i_version;
- if (real_inode != inode) {
- iint->real_ino = real_inode->i_ino;
- iint->real_dev = real_inode->i_sb->s_dev;
- }
+ if (real_inode == inode)
+ iint->real_inode.version = i_version;
+ else
+ integrity_inode_attrs_store(&iint->real_inode, i_version,
+ real_inode);
/* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result)
@@ -317,9 +319,13 @@ out:
if (file->f_flags & O_DIRECT)
audit_cause = "failed(directio)";
+ take_dentry_name_snapshot(&filename, file->f_path.dentry);
+
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
- filename, "collect_data", audit_cause,
- result, 0);
+ filename.name.name, "collect_data",
+ audit_cause, result, 0);
+
+ release_dentry_name_snapshot(&filename);
}
return result;
}
@@ -432,6 +438,7 @@ out:
*/
const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
{
+ struct name_snapshot filename;
char *pathname = NULL;
*pathbuf = __getname();
@@ -445,7 +452,10 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
}
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;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 3497741caea9..656c709b974f 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -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,
- iint->ima_hash->digest, &hash.hdr);
+ iint->ima_hash->digest,
+ container_of(&hash.hdr,
+ struct ima_digest_data, hdr));
if (rc) {
*cause = "sigv3-hashing-error";
*status = INTEGRITY_FAIL;
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index f3738b2c8bcd..6f5696d999d0 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -57,11 +57,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
static struct crypto_shash *ima_shash_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_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;
-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)
{
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index cd1683dad3bf..abdd22007ed8 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -116,9 +116,31 @@ void ima_putc(struct seq_file *m, void *data, int datalen)
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:
* 32bit-le=pcr#
- * char[20]=template digest
+ * char[n]=template digest
* 32bit-le=template name size
* char[n]=template name
* [eventdata length]
@@ -132,7 +154,15 @@ int ima_measurements_show(struct seq_file *m, void *v)
char *template_name;
u32 pcr, namelen, template_data_len; /* temporary fields */
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 */
e = qe->entry;
@@ -151,7 +181,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, &pcr, sizeof(e->pcr));
/* 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 */
namelen = !ima_canonical_fmt ? strlen(template_name) :
@@ -220,7 +250,15 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
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 */
e = qe->entry;
@@ -233,8 +271,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
/* 1st: PCR used (config option) */
seq_printf(m, "%2d ", e->pcr);
- /* 2nd: SHA1 template hash */
- ima_print_digest(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE);
+ /* 2nd: template hash */
+ ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
/* 3th: template name */
seq_printf(m, " %s", template_name);
@@ -379,6 +417,71 @@ static const struct seq_operations ima_policy_seqops = {
};
#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
*/
@@ -454,6 +557,9 @@ int __init ima_fs_init(void)
{
int ret;
+ ascii_securityfs_measurement_lists = NULL;
+ binary_securityfs_measurement_lists = NULL;
+
ima_dir = securityfs_create_dir("ima", integrity_dir);
if (IS_ERR(ima_dir))
return PTR_ERR(ima_dir);
@@ -465,19 +571,21 @@ int __init ima_fs_init(void)
goto out;
}
+ ret = create_securityfs_measurement_lists();
+ if (ret != 0)
+ goto out;
+
binary_runtime_measurements =
- securityfs_create_file("binary_runtime_measurements",
- S_IRUSR | S_IRGRP, ima_dir, NULL,
- &ima_measurements_ops);
+ securityfs_create_symlink("binary_runtime_measurements", ima_dir,
+ "binary_runtime_measurements_sha1", NULL);
if (IS_ERR(binary_runtime_measurements)) {
ret = PTR_ERR(binary_runtime_measurements);
goto out;
}
ascii_runtime_measurements =
- securityfs_create_file("ascii_runtime_measurements",
- S_IRUSR | S_IRGRP, ima_dir, NULL,
- &ima_ascii_measurements_ops);
+ securityfs_create_symlink("ascii_runtime_measurements", ima_dir,
+ "ascii_runtime_measurements_sha1", NULL);
if (IS_ERR(ascii_runtime_measurements)) {
ret = PTR_ERR(ascii_runtime_measurements);
goto out;
@@ -515,6 +623,8 @@ out:
securityfs_remove(runtime_measurements_count);
securityfs_remove(ascii_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_dir);
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index e7c9c216c1c6..e23412a2c56b 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -59,7 +59,7 @@ static void ima_iint_init_always(struct ima_iint_cache *iint,
struct inode *inode)
{
iint->ima_hash = NULL;
- iint->version = 0;
+ iint->real_inode.version = 0;
iint->flags = 0UL;
iint->atomic_flags = 0UL;
iint->ima_file_status = INTEGRITY_UNKNOWN;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 393f5c7912d5..4e208239a40e 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -48,12 +48,14 @@ static int __init ima_add_boot_aggregate(void)
struct ima_event_data event_data = { .iint = iint,
.filename = boot_aggregate_name };
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 violation = 0;
memset(iint, 0, sizeof(*iint));
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->length = hash_digest_size[ima_hash_algo];
@@ -70,7 +72,7 @@ static int __init ima_add_boot_aggregate(void)
* is not found.
*/
if (ima_tpm_chip) {
- result = ima_calc_boot_aggregate(&hash.hdr);
+ result = ima_calc_boot_aggregate(hash_hdr);
if (result < 0) {
audit_cause = "hashing_error";
goto err_out;
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index dadc1d138118..52e00332defe 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -30,6 +30,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
goto out;
}
+ file.file = NULL;
file.size = segment_size;
file.read_pos = 0;
file.count = sizeof(khdr); /* reserved space */
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index c84e8c55333d..f04f43af651c 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -26,6 +26,7 @@
#include <linux/ima.h>
#include <linux/fs.h>
#include <linux/iversion.h>
+#include <linux/evm.h>
#include "ima.h"
@@ -173,7 +174,7 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT) ||
!(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->measured_pcrs = 0;
if (update)
@@ -208,9 +209,10 @@ static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
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_template_desc *template_desc = NULL;
+ struct inode *metadata_inode;
char *pathbuf = NULL;
char filename[NAME_MAX];
const char *pathname = NULL;
@@ -285,17 +287,28 @@ static int process_measurement(struct file *file, const struct cred *cred,
iint->measured_pcrs = 0;
}
- /* Detect and re-evaluate changes made to the backing file. */
- backing_inode = d_real_inode(file_dentry(file));
- if (backing_inode != inode &&
+ /*
+ * On stacked filesystems, detect and re-evaluate file data and
+ * metadata changes.
+ */
+ real_inode = d_real_inode(file_dentry(file));
+ if (real_inode != inode &&
(action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
- if (!IS_I_VERSION(backing_inode) ||
- backing_inode->i_sb->s_dev != iint->real_dev ||
- backing_inode->i_ino != iint->real_ino ||
- !inode_eq_iversion(backing_inode, iint->version)) {
+ if (!IS_I_VERSION(real_inode) ||
+ integrity_inode_attrs_changed(&iint->real_inode,
+ real_inode)) {
iint->flags &= ~IMA_DONE_MASK;
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
@@ -902,6 +915,13 @@ static int ima_post_load_data(char *buf, loff_t size,
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;
}
@@ -941,6 +961,8 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
.buf_len = size};
struct ima_template_desc *template;
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];
int digest_hash_len = hash_digest_size[ima_hash_algo];
int violation = 0;
@@ -979,7 +1001,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
if (!pcr)
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->length = hash_digest_size[ima_hash_algo];
@@ -990,7 +1012,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
}
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,
iint.ima_hash);
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 6cd0add524cd..4183956c53af 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -339,6 +339,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
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;
u32 cur_digestsize = 0;
struct inode *inode;
@@ -358,7 +360,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
if ((const char *)event_data->filename == boot_aggregate_name) {
if (ima_tpm_chip) {
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 */
if (!result && hash.hdr.algo != HASH_ALGO_SHA1)
@@ -368,7 +370,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
memset(&hash, 0, sizeof(hash));
}
- cur_digest = hash.hdr.digest;
+ cur_digest = hash_hdr->digest;
cur_digestsize = hash_digest_size[HASH_ALGO_SHA1];
goto out;
}
@@ -379,14 +381,14 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
inode = file_inode(event_data->file);
hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
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) {
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
event_data->filename, "collect_data",
"failed", result, 0);
return result;
}
- cur_digest = hash.hdr.digest;
+ cur_digest = hash_hdr->digest;
cur_digestsize = hash.hdr.length;
out:
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,
bool size_limit)
{
const char *cur_filename = NULL;
+ struct name_snapshot filename;
u32 cur_filename_len = 0;
+ bool snapshot = false;
+ int ret;
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,
}
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);
} else
/*
@@ -505,8 +513,13 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
*/
cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
out:
- return ima_write_template_field_data(cur_filename, cur_filename_len,
- DATA_FMT_STRING, field_data);
+ ret = ima_write_template_field_data(cur_filename, cur_filename_len,
+ DATA_FMT_STRING, field_data);
+
+ if (snapshot)
+ release_dentry_name_snapshot(&filename);
+
+ return ret;
}
/*
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 50d6f798e613..660f76cb69d3 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -31,19 +31,24 @@ enum evm_ima_xattr_type {
};
struct evm_ima_xattr_data {
- u8 type;
+ /* New members must be added within the __struct_group() macro below. */
+ __struct_group(evm_ima_xattr_data_hdr, hdr, __packed,
+ u8 type;
+ );
u8 data[];
} __packed;
/* Only used in the EVM HMAC code. */
struct evm_xattr {
- struct evm_ima_xattr_data data;
+ struct evm_ima_xattr_data_hdr data;
u8 digest[SHA1_DIGEST_SIZE];
} __packed;
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
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 length;
union {
@@ -57,6 +62,7 @@ struct ima_digest_data {
} ng;
u8 data[2];
} xattr;
+ );
u8 digest[];
} __packed;
@@ -65,7 +71,7 @@ struct ima_digest_data {
* with the maximum hash size, define ima_max_digest_data struct.
*/
struct ima_max_digest_data {
- struct ima_digest_data hdr;
+ struct ima_digest_data_hdr hdr;
u8 digest[HASH_MAX_DIGESTSIZE];
} __packed;
diff --git a/security/security.c b/security/security.c
index 0a9a0ac3f266..e5da848c50b9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2628,6 +2628,7 @@ EXPORT_SYMBOL(security_inode_copy_up);
/**
* security_inode_copy_up_xattr() - Filter xattrs in an overlayfs copy-up op
+ * @src: union dentry of copy-up file
* @name: xattr name
*
* 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);
* if the security module does not know about attribute, or a negative
* 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;
@@ -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
* 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))
return rc;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e92061019bea..7eed331e90f0 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3526,7 +3526,7 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
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
* don't then want to overwrite it by blindly copying all the lower
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index efeac8365ad0..70ba2841e181 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4886,7 +4886,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
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.