summaryrefslogtreecommitdiff
path: root/security/integrity/ima/ima_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity/ima/ima_api.c')
-rw-r--r--security/integrity/ima/ima_api.c128
1 files changed, 92 insertions, 36 deletions
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index bf22de8b7ce0..c35ea613c9f8 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -13,7 +13,7 @@
#include <linux/fs.h>
#include <linux/xattr.h>
#include <linux/evm.h>
-#include <linux/iversion.h>
+#include <linux/fsverity.h>
#include "ima.h"
@@ -131,8 +131,8 @@ int ima_store_template(struct ima_template_entry *entry,
* value is invalidated.
*/
void ima_add_violation(struct file *file, const unsigned char *filename,
- struct integrity_iint_cache *iint,
- const char *op, const char *cause)
+ struct ima_iint_cache *iint, const char *op,
+ const char *cause)
{
struct ima_template_entry *entry;
struct inode *inode = file_inode(file);
@@ -162,38 +162,70 @@ err_out:
/**
* ima_get_action - appraise & measure decision based on policy.
- * @inode: pointer to inode to measure
+ * @idmap: idmap of the mount the inode was found from
+ * @inode: pointer to the inode associated with the object being validated
* @cred: pointer to credentials structure to validate
- * @secid: secid of the task being validated
+ * @prop: properties of the task being validated
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
* MAY_APPEND)
* @func: caller identifier
* @pcr: pointer filled in if matched measure policy sets pcr=
* @template_desc: pointer filled in if matched measure policy sets template=
- * @keyring: keyring name used to determine the action
+ * @func_data: func specific data, may be NULL
+ * @allowed_algos: allowlist of hash algorithms for the IMA xattr
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
- * | KEXEC_CMDLINE | KEY_CHECK
+ * | KEXEC_CMDLINE | KEY_CHECK | CRITICAL_DATA | SETXATTR_CHECK
+ * | MMAP_CHECK_REQPROT
* mask: contains the permission mask
* fsmagic: hex value
*
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
-int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
- int mask, enum ima_hooks func, int *pcr,
+int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
+ const struct cred *cred, struct lsm_prop *prop, int mask,
+ enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
- const char *keyring)
+ const char *func_data, unsigned int *allowed_algos)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
- return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
- template_desc, keyring);
+ return ima_match_policy(idmap, inode, cred, prop, func, mask,
+ flags, pcr, template_desc, func_data,
+ allowed_algos);
+}
+
+static bool ima_get_verity_digest(struct ima_iint_cache *iint,
+ struct inode *inode,
+ struct ima_max_digest_data *hash)
+{
+ enum hash_algo alg;
+ int digest_len;
+
+ /*
+ * On failure, 'measure' policy rules will result in a file data
+ * hash containing 0's.
+ */
+ digest_len = fsverity_get_digest(inode, hash->digest, NULL, &alg);
+ if (digest_len == 0)
+ return false;
+
+ /*
+ * Unlike in the case of actually calculating the file hash, in
+ * the fsverity case regardless of the hash algorithm, return
+ * the verity digest to be included in the measurement list. A
+ * mismatch between the verity algorithm and the xattr signature
+ * algorithm, if one exists, will be detected later.
+ */
+ hash->hdr.algo = alg;
+ hash->hdr.length = digest_len;
+ return true;
}
/*
@@ -206,21 +238,22 @@ int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
*
* Return 0 on success, error code otherwise
*/
-int ima_collect_measurement(struct integrity_iint_cache *iint,
- struct file *file, void *buf, loff_t size,
- enum hash_algo algo, struct modsig *modsig)
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
+ void *buf, loff_t size, enum hash_algo algo,
+ struct modsig *modsig)
{
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
- const char *filename = file->f_path.dentry->d_name.name;
+ struct inode *real_inode = d_real_inode(file_dentry(file));
+ 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;
void *tmpbuf;
- u64 i_version;
- struct {
- struct ima_digest_data hdr;
- char digest[IMA_MAX_DIGEST_SIZE];
- } hash;
+ u64 i_version = 0;
/*
* Always collect the modsig, because IMA might have already collected
@@ -234,20 +267,31 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
goto out;
/*
- * Dectecting file change is based on i_version. On filesystems
- * which do not support i_version, support is limited to an initial
- * measurement/appraisal/audit.
+ * Detecting file change is based on i_version. On filesystems
+ * which do not support i_version, support was originally limited
+ * to an initial measurement/appraisal/audit, but was modified to
+ * assume the file changed.
*/
- i_version = inode_query_iversion(inode);
+ result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
+ AT_STATX_SYNC_AS_STAT);
+ if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
+ i_version = stat.change_cookie;
hash.hdr.algo = algo;
+ hash.hdr.length = hash_digest_size[algo];
/* Initialize hash digest to 0's in case of failure */
memset(&hash.digest, 0, sizeof(hash.digest));
- if (buf)
- result = ima_calc_buffer_hash(buf, size, &hash.hdr);
- else
- result = ima_calc_file_hash(file, &hash.hdr);
+ if (iint->flags & IMA_VERITY_REQUIRED) {
+ if (!ima_get_verity_digest(iint, inode, &hash)) {
+ audit_cause = "no-verity-digest";
+ result = -ENODATA;
+ }
+ } else if (buf) {
+ result = ima_calc_buffer_hash(buf, size, hash_hdr);
+ } else {
+ result = ima_calc_file_hash(file, hash_hdr);
+ }
if (result && result != -EBADF && result != -EINVAL)
goto out;
@@ -261,7 +305,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
- iint->version = i_version;
+ 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)
@@ -271,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;
}
@@ -293,8 +345,8 @@ out:
*
* Must be called with iint->mutex held.
*/
-void ima_store_measurement(struct integrity_iint_cache *iint,
- struct file *file, const unsigned char *filename,
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc)
@@ -337,7 +389,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
ima_free_template_entry(entry);
}
-void ima_audit_measurement(struct integrity_iint_cache *iint,
+void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename)
{
struct audit_buffer *ab;
@@ -386,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();
@@ -399,7 +452,10 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
}
if (!pathname) {
- strlcpy(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;
}