|
From: Dmitry K. <d.k...@sa...> - 2014-10-30 13:57:02
|
Extended attributes provide means to assign capabilities to the processes
and security labels. System might want to have them immutable and protected
by signature. This patch provides immutable EVM signatures.
Immutable EVM signature cannot be replaced with HMAC, thus modification
of file content, and file attributes with 'chown' and 'chmod' is forbidden.
Signed-off-by: Dmitry Kasatkin <d.k...@sa...>
---
include/linux/integrity.h | 1 +
security/integrity/digsig.c | 1 +
security/integrity/evm/evm_crypto.c | 18 +++++++++++++++++-
security/integrity/evm/evm_main.c | 37 +++++++++++++++++++++++++++++--------
security/integrity/ima/ima_main.c | 3 ++-
security/integrity/integrity.h | 7 +++++++
6 files changed, 57 insertions(+), 10 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index c2d6082..ae5911b 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -18,6 +18,7 @@ enum integrity_status {
INTEGRITY_NOLABEL,
INTEGRITY_NOXATTRS,
INTEGRITY_UNKNOWN,
+ INTEGRITY_PASS_DIGSIG,
};
/* List of EVM protected security xattrs */
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index f0c99bb..d1f1194 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -57,6 +57,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
return digsig_verify(keyring[id], sig + 1, siglen - 1,
digest, digestlen);
case 2:
+ case 3:
return asymmetric_verify(keyring[id], sig, siglen,
digest, digestlen);
}
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index e87ed57..2851d7b 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -118,6 +118,19 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
crypto_shash_final(desc, digest);
}
+static void hmac_add_misc_digsig(struct shash_desc *desc, struct inode *inode,
+ char *digest)
+{
+ struct h_misc_digsig hmac_misc;
+
+ memset(&hmac_misc, 0, sizeof(hmac_misc));
+ hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
+ hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
+ hmac_misc.mode = inode->i_mode;
+ crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
+ crypto_shash_final(desc, digest);
+}
+
/*
* Calculate the HMAC value across the set of protected security xattrs.
*
@@ -167,7 +180,10 @@ int evm_calc_hmac_or_hash(struct dentry *dentry,
xattr_size = size;
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
}
- hmac_add_misc(desc, inode, digest);
+ if (type == EVM_IMA_XATTR_DIGSIG)
+ hmac_add_misc_digsig(desc, inode, digest);
+ else
+ hmac_add_misc(desc, inode, digest);
out:
kfree(xattr_value);
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0104bdf..8d53ec4 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -28,7 +28,7 @@
int evm_initialized;
static char *integrity_status_msg[] = {
- "pass", "fail", "no_label", "no_xattrs", "unknown"
+ "pass", "fail", "no_label", "no_xattrs", "unknown", "pass_digsig"
};
char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1";
@@ -115,7 +115,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
struct evm_ima_xattr_data *xattr_data = NULL;
struct evm_ima_xattr_data calc;
enum integrity_status evm_status = INTEGRITY_PASS;
- int rc, xattr_len;
+ int rc, xattr_len, version;
if (iint && iint->evm_status == INTEGRITY_PASS)
return iint->evm_status;
@@ -141,6 +141,9 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
xattr_len = rc;
+ if (iint)
+ clear_bit(EVM_DIGSIG, &iint->atomic_flags);
+
/* check value type */
switch (xattr_data->type) {
case EVM_XATTR_HMAC:
@@ -155,8 +158,11 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
rc = -EINVAL;
break;
case EVM_IMA_XATTR_DIGSIG:
+ version = ((const char *)xattr_data)[1];
rc = evm_calc_hmac_or_hash(dentry, xattr_name, xattr_value,
- xattr_value_len, IMA_XATTR_DIGEST,
+ xattr_value_len,
+ version == 3 ? EVM_IMA_XATTR_DIGSIG :
+ IMA_XATTR_DIGEST,
calc.digest);
if (rc)
break;
@@ -164,14 +170,27 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
(const char *)xattr_data, xattr_len,
calc.digest, sizeof(calc.digest));
if (!rc) {
+ if (iint)
+ set_bit(EVM_DIGSIG, &iint->atomic_flags);
+ if (version == 3) {
+ /* immutable/unreplaceable signature */
+ if (!iint)
+ /* for evm_verify_current_integrity */
+ evm_status = INTEGRITY_PASS_DIGSIG;
+ goto out;
+ }
/* Replace RSA with HMAC if not mounted readonly and
* not immutable
*/
if (!IS_RDONLY(dentry->d_inode) &&
- !IS_IMMUTABLE(dentry->d_inode))
+ !IS_IMMUTABLE(dentry->d_inode)) {
evm_update_evmxattr(dentry, xattr_name,
xattr_value,
xattr_value_len);
+ if (iint)
+ clear_bit(EVM_DIGSIG,
+ &iint->atomic_flags);
+ }
}
break;
default:
@@ -300,7 +319,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
return 0;
}
out:
- if (evm_status != INTEGRITY_PASS)
+ if (evm_status != INTEGRITY_PASS && evm_status != INTEGRITY_PASS_DIGSIG)
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode,
dentry->d_name.name, "appraise_metadata",
integrity_status_msg[evm_status],
@@ -406,9 +425,11 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
if ((evm_status == INTEGRITY_PASS) ||
(evm_status == INTEGRITY_NOXATTRS))
return 0;
- integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode,
- dentry->d_name.name, "appraise_metadata",
- integrity_status_msg[evm_status], -EPERM, 0);
+ if (evm_status != INTEGRITY_PASS_DIGSIG)
+ integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode,
+ dentry->d_name.name, "appraise_metadata",
+ integrity_status_msg[evm_status], -EPERM,
+ 0);
return -EPERM;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 58a4773..b110c10 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -268,7 +268,8 @@ static int process_measurement(struct file *file, int mask, int function,
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
out_locked:
- if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags))
+ if ((mask & MAY_WRITE) && (test_bit(IMA_DIGSIG, &iint->atomic_flags) ||
+ test_bit(EVM_DIGSIG, &iint->atomic_flags)))
rc = -EACCES;
mutex_unlock(&iint->mutex);
kfree(xattr_value);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index acf2648..36f853f 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -62,6 +62,7 @@
#define IMA_UPDATE_XATTR 1
#define IMA_CHANGE_ATTR 2
#define IMA_DIGSIG 3
+#define EVM_DIGSIG 4
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
@@ -107,6 +108,12 @@ struct signature_v2_hdr {
uint8_t sig[0]; /* signature payload */
} __packed;
+struct h_misc_digsig {
+ uid_t uid;
+ gid_t gid;
+ umode_t mode;
+};
+
/* integrity data associated with an inode */
struct integrity_iint_cache {
struct rb_node rb_node; /* rooted in integrity_iint_tree */
--
1.9.1
|