Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull integrity updates from James Morris: "This contains a mixture of bug fixes, code cleanup, and new functionality. Of note is the integrity cache locking fix, file change detection, and support for a new EVM portable and immutable signature type. The re-introduction of the integrity cache lock (iint) fixes the problem of attempting to take the i_rwsem shared a second time, when it was previously taken exclusively. Defining atomic flags resolves the original iint/i_rwsem circular locking - accessing the file data vs. modifying the file metadata. Although it fixes the O_DIRECT problem as well, a subsequent patch is needed to remove the explicit O_DIRECT prevention. For performance reasons, detecting when a file has changed and needs to be re-measured, re-appraised, and/or re-audited, was limited to after the last writer has closed, and only if the file data has changed. Detecting file change is based on i_version. For filesystems that do not support i_version, remote filesystems, or userspace filesystems, the file was measured, appraised and/or audited once and never re-evaluated. Now local filesystems, which do not support i_version or are not mounted with the i_version option, assume the file has changed and are required to re-evaluate the file. This change does not address detecting file change on remote or userspace filesystems. Unlike file data signatures, which can be included and distributed in software packages (eg. rpm, deb), the existing EVM signature, which protects the file metadata, could not be included in software packages, as it includes file system specific information (eg. i_ino, possibly the UUID). This pull request defines a new EVM portable and immutable file metadata signature format, which can be included in software packages" * 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: ima/policy: fix parsing of fsuuid ima: Use i_version only when filesystem supports it integrity: remove unneeded initializations in integrity_iint_cache entries ima: log message to module appraisal error ima: pass filename to ima_rdwr_violation_check() ima: Fix line continuation format ima: support new "hash" and "dont_hash" policy actions ima: re-introduce own integrity cache lock EVM: Add support for portable signature format EVM: Allow userland to permit modification of EVM-protected metadata ima: relax requiring a file signature for new files with zero length
This commit is contained in:
commit
3c29548f87
|
@ -14,30 +14,46 @@ Description:
|
||||||
generated either locally or remotely using an
|
generated either locally or remotely using an
|
||||||
asymmetric key. These keys are loaded onto root's
|
asymmetric key. These keys are loaded onto root's
|
||||||
keyring using keyctl, and EVM is then enabled by
|
keyring using keyctl, and EVM is then enabled by
|
||||||
echoing a value to <securityfs>/evm:
|
echoing a value to <securityfs>/evm made up of the
|
||||||
|
following bits:
|
||||||
|
|
||||||
1: enable HMAC validation and creation
|
Bit Effect
|
||||||
2: enable digital signature validation
|
0 Enable HMAC validation and creation
|
||||||
3: enable HMAC and digital signature validation and HMAC
|
1 Enable digital signature validation
|
||||||
creation
|
2 Permit modification of EVM-protected metadata at
|
||||||
|
runtime. Not supported if HMAC validation and
|
||||||
|
creation is enabled.
|
||||||
|
31 Disable further runtime modification of EVM policy
|
||||||
|
|
||||||
Further writes will be blocked if HMAC support is enabled or
|
For example:
|
||||||
if bit 32 is set:
|
|
||||||
|
|
||||||
echo 0x80000002 ><securityfs>/evm
|
echo 1 ><securityfs>/evm
|
||||||
|
|
||||||
will enable digital signature validation and block
|
will enable HMAC validation and creation
|
||||||
further writes to <securityfs>/evm.
|
|
||||||
|
|
||||||
Until this is done, EVM can not create or validate the
|
echo 0x80000003 ><securityfs>/evm
|
||||||
'security.evm' xattr, but returns INTEGRITY_UNKNOWN.
|
|
||||||
Loading keys and signaling EVM should be done as early
|
|
||||||
as possible. Normally this is done in the initramfs,
|
|
||||||
which has already been measured as part of the trusted
|
|
||||||
boot. For more information on creating and loading
|
|
||||||
existing trusted/encrypted keys, refer to:
|
|
||||||
|
|
||||||
Documentation/security/keys/trusted-encrypted.rst. Both dracut
|
will enable HMAC and digital signature validation and
|
||||||
(via 97masterkey and 98integrity) and systemd (via
|
HMAC creation and disable all further modification of policy.
|
||||||
|
|
||||||
|
echo 0x80000006 ><securityfs>/evm
|
||||||
|
|
||||||
|
will enable digital signature validation, permit
|
||||||
|
modification of EVM-protected metadata and
|
||||||
|
disable all further modification of policy
|
||||||
|
|
||||||
|
Note that once a key has been loaded, it will no longer be
|
||||||
|
possible to enable metadata modification.
|
||||||
|
|
||||||
|
Until key loading has been signaled EVM can not create
|
||||||
|
or validate the 'security.evm' xattr, but returns
|
||||||
|
INTEGRITY_UNKNOWN. Loading keys and signaling EVM
|
||||||
|
should be done as early as possible. Normally this is
|
||||||
|
done in the initramfs, which has already been measured
|
||||||
|
as part of the trusted boot. For more information on
|
||||||
|
creating and loading existing trusted/encrypted keys,
|
||||||
|
refer to:
|
||||||
|
Documentation/security/keys/trusted-encrypted.rst. Both
|
||||||
|
dracut (via 97masterkey and 98integrity) and systemd (via
|
||||||
core/ima-setup) have support for loading keys at boot
|
core/ima-setup) have support for loading keys at boot
|
||||||
time.
|
time.
|
||||||
|
|
|
@ -17,7 +17,8 @@ Description:
|
||||||
|
|
||||||
rule format: action [condition ...]
|
rule format: action [condition ...]
|
||||||
|
|
||||||
action: measure | dont_measure | appraise | dont_appraise | audit
|
action: measure | dont_measure | appraise | dont_appraise |
|
||||||
|
audit | hash | dont_hash
|
||||||
condition:= base | lsm [option]
|
condition:= base | lsm [option]
|
||||||
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
|
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
|
||||||
[euid=] [fowner=]]
|
[euid=] [fowner=]]
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
enum integrity_status {
|
enum integrity_status {
|
||||||
INTEGRITY_PASS = 0,
|
INTEGRITY_PASS = 0,
|
||||||
|
INTEGRITY_PASS_IMMUTABLE,
|
||||||
INTEGRITY_FAIL,
|
INTEGRITY_FAIL,
|
||||||
INTEGRITY_NOLABEL,
|
INTEGRITY_NOLABEL,
|
||||||
INTEGRITY_NOXATTRS,
|
INTEGRITY_NOXATTRS,
|
||||||
|
|
|
@ -23,9 +23,12 @@
|
||||||
|
|
||||||
#define EVM_INIT_HMAC 0x0001
|
#define EVM_INIT_HMAC 0x0001
|
||||||
#define EVM_INIT_X509 0x0002
|
#define EVM_INIT_X509 0x0002
|
||||||
#define EVM_SETUP 0x80000000 /* userland has signaled key load */
|
#define EVM_ALLOW_METADATA_WRITES 0x0004
|
||||||
|
#define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */
|
||||||
|
|
||||||
#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP)
|
#define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509)
|
||||||
|
#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \
|
||||||
|
EVM_ALLOW_METADATA_WRITES)
|
||||||
|
|
||||||
extern int evm_initialized;
|
extern int evm_initialized;
|
||||||
extern char *evm_hmac;
|
extern char *evm_hmac;
|
||||||
|
@ -51,7 +54,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
|
||||||
size_t req_xattr_value_len, char *digest);
|
size_t req_xattr_value_len, char *digest);
|
||||||
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 *digest);
|
size_t req_xattr_value_len, char type, char *digest);
|
||||||
int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
|
int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
|
||||||
char *hmac_val);
|
char *hmac_val);
|
||||||
int evm_init_secfs(void);
|
int evm_init_secfs(void);
|
||||||
|
|
|
@ -138,7 +138,7 @@ out:
|
||||||
* protection.)
|
* protection.)
|
||||||
*/
|
*/
|
||||||
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
|
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
|
||||||
char *digest)
|
char type, char *digest)
|
||||||
{
|
{
|
||||||
struct h_misc {
|
struct h_misc {
|
||||||
unsigned long ino;
|
unsigned long ino;
|
||||||
|
@ -149,8 +149,13 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
|
||||||
} hmac_misc;
|
} hmac_misc;
|
||||||
|
|
||||||
memset(&hmac_misc, 0, sizeof(hmac_misc));
|
memset(&hmac_misc, 0, sizeof(hmac_misc));
|
||||||
hmac_misc.ino = inode->i_ino;
|
/* Don't include the inode or generation number in portable
|
||||||
hmac_misc.generation = inode->i_generation;
|
* signatures
|
||||||
|
*/
|
||||||
|
if (type != EVM_XATTR_PORTABLE_DIGSIG) {
|
||||||
|
hmac_misc.ino = inode->i_ino;
|
||||||
|
hmac_misc.generation = inode->i_generation;
|
||||||
|
}
|
||||||
/* The hmac uid and gid must be encoded in the initial user
|
/* The hmac uid and gid must be encoded in the initial user
|
||||||
* namespace (not the filesystems user namespace) as encoding
|
* namespace (not the filesystems user namespace) as encoding
|
||||||
* them in the filesystems user namespace allows an attack
|
* them in the filesystems user namespace allows an attack
|
||||||
|
@ -163,7 +168,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
|
||||||
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
|
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
|
||||||
hmac_misc.mode = inode->i_mode;
|
hmac_misc.mode = inode->i_mode;
|
||||||
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
|
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
|
||||||
if (evm_hmac_attrs & EVM_ATTR_FSUUID)
|
if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
|
||||||
|
type != EVM_XATTR_PORTABLE_DIGSIG)
|
||||||
crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0],
|
crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0],
|
||||||
sizeof(inode->i_sb->s_uuid));
|
sizeof(inode->i_sb->s_uuid));
|
||||||
crypto_shash_final(desc, digest);
|
crypto_shash_final(desc, digest);
|
||||||
|
@ -189,6 +195,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
||||||
char *xattr_value = NULL;
|
char *xattr_value = NULL;
|
||||||
int error;
|
int error;
|
||||||
int size;
|
int size;
|
||||||
|
bool ima_present = false;
|
||||||
|
|
||||||
if (!(inode->i_opflags & IOP_XATTR))
|
if (!(inode->i_opflags & IOP_XATTR))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -199,11 +206,18 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
||||||
|
|
||||||
error = -ENODATA;
|
error = -ENODATA;
|
||||||
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
|
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
|
||||||
|
bool is_ima = false;
|
||||||
|
|
||||||
|
if (strcmp(*xattrname, XATTR_NAME_IMA) == 0)
|
||||||
|
is_ima = true;
|
||||||
|
|
||||||
if ((req_xattr_name && req_xattr_value)
|
if ((req_xattr_name && req_xattr_value)
|
||||||
&& !strcmp(*xattrname, req_xattr_name)) {
|
&& !strcmp(*xattrname, req_xattr_name)) {
|
||||||
error = 0;
|
error = 0;
|
||||||
crypto_shash_update(desc, (const u8 *)req_xattr_value,
|
crypto_shash_update(desc, (const u8 *)req_xattr_value,
|
||||||
req_xattr_value_len);
|
req_xattr_value_len);
|
||||||
|
if (is_ima)
|
||||||
|
ima_present = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
size = vfs_getxattr_alloc(dentry, *xattrname,
|
size = vfs_getxattr_alloc(dentry, *xattrname,
|
||||||
|
@ -218,9 +232,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
||||||
error = 0;
|
error = 0;
|
||||||
xattr_size = size;
|
xattr_size = size;
|
||||||
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
|
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
|
||||||
|
if (is_ima)
|
||||||
|
ima_present = true;
|
||||||
}
|
}
|
||||||
hmac_add_misc(desc, inode, digest);
|
hmac_add_misc(desc, inode, type, digest);
|
||||||
|
|
||||||
|
/* Portable EVM signatures must include an IMA hash */
|
||||||
|
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
|
||||||
|
return -EPERM;
|
||||||
out:
|
out:
|
||||||
kfree(xattr_value);
|
kfree(xattr_value);
|
||||||
kfree(desc);
|
kfree(desc);
|
||||||
|
@ -232,17 +251,45 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
|
||||||
char *digest)
|
char *digest)
|
||||||
{
|
{
|
||||||
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, digest);
|
req_xattr_value_len, EVM_XATTR_HMAC, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 *digest)
|
char type, char *digest)
|
||||||
{
|
{
|
||||||
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, IMA_XATTR_DIGEST, digest);
|
req_xattr_value_len, type, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
|
||||||
|
{
|
||||||
|
const struct evm_ima_xattr_data *xattr_data = NULL;
|
||||||
|
struct integrity_iint_cache *iint;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
iint = integrity_iint_find(inode);
|
||||||
|
if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Do this the hard way */
|
||||||
|
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
|
||||||
|
GFP_NOFS);
|
||||||
|
if (rc <= 0) {
|
||||||
|
if (rc == -ENODATA)
|
||||||
|
return 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
|
||||||
|
rc = 1;
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
kfree(xattr_data);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the hmac and update security.evm xattr
|
* Calculate the hmac and update security.evm xattr
|
||||||
*
|
*
|
||||||
|
@ -255,6 +302,16 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
struct evm_ima_xattr_data xattr_data;
|
struct evm_ima_xattr_data xattr_data;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't permit any transformation of the EVM xattr if the signature
|
||||||
|
* is of an immutable type
|
||||||
|
*/
|
||||||
|
rc = evm_is_immutable(dentry, inode);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
if (rc)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
|
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
|
||||||
xattr_value_len, xattr_data.digest);
|
xattr_value_len, xattr_data.digest);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
|
@ -280,7 +337,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
|
crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
|
||||||
hmac_add_misc(desc, inode, hmac_val);
|
hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
|
||||||
kfree(desc);
|
kfree(desc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
int evm_initialized;
|
int evm_initialized;
|
||||||
|
|
||||||
static char *integrity_status_msg[] = {
|
static char *integrity_status_msg[] = {
|
||||||
"pass", "fail", "no_label", "no_xattrs", "unknown"
|
"pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown"
|
||||||
};
|
};
|
||||||
char *evm_hmac = "hmac(sha1)";
|
char *evm_hmac = "hmac(sha1)";
|
||||||
char *evm_hash = "sha1";
|
char *evm_hash = "sha1";
|
||||||
|
@ -76,6 +76,11 @@ static void __init evm_init_config(void)
|
||||||
pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs);
|
pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool evm_key_loaded(void)
|
||||||
|
{
|
||||||
|
return (bool)(evm_initialized & EVM_KEY_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
static int evm_find_protected_xattrs(struct dentry *dentry)
|
static int evm_find_protected_xattrs(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_backing_inode(dentry);
|
struct inode *inode = d_backing_inode(dentry);
|
||||||
|
@ -123,7 +128,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
|
||||||
enum integrity_status evm_status = INTEGRITY_PASS;
|
enum integrity_status evm_status = INTEGRITY_PASS;
|
||||||
int rc, xattr_len;
|
int rc, xattr_len;
|
||||||
|
|
||||||
if (iint && iint->evm_status == INTEGRITY_PASS)
|
if (iint && (iint->evm_status == INTEGRITY_PASS ||
|
||||||
|
iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
|
||||||
return iint->evm_status;
|
return iint->evm_status;
|
||||||
|
|
||||||
/* if status is not PASS, try to check again - against -ENOMEM */
|
/* if status is not PASS, try to check again - against -ENOMEM */
|
||||||
|
@ -164,22 +170,26 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
break;
|
break;
|
||||||
case EVM_IMA_XATTR_DIGSIG:
|
case EVM_IMA_XATTR_DIGSIG:
|
||||||
|
case EVM_XATTR_PORTABLE_DIGSIG:
|
||||||
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
|
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
|
||||||
xattr_value_len, calc.digest);
|
xattr_value_len, xattr_data->type,
|
||||||
|
calc.digest);
|
||||||
if (rc)
|
if (rc)
|
||||||
break;
|
break;
|
||||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
|
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
|
||||||
(const char *)xattr_data, xattr_len,
|
(const char *)xattr_data, xattr_len,
|
||||||
calc.digest, sizeof(calc.digest));
|
calc.digest, sizeof(calc.digest));
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
/* Replace RSA with HMAC if not mounted readonly and
|
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
|
||||||
* not immutable
|
if (iint)
|
||||||
*/
|
iint->flags |= EVM_IMMUTABLE_DIGSIG;
|
||||||
if (!IS_RDONLY(d_backing_inode(dentry)) &&
|
evm_status = INTEGRITY_PASS_IMMUTABLE;
|
||||||
!IS_IMMUTABLE(d_backing_inode(dentry)))
|
} else if (!IS_RDONLY(d_backing_inode(dentry)) &&
|
||||||
|
!IS_IMMUTABLE(d_backing_inode(dentry))) {
|
||||||
evm_update_evmxattr(dentry, xattr_name,
|
evm_update_evmxattr(dentry, xattr_name,
|
||||||
xattr_value,
|
xattr_value,
|
||||||
xattr_value_len);
|
xattr_value_len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -241,7 +251,7 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
|
||||||
void *xattr_value, size_t xattr_value_len,
|
void *xattr_value, size_t xattr_value_len,
|
||||||
struct integrity_iint_cache *iint)
|
struct integrity_iint_cache *iint)
|
||||||
{
|
{
|
||||||
if (!evm_initialized || !evm_protected_xattr(xattr_name))
|
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
|
||||||
return INTEGRITY_UNKNOWN;
|
return INTEGRITY_UNKNOWN;
|
||||||
|
|
||||||
if (!iint) {
|
if (!iint) {
|
||||||
|
@ -265,7 +275,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_backing_inode(dentry);
|
struct inode *inode = d_backing_inode(dentry);
|
||||||
|
|
||||||
if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
|
if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
|
||||||
return 0;
|
return 0;
|
||||||
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
|
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
|
||||||
}
|
}
|
||||||
|
@ -280,7 +290,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
|
||||||
* affect security.evm. An interesting side affect of writing posix xattr
|
* affect security.evm. An interesting side affect of writing posix xattr
|
||||||
* acls is their modifying of the i_mode, which is included in security.evm.
|
* acls is their modifying of the i_mode, which is included in security.evm.
|
||||||
* For posix xattr acls only, permit security.evm, even if it currently
|
* For posix xattr acls only, permit security.evm, even if it currently
|
||||||
* doesn't exist, to be updated.
|
* doesn't exist, to be updated unless the EVM signature is immutable.
|
||||||
*/
|
*/
|
||||||
static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
|
static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
|
||||||
const void *xattr_value, size_t xattr_value_len)
|
const void *xattr_value, size_t xattr_value_len)
|
||||||
|
@ -299,6 +309,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
|
||||||
return 0;
|
return 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
evm_status = evm_verify_current_integrity(dentry);
|
evm_status = evm_verify_current_integrity(dentry);
|
||||||
if (evm_status == INTEGRITY_NOXATTRS) {
|
if (evm_status == INTEGRITY_NOXATTRS) {
|
||||||
struct integrity_iint_cache *iint;
|
struct integrity_iint_cache *iint;
|
||||||
|
@ -345,10 +356,17 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
{
|
{
|
||||||
const struct evm_ima_xattr_data *xattr_data = xattr_value;
|
const struct evm_ima_xattr_data *xattr_data = xattr_value;
|
||||||
|
|
||||||
|
/* Policy permits modification of the protected xattrs even though
|
||||||
|
* there's no HMAC key loaded
|
||||||
|
*/
|
||||||
|
if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
|
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
|
||||||
if (!xattr_value_len)
|
if (!xattr_value_len)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (xattr_data->type != EVM_IMA_XATTR_DIGSIG)
|
if (xattr_data->type != EVM_IMA_XATTR_DIGSIG &&
|
||||||
|
xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
return evm_protect_xattr(dentry, xattr_name, xattr_value,
|
return evm_protect_xattr(dentry, xattr_name, xattr_value,
|
||||||
|
@ -365,6 +383,12 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
*/
|
*/
|
||||||
int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
|
int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||||
{
|
{
|
||||||
|
/* Policy permits modification of the protected xattrs even though
|
||||||
|
* there's no HMAC key loaded
|
||||||
|
*/
|
||||||
|
if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return evm_protect_xattr(dentry, xattr_name, NULL, 0);
|
return evm_protect_xattr(dentry, xattr_name, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,8 +417,8 @@ static void evm_reset_status(struct inode *inode)
|
||||||
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
|
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
const void *xattr_value, size_t xattr_value_len)
|
const void *xattr_value, size_t xattr_value_len)
|
||||||
{
|
{
|
||||||
if (!evm_initialized || (!evm_protected_xattr(xattr_name)
|
if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name)
|
||||||
&& !posix_xattr_acl(xattr_name)))
|
&& !posix_xattr_acl(xattr_name)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
evm_reset_status(dentry->d_inode);
|
evm_reset_status(dentry->d_inode);
|
||||||
|
@ -414,7 +438,7 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
*/
|
*/
|
||||||
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
|
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||||
{
|
{
|
||||||
if (!evm_initialized || !evm_protected_xattr(xattr_name))
|
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
evm_reset_status(dentry->d_inode);
|
evm_reset_status(dentry->d_inode);
|
||||||
|
@ -425,12 +449,21 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||||
/**
|
/**
|
||||||
* evm_inode_setattr - prevent updating an invalid EVM extended attribute
|
* evm_inode_setattr - prevent updating an invalid EVM extended attribute
|
||||||
* @dentry: pointer to the affected dentry
|
* @dentry: pointer to the affected dentry
|
||||||
|
*
|
||||||
|
* Permit update of file attributes when files have a valid EVM signature,
|
||||||
|
* except in the case of them having an immutable portable signature.
|
||||||
*/
|
*/
|
||||||
int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
|
int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
{
|
{
|
||||||
unsigned int ia_valid = attr->ia_valid;
|
unsigned int ia_valid = attr->ia_valid;
|
||||||
enum integrity_status evm_status;
|
enum integrity_status evm_status;
|
||||||
|
|
||||||
|
/* Policy permits modification of the protected attrs even though
|
||||||
|
* there's no HMAC key loaded
|
||||||
|
*/
|
||||||
|
if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
|
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
|
||||||
return 0;
|
return 0;
|
||||||
evm_status = evm_verify_current_integrity(dentry);
|
evm_status = evm_verify_current_integrity(dentry);
|
||||||
|
@ -456,7 +489,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
*/
|
*/
|
||||||
void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
|
void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
|
||||||
{
|
{
|
||||||
if (!evm_initialized)
|
if (!evm_key_loaded())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
|
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
|
||||||
|
@ -473,7 +506,7 @@ int evm_inode_init_security(struct inode *inode,
|
||||||
struct evm_ima_xattr_data *xattr_data;
|
struct evm_ima_xattr_data *xattr_data;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
|
if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
|
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
|
||||||
|
|
|
@ -40,7 +40,7 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
|
||||||
if (*ppos != 0)
|
if (*ppos != 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP));
|
sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
|
||||||
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
|
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -63,7 +63,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP))
|
if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
ret = kstrtoint_from_user(buf, count, 0, &i);
|
ret = kstrtoint_from_user(buf, count, 0, &i);
|
||||||
|
@ -75,16 +75,30 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
|
||||||
if (!i || (i & ~EVM_INIT_MASK) != 0)
|
if (!i || (i & ~EVM_INIT_MASK) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Don't allow a request to freshly enable metadata writes if
|
||||||
|
* keys are loaded.
|
||||||
|
*/
|
||||||
|
if ((i & EVM_ALLOW_METADATA_WRITES) &&
|
||||||
|
((evm_initialized & EVM_KEY_MASK) != 0) &&
|
||||||
|
!(evm_initialized & EVM_ALLOW_METADATA_WRITES))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
if (i & EVM_INIT_HMAC) {
|
if (i & EVM_INIT_HMAC) {
|
||||||
ret = evm_init_key();
|
ret = evm_init_key();
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
/* Forbid further writes after the symmetric key is loaded */
|
/* Forbid further writes after the symmetric key is loaded */
|
||||||
i |= EVM_SETUP;
|
i |= EVM_SETUP_COMPLETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
evm_initialized |= i;
|
evm_initialized |= i;
|
||||||
|
|
||||||
|
/* Don't allow protected metadata modification if a symmetric key
|
||||||
|
* is loaded
|
||||||
|
*/
|
||||||
|
if (evm_initialized & EVM_INIT_HMAC)
|
||||||
|
evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,14 +153,12 @@ static void init_once(void *foo)
|
||||||
struct integrity_iint_cache *iint = foo;
|
struct integrity_iint_cache *iint = foo;
|
||||||
|
|
||||||
memset(iint, 0, sizeof(*iint));
|
memset(iint, 0, sizeof(*iint));
|
||||||
iint->version = 0;
|
|
||||||
iint->flags = 0UL;
|
|
||||||
iint->ima_file_status = INTEGRITY_UNKNOWN;
|
iint->ima_file_status = INTEGRITY_UNKNOWN;
|
||||||
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
|
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
|
||||||
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
|
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
|
||||||
iint->ima_read_status = INTEGRITY_UNKNOWN;
|
iint->ima_read_status = INTEGRITY_UNKNOWN;
|
||||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||||
iint->measured_pcrs = 0;
|
mutex_init(&iint->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init integrity_iintcache_init(void)
|
static int __init integrity_iintcache_init(void)
|
||||||
|
|
|
@ -175,7 +175,7 @@ err_out:
|
||||||
*/
|
*/
|
||||||
int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
|
int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
|
||||||
{
|
{
|
||||||
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
|
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
|
||||||
|
|
||||||
flags &= ima_policy_flag;
|
flags &= ima_policy_flag;
|
||||||
|
|
||||||
|
|
|
@ -46,14 +46,15 @@ bool is_ima_appraise_enabled(void)
|
||||||
/*
|
/*
|
||||||
* ima_must_appraise - set appraise flag
|
* ima_must_appraise - set appraise flag
|
||||||
*
|
*
|
||||||
* Return 1 to appraise
|
* Return 1 to appraise or hash
|
||||||
*/
|
*/
|
||||||
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
|
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
|
||||||
{
|
{
|
||||||
if (!ima_appraise)
|
if (!ima_appraise)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL);
|
return ima_match_policy(inode, func, mask, IMA_APPRAISE | IMA_HASH,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ima_fix_xattr(struct dentry *dentry,
|
static int ima_fix_xattr(struct dentry *dentry,
|
||||||
|
@ -223,13 +224,16 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
if (opened & FILE_CREATED)
|
if (opened & FILE_CREATED)
|
||||||
iint->flags |= IMA_NEW_FILE;
|
iint->flags |= IMA_NEW_FILE;
|
||||||
if ((iint->flags & IMA_NEW_FILE) &&
|
if ((iint->flags & IMA_NEW_FILE) &&
|
||||||
!(iint->flags & IMA_DIGSIG_REQUIRED))
|
(!(iint->flags & IMA_DIGSIG_REQUIRED) ||
|
||||||
|
(inode->i_size == 0)))
|
||||||
status = INTEGRITY_PASS;
|
status = INTEGRITY_PASS;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
|
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
|
||||||
if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
|
if ((status != INTEGRITY_PASS) &&
|
||||||
|
(status != INTEGRITY_PASS_IMMUTABLE) &&
|
||||||
|
(status != INTEGRITY_UNKNOWN)) {
|
||||||
if ((status == INTEGRITY_NOLABEL)
|
if ((status == INTEGRITY_NOLABEL)
|
||||||
|| (status == INTEGRITY_NOXATTRS))
|
|| (status == INTEGRITY_NOXATTRS))
|
||||||
cause = "missing-HMAC";
|
cause = "missing-HMAC";
|
||||||
|
@ -248,6 +252,7 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
status = INTEGRITY_FAIL;
|
status = INTEGRITY_FAIL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
|
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
|
||||||
iint->ima_hash->length)
|
iint->ima_hash->length)
|
||||||
/* xattr length may be longer. md5 hash in previous
|
/* xattr length may be longer. md5 hash in previous
|
||||||
|
@ -266,7 +271,7 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||||
status = INTEGRITY_PASS;
|
status = INTEGRITY_PASS;
|
||||||
break;
|
break;
|
||||||
case EVM_IMA_XATTR_DIGSIG:
|
case EVM_IMA_XATTR_DIGSIG:
|
||||||
iint->flags |= IMA_DIGSIG;
|
set_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||||
(const char *)xattr_value, rc,
|
(const char *)xattr_value, rc,
|
||||||
iint->ima_hash->digest,
|
iint->ima_hash->digest,
|
||||||
|
@ -317,17 +322,20 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
/* do not collect and update hash for digital signatures */
|
/* do not collect and update hash for digital signatures */
|
||||||
if (iint->flags & IMA_DIGSIG)
|
if (test_bit(IMA_DIGSIG, &iint->atomic_flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (iint->ima_file_status != INTEGRITY_PASS)
|
if ((iint->ima_file_status != INTEGRITY_PASS) &&
|
||||||
|
!(iint->flags & IMA_HASH))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo);
|
rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
inode_lock(file_inode(file));
|
||||||
ima_fix_xattr(dentry, iint);
|
ima_fix_xattr(dentry, iint);
|
||||||
|
inode_unlock(file_inode(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -343,23 +351,21 @@ void ima_inode_post_setattr(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_backing_inode(dentry);
|
struct inode *inode = d_backing_inode(dentry);
|
||||||
struct integrity_iint_cache *iint;
|
struct integrity_iint_cache *iint;
|
||||||
int must_appraise;
|
int action;
|
||||||
|
|
||||||
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|
||||||
|| !(inode->i_opflags & IOP_XATTR))
|
|| !(inode->i_opflags & IOP_XATTR))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
||||||
|
if (!action)
|
||||||
|
__vfs_removexattr(dentry, XATTR_NAME_IMA);
|
||||||
iint = integrity_iint_find(inode);
|
iint = integrity_iint_find(inode);
|
||||||
if (iint) {
|
if (iint) {
|
||||||
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
|
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
|
||||||
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
|
if (!action)
|
||||||
IMA_ACTION_RULE_FLAGS);
|
clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
|
||||||
if (must_appraise)
|
|
||||||
iint->flags |= IMA_APPRAISE;
|
|
||||||
}
|
}
|
||||||
if (!must_appraise)
|
|
||||||
__vfs_removexattr(dentry, XATTR_NAME_IMA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -388,12 +394,12 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
|
||||||
iint = integrity_iint_find(inode);
|
iint = integrity_iint_find(inode);
|
||||||
if (!iint)
|
if (!iint)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
iint->flags &= ~IMA_DONE_MASK;
|
|
||||||
iint->measured_pcrs = 0;
|
iint->measured_pcrs = 0;
|
||||||
|
set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
|
||||||
if (digsig)
|
if (digsig)
|
||||||
iint->flags |= IMA_DIGSIG;
|
set_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
return;
|
else
|
||||||
|
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||||
|
|
|
@ -85,10 +85,10 @@ static void ima_rdwr_violation_check(struct file *file,
|
||||||
struct integrity_iint_cache *iint,
|
struct integrity_iint_cache *iint,
|
||||||
int must_measure,
|
int must_measure,
|
||||||
char **pathbuf,
|
char **pathbuf,
|
||||||
const char **pathname)
|
const char **pathname,
|
||||||
|
char *filename)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
char filename[NAME_MAX];
|
|
||||||
fmode_t mode = file->f_mode;
|
fmode_t mode = file->f_mode;
|
||||||
bool send_tomtou = false, send_writers = false;
|
bool send_tomtou = false, send_writers = false;
|
||||||
|
|
||||||
|
@ -97,10 +97,13 @@ static void ima_rdwr_violation_check(struct file *file,
|
||||||
if (!iint)
|
if (!iint)
|
||||||
iint = integrity_iint_find(inode);
|
iint = integrity_iint_find(inode);
|
||||||
/* IMA_MEASURE is set from reader side */
|
/* IMA_MEASURE is set from reader side */
|
||||||
if (iint && (iint->flags & IMA_MEASURE))
|
if (iint && test_bit(IMA_MUST_MEASURE,
|
||||||
|
&iint->atomic_flags))
|
||||||
send_tomtou = true;
|
send_tomtou = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (must_measure)
|
||||||
|
set_bit(IMA_MUST_MEASURE, &iint->atomic_flags);
|
||||||
if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
|
if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
|
||||||
send_writers = true;
|
send_writers = true;
|
||||||
}
|
}
|
||||||
|
@ -122,22 +125,25 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
|
||||||
struct inode *inode, struct file *file)
|
struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
fmode_t mode = file->f_mode;
|
fmode_t mode = file->f_mode;
|
||||||
|
bool update;
|
||||||
|
|
||||||
if (!(mode & FMODE_WRITE))
|
if (!(mode & FMODE_WRITE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inode_lock(inode);
|
mutex_lock(&iint->mutex);
|
||||||
if (atomic_read(&inode->i_writecount) == 1) {
|
if (atomic_read(&inode->i_writecount) == 1) {
|
||||||
|
update = test_and_clear_bit(IMA_UPDATE_XATTR,
|
||||||
|
&iint->atomic_flags);
|
||||||
if (!IS_I_VERSION(inode) ||
|
if (!IS_I_VERSION(inode) ||
|
||||||
inode_cmp_iversion(inode, iint->version) ||
|
inode_cmp_iversion(inode, iint->version) ||
|
||||||
(iint->flags & IMA_NEW_FILE)) {
|
(iint->flags & IMA_NEW_FILE)) {
|
||||||
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 (iint->flags & IMA_APPRAISE)
|
if (update)
|
||||||
ima_update_xattr(iint, file);
|
ima_update_xattr(iint, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inode_unlock(inode);
|
mutex_unlock(&iint->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,7 +176,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
|
||||||
char *pathbuf = NULL;
|
char *pathbuf = NULL;
|
||||||
char filename[NAME_MAX];
|
char filename[NAME_MAX];
|
||||||
const char *pathname = NULL;
|
const char *pathname = NULL;
|
||||||
int rc = -ENOMEM, action, must_appraise;
|
int rc = 0, action, must_appraise = 0;
|
||||||
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
|
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
|
||||||
struct evm_ima_xattr_data *xattr_value = NULL;
|
struct evm_ima_xattr_data *xattr_value = NULL;
|
||||||
int xattr_len = 0;
|
int xattr_len = 0;
|
||||||
|
@ -201,17 +207,31 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
|
||||||
if (action) {
|
if (action) {
|
||||||
iint = integrity_inode_get(inode);
|
iint = integrity_inode_get(inode);
|
||||||
if (!iint)
|
if (!iint)
|
||||||
goto out;
|
rc = -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (violation_check) {
|
if (!rc && violation_check)
|
||||||
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
|
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
|
||||||
&pathbuf, &pathname);
|
&pathbuf, &pathname, filename);
|
||||||
if (!action) {
|
|
||||||
rc = 0;
|
inode_unlock(inode);
|
||||||
goto out_free;
|
|
||||||
}
|
if (rc)
|
||||||
}
|
goto out;
|
||||||
|
if (!action)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
mutex_lock(&iint->mutex);
|
||||||
|
|
||||||
|
if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
|
||||||
|
/* reset appraisal flags if ima_inode_post_setattr was called */
|
||||||
|
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
|
||||||
|
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
|
||||||
|
IMA_ACTION_FLAGS);
|
||||||
|
|
||||||
|
if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags))
|
||||||
|
/* reset all flags if ima_inode_setxattr was called */
|
||||||
|
iint->flags &= ~IMA_DONE_MASK;
|
||||||
|
|
||||||
/* Determine if already appraised/measured based on bitmask
|
/* Determine if already appraised/measured based on bitmask
|
||||||
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
|
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
|
||||||
|
@ -225,11 +245,23 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
|
||||||
if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
|
if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
|
||||||
action ^= IMA_MEASURE;
|
action ^= IMA_MEASURE;
|
||||||
|
|
||||||
|
/* HASH sets the digital signature and update flags, nothing else */
|
||||||
|
if ((action & IMA_HASH) &&
|
||||||
|
!(test_bit(IMA_DIGSIG, &iint->atomic_flags))) {
|
||||||
|
xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
|
||||||
|
if ((xattr_value && xattr_len > 2) &&
|
||||||
|
(xattr_value->type == EVM_IMA_XATTR_DIGSIG))
|
||||||
|
set_bit(IMA_DIGSIG, &iint->atomic_flags);
|
||||||
|
iint->flags |= IMA_HASHED;
|
||||||
|
action ^= IMA_HASH;
|
||||||
|
set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
|
||||||
|
}
|
||||||
|
|
||||||
/* Nothing to do, just return existing appraised status */
|
/* Nothing to do, just return existing appraised status */
|
||||||
if (!action) {
|
if (!action) {
|
||||||
if (must_appraise)
|
if (must_appraise)
|
||||||
rc = ima_get_cache_status(iint, func);
|
rc = ima_get_cache_status(iint, func);
|
||||||
goto out_digsig;
|
goto out_locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
template_desc = ima_template_desc_current();
|
template_desc = ima_template_desc_current();
|
||||||
|
@ -242,7 +274,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
|
||||||
|
|
||||||
rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
|
rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
|
||||||
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
|
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
|
||||||
goto out_digsig;
|
goto out_locked;
|
||||||
|
|
||||||
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
|
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
|
||||||
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
|
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
|
||||||
|
@ -250,26 +282,32 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
|
||||||
if (action & IMA_MEASURE)
|
if (action & IMA_MEASURE)
|
||||||
ima_store_measurement(iint, file, pathname,
|
ima_store_measurement(iint, file, pathname,
|
||||||
xattr_value, xattr_len, pcr);
|
xattr_value, xattr_len, pcr);
|
||||||
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK))
|
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
|
||||||
|
inode_lock(inode);
|
||||||
rc = ima_appraise_measurement(func, iint, file, pathname,
|
rc = ima_appraise_measurement(func, iint, file, pathname,
|
||||||
xattr_value, xattr_len, opened);
|
xattr_value, xattr_len, opened);
|
||||||
|
inode_unlock(inode);
|
||||||
|
}
|
||||||
if (action & IMA_AUDIT)
|
if (action & IMA_AUDIT)
|
||||||
ima_audit_measurement(iint, pathname);
|
ima_audit_measurement(iint, pathname);
|
||||||
|
|
||||||
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
|
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
|
||||||
rc = 0;
|
rc = 0;
|
||||||
out_digsig:
|
out_locked:
|
||||||
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
|
if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
|
||||||
!(iint->flags & IMA_NEW_FILE))
|
!(iint->flags & IMA_NEW_FILE))
|
||||||
rc = -EACCES;
|
rc = -EACCES;
|
||||||
|
mutex_unlock(&iint->mutex);
|
||||||
kfree(xattr_value);
|
kfree(xattr_value);
|
||||||
out_free:
|
out:
|
||||||
if (pathbuf)
|
if (pathbuf)
|
||||||
__putname(pathbuf);
|
__putname(pathbuf);
|
||||||
out:
|
if (must_appraise) {
|
||||||
inode_unlock(inode);
|
if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE))
|
||||||
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
|
return -EACCES;
|
||||||
return -EACCES;
|
if (file->f_mode & FMODE_WRITE)
|
||||||
|
set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,8 +406,10 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id)
|
||||||
|
|
||||||
if (!file && read_id == READING_MODULE) {
|
if (!file && read_id == READING_MODULE) {
|
||||||
if (!sig_enforce && (ima_appraise & IMA_APPRAISE_MODULES) &&
|
if (!sig_enforce && (ima_appraise & IMA_APPRAISE_MODULES) &&
|
||||||
(ima_appraise & IMA_APPRAISE_ENFORCE))
|
(ima_appraise & IMA_APPRAISE_ENFORCE)) {
|
||||||
|
pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n");
|
||||||
return -EACCES; /* INTEGRITY_UNKNOWN */
|
return -EACCES; /* INTEGRITY_UNKNOWN */
|
||||||
|
}
|
||||||
return 0; /* We rely on module signature checking */
|
return 0; /* We rely on module signature checking */
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
|
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
|
||||||
#define DONT_APPRAISE 0x0008
|
#define DONT_APPRAISE 0x0008
|
||||||
#define AUDIT 0x0040
|
#define AUDIT 0x0040
|
||||||
|
#define HASH 0x0100
|
||||||
|
#define DONT_HASH 0x0200
|
||||||
|
|
||||||
#define INVALID_PCR(a) (((a) < 0) || \
|
#define INVALID_PCR(a) (((a) < 0) || \
|
||||||
(a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8))
|
(a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8))
|
||||||
|
@ -380,8 +382,10 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||||
action |= entry->flags & IMA_ACTION_FLAGS;
|
action |= entry->flags & IMA_ACTION_FLAGS;
|
||||||
|
|
||||||
action |= entry->action & IMA_DO_MASK;
|
action |= entry->action & IMA_DO_MASK;
|
||||||
if (entry->action & IMA_APPRAISE)
|
if (entry->action & IMA_APPRAISE) {
|
||||||
action |= get_subaction(entry, func);
|
action |= get_subaction(entry, func);
|
||||||
|
action ^= IMA_HASH;
|
||||||
|
}
|
||||||
|
|
||||||
if (entry->action & IMA_DO_MASK)
|
if (entry->action & IMA_DO_MASK)
|
||||||
actmask &= ~(entry->action | entry->action << 1);
|
actmask &= ~(entry->action | entry->action << 1);
|
||||||
|
@ -521,7 +525,7 @@ enum {
|
||||||
Opt_err = -1,
|
Opt_err = -1,
|
||||||
Opt_measure = 1, Opt_dont_measure,
|
Opt_measure = 1, Opt_dont_measure,
|
||||||
Opt_appraise, Opt_dont_appraise,
|
Opt_appraise, Opt_dont_appraise,
|
||||||
Opt_audit,
|
Opt_audit, Opt_hash, Opt_dont_hash,
|
||||||
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
||||||
Opt_subj_user, Opt_subj_role, Opt_subj_type,
|
Opt_subj_user, Opt_subj_role, Opt_subj_type,
|
||||||
Opt_func, Opt_mask, Opt_fsmagic,
|
Opt_func, Opt_mask, Opt_fsmagic,
|
||||||
|
@ -538,6 +542,8 @@ static match_table_t policy_tokens = {
|
||||||
{Opt_appraise, "appraise"},
|
{Opt_appraise, "appraise"},
|
||||||
{Opt_dont_appraise, "dont_appraise"},
|
{Opt_dont_appraise, "dont_appraise"},
|
||||||
{Opt_audit, "audit"},
|
{Opt_audit, "audit"},
|
||||||
|
{Opt_hash, "hash"},
|
||||||
|
{Opt_dont_hash, "dont_hash"},
|
||||||
{Opt_obj_user, "obj_user=%s"},
|
{Opt_obj_user, "obj_user=%s"},
|
||||||
{Opt_obj_role, "obj_role=%s"},
|
{Opt_obj_role, "obj_role=%s"},
|
||||||
{Opt_obj_type, "obj_type=%s"},
|
{Opt_obj_type, "obj_type=%s"},
|
||||||
|
@ -671,6 +677,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
|
|
||||||
entry->action = AUDIT;
|
entry->action = AUDIT;
|
||||||
break;
|
break;
|
||||||
|
case Opt_hash:
|
||||||
|
ima_log_string(ab, "action", "hash");
|
||||||
|
|
||||||
|
if (entry->action != UNKNOWN)
|
||||||
|
result = -EINVAL;
|
||||||
|
|
||||||
|
entry->action = HASH;
|
||||||
|
break;
|
||||||
|
case Opt_dont_hash:
|
||||||
|
ima_log_string(ab, "action", "dont_hash");
|
||||||
|
|
||||||
|
if (entry->action != UNKNOWN)
|
||||||
|
result = -EINVAL;
|
||||||
|
|
||||||
|
entry->action = DONT_HASH;
|
||||||
|
break;
|
||||||
case Opt_func:
|
case Opt_func:
|
||||||
ima_log_string(ab, "func", args[0].from);
|
ima_log_string(ab, "func", args[0].from);
|
||||||
|
|
||||||
|
@ -743,7 +765,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
case Opt_fsuuid:
|
case Opt_fsuuid:
|
||||||
ima_log_string(ab, "fsuuid", args[0].from);
|
ima_log_string(ab, "fsuuid", args[0].from);
|
||||||
|
|
||||||
if (uuid_is_null(&entry->fsuuid)) {
|
if (!uuid_is_null(&entry->fsuuid)) {
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1040,6 +1062,10 @@ int ima_policy_show(struct seq_file *m, void *v)
|
||||||
seq_puts(m, pt(Opt_dont_appraise));
|
seq_puts(m, pt(Opt_dont_appraise));
|
||||||
if (entry->action & AUDIT)
|
if (entry->action & AUDIT)
|
||||||
seq_puts(m, pt(Opt_audit));
|
seq_puts(m, pt(Opt_audit));
|
||||||
|
if (entry->action & HASH)
|
||||||
|
seq_puts(m, pt(Opt_hash));
|
||||||
|
if (entry->action & DONT_HASH)
|
||||||
|
seq_puts(m, pt(Opt_dont_hash));
|
||||||
|
|
||||||
seq_puts(m, " ");
|
seq_puts(m, " ");
|
||||||
|
|
||||||
|
|
|
@ -377,8 +377,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) {
|
if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) {
|
||||||
pr_err("attempting to restore a template name \
|
pr_err("attempting to restore a template name that is too long\n");
|
||||||
that is too long\n");
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -389,8 +388,8 @@ int ima_restore_measurement_list(loff_t size, void *buf)
|
||||||
template_name[hdr[HDR_TEMPLATE_NAME].len] = 0;
|
template_name[hdr[HDR_TEMPLATE_NAME].len] = 0;
|
||||||
|
|
||||||
if (strcmp(template_name, "ima") == 0) {
|
if (strcmp(template_name, "ima") == 0) {
|
||||||
pr_err("attempting to restore an unsupported \
|
pr_err("attempting to restore an unsupported template \"%s\" failed\n",
|
||||||
template \"%s\" failed\n", template_name);
|
template_name);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -410,8 +409,8 @@ int ima_restore_measurement_list(loff_t size, void *buf)
|
||||||
&(template_desc->fields),
|
&(template_desc->fields),
|
||||||
&(template_desc->num_fields));
|
&(template_desc->num_fields));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("attempting to restore the template fmt \"%s\" \
|
pr_err("attempting to restore the template fmt \"%s\" failed\n",
|
||||||
failed\n", template_desc->fmt);
|
template_desc->fmt);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,39 +25,50 @@
|
||||||
#define IMA_COLLECTED 0x00000020
|
#define IMA_COLLECTED 0x00000020
|
||||||
#define IMA_AUDIT 0x00000040
|
#define IMA_AUDIT 0x00000040
|
||||||
#define IMA_AUDITED 0x00000080
|
#define IMA_AUDITED 0x00000080
|
||||||
|
#define IMA_HASH 0x00000100
|
||||||
|
#define IMA_HASHED 0x00000200
|
||||||
|
|
||||||
/* iint cache flags */
|
/* iint cache flags */
|
||||||
#define IMA_ACTION_FLAGS 0xff000000
|
#define IMA_ACTION_FLAGS 0xff000000
|
||||||
#define IMA_ACTION_RULE_FLAGS 0x06000000
|
#define IMA_ACTION_RULE_FLAGS 0x06000000
|
||||||
#define IMA_DIGSIG 0x01000000
|
#define IMA_DIGSIG_REQUIRED 0x01000000
|
||||||
#define IMA_DIGSIG_REQUIRED 0x02000000
|
#define IMA_PERMIT_DIRECTIO 0x02000000
|
||||||
#define IMA_PERMIT_DIRECTIO 0x04000000
|
#define IMA_NEW_FILE 0x04000000
|
||||||
#define IMA_NEW_FILE 0x08000000
|
#define EVM_IMMUTABLE_DIGSIG 0x08000000
|
||||||
|
|
||||||
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
|
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
|
||||||
IMA_APPRAISE_SUBMASK)
|
IMA_HASH | IMA_APPRAISE_SUBMASK)
|
||||||
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
|
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
|
||||||
IMA_COLLECTED | IMA_APPRAISED_SUBMASK)
|
IMA_HASHED | IMA_COLLECTED | \
|
||||||
|
IMA_APPRAISED_SUBMASK)
|
||||||
|
|
||||||
/* iint subaction appraise cache flags */
|
/* iint subaction appraise cache flags */
|
||||||
#define IMA_FILE_APPRAISE 0x00000100
|
#define IMA_FILE_APPRAISE 0x00001000
|
||||||
#define IMA_FILE_APPRAISED 0x00000200
|
#define IMA_FILE_APPRAISED 0x00002000
|
||||||
#define IMA_MMAP_APPRAISE 0x00000400
|
#define IMA_MMAP_APPRAISE 0x00004000
|
||||||
#define IMA_MMAP_APPRAISED 0x00000800
|
#define IMA_MMAP_APPRAISED 0x00008000
|
||||||
#define IMA_BPRM_APPRAISE 0x00001000
|
#define IMA_BPRM_APPRAISE 0x00010000
|
||||||
#define IMA_BPRM_APPRAISED 0x00002000
|
#define IMA_BPRM_APPRAISED 0x00020000
|
||||||
#define IMA_READ_APPRAISE 0x00004000
|
#define IMA_READ_APPRAISE 0x00040000
|
||||||
#define IMA_READ_APPRAISED 0x00008000
|
#define IMA_READ_APPRAISED 0x00080000
|
||||||
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
|
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
|
||||||
IMA_BPRM_APPRAISE | IMA_READ_APPRAISE)
|
IMA_BPRM_APPRAISE | IMA_READ_APPRAISE)
|
||||||
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
|
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
|
||||||
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED)
|
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED)
|
||||||
|
|
||||||
|
/* iint cache atomic_flags */
|
||||||
|
#define IMA_CHANGE_XATTR 0
|
||||||
|
#define IMA_UPDATE_XATTR 1
|
||||||
|
#define IMA_CHANGE_ATTR 2
|
||||||
|
#define IMA_DIGSIG 3
|
||||||
|
#define IMA_MUST_MEASURE 4
|
||||||
|
|
||||||
enum evm_ima_xattr_type {
|
enum evm_ima_xattr_type {
|
||||||
IMA_XATTR_DIGEST = 0x01,
|
IMA_XATTR_DIGEST = 0x01,
|
||||||
EVM_XATTR_HMAC,
|
EVM_XATTR_HMAC,
|
||||||
EVM_IMA_XATTR_DIGSIG,
|
EVM_IMA_XATTR_DIGSIG,
|
||||||
IMA_XATTR_DIGEST_NG,
|
IMA_XATTR_DIGEST_NG,
|
||||||
|
EVM_XATTR_PORTABLE_DIGSIG,
|
||||||
IMA_XATTR_LAST
|
IMA_XATTR_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,10 +111,12 @@ struct signature_v2_hdr {
|
||||||
/* integrity data associated with an inode */
|
/* integrity data associated with an inode */
|
||||||
struct integrity_iint_cache {
|
struct integrity_iint_cache {
|
||||||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||||
|
struct mutex mutex; /* protects: version, flags, digest */
|
||||||
struct inode *inode; /* back pointer to inode in question */
|
struct inode *inode; /* back pointer to inode in question */
|
||||||
u64 version; /* track inode changes */
|
u64 version; /* track inode changes */
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long measured_pcrs;
|
unsigned long measured_pcrs;
|
||||||
|
unsigned long atomic_flags;
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue