evm: add interface to read and write EVM state (ENABLE/DISABLE/FIX). The state of EVM can be change by writing to the <securityfs>/evm_state, and can be check by reading from the interface. To change the state of EVM write: 0 - for disabling 1 - for enabling 2 - for FIX mode Notice that evm have to be initialized before you use the interface. To enable this option you have to set a proper option in Kconfig. Signed-off-by: Janusz Kozerski <j.kozerski@samsung.com> Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index 593a680..dfb4d4f 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig
@@ -82,3 +82,13 @@ default "/etc/ima/evm-kmk" help This option defines EVM master key path. + +config EVM_STATE_INTERFACE + boolean "EVM state interface" + depends on EVM + default n + help + Creates the EVM state interface and makes possible change EVM + state in runtime. + + WARNING: Enable this option only if you really need it.
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index ac6d383..4dc4c3d 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h
@@ -24,7 +24,13 @@ #define EVM_INIT_HMAC 0x0001 #define EVM_INIT_X509 0x0002 +#define EVM_STATE_DISABLED 0x00 +#define EVM_STATE_ENABLED 0x01 +#define EVM_STATE_FIX 0x02 + +extern int evm_enabled; extern int evm_initialized; +extern int evm_fixmode; extern char *evm_hmac; extern char *evm_hash;
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 5da2ec3..04275b6 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c
@@ -59,6 +59,7 @@ goto inval; memcpy(evmkey, key, keylen); evm_initialized |= EVM_INIT_HMAC; + evm_enabled = evm_fixmode ? EVM_STATE_FIX : EVM_STATE_ENABLED; pr_info("key initialized\n"); return 0; inval:
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index c5d45bd..5130324 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c
@@ -25,7 +25,10 @@ #include <crypto/hash.h> #include "evm.h" + +int evm_enabled; int evm_initialized; +int evm_fixmode; static char *integrity_status_msg[] = { "pass", "fail", "no_label", "no_xattrs", "unknown", "pass_digsig" @@ -53,7 +56,6 @@ NULL }; -static int evm_fixmode; static int __init evm_set_fixmode(char *str) { if (strncmp(str, "fix", 3) == 0) @@ -266,7 +268,7 @@ void *xattr_value, size_t xattr_value_len, struct integrity_iint_cache *iint) { - if (!evm_initialized || !evm_protected_xattr(xattr_name)) + if (!evm_enabled || !evm_protected_xattr(xattr_name)) return INTEGRITY_UNKNOWN; if (!iint) { @@ -290,7 +292,7 @@ { struct inode *inode = d_backing_inode(dentry); - if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode) + if (!evm_enabled || !S_ISREG(inode->i_mode) || evm_enabled == EVM_STATE_FIX) return 0; return evm_verify_hmac(dentry, NULL, NULL, 0, NULL); } @@ -418,7 +420,7 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { - if (!evm_initialized || (!evm_protected_xattr(xattr_name) + if (!evm_enabled || (!evm_protected_xattr(xattr_name) && !posix_xattr_acl(xattr_name))) return; @@ -439,7 +441,7 @@ */ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) { - if (!evm_initialized || !evm_protected_xattr(xattr_name)) + if (!evm_enabled || !evm_protected_xattr(xattr_name)) return; evm_reset_status(dentry->d_inode); @@ -482,7 +484,7 @@ */ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) { - if (!evm_initialized) + if (!evm_enabled) return; if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) @@ -499,7 +501,7 @@ struct evm_ima_xattr_data *xattr_data; int rc; - if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name)) + if (!evm_enabled || !evm_protected_xattr(lsm_xattr->name)) return 0; xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS); @@ -529,8 +531,10 @@ #ifdef CONFIG_EVM_LOAD_X509 rc = integrity_load_x509(INTEGRITY_KEYRING_EVM, CONFIG_EVM_X509_PATH); - if (!rc) + if (!rc) { evm_initialized |= EVM_INIT_X509; + evm_enabled = evm_fixmode ? EVM_STATE_FIX : EVM_STATE_ENABLED; + } #endif #ifdef CONFIG_EVM_LOAD_KEY if (evm_load)
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index c8dccd5..1b77d11 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c
@@ -29,6 +29,18 @@ * @count: maximum to send along * @ppos: where to start * + * If EVM_STATE_INTERFACE is enabled this should write to the userspace: + * 0 - when EVM is disabled or uninitialized + * 1 - when EVM is in enforce mode (initialized and enabled) + * 2 - when EVM is in FIX mode (initialized and fix) + * To use this interface user needs to have CAP_SYS_ADMIN + * Notice that the param "evm=fix" has higher priority, and if it was passed + * to the kernel then EVM will be running in FIX mode (or will be disabled). + * + * If EVM_STATE_INTERFACE is disabled it writes: + * 0 - when EVM is uninitialized + * 1 - when EVM is initialized + * * Returns number of bytes read or error code, as appropriate */ static ssize_t evm_read_key(struct file *filp, char __user *buf, @@ -36,11 +48,16 @@ { char temp[80]; ssize_t rc; + int mode = evm_initialized; if (*ppos != 0) return 0; - sprintf(temp, "%d", evm_initialized); +#ifdef CONFIG_EVM_STATE_INTERFACE + mode = evm_enabled; +#endif + + sprintf(temp, "%d", mode); rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); return rc; @@ -62,9 +79,9 @@ size_t count, loff_t *ppos) { char temp[80]; - int i; + long mode; - if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC)) + if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (count >= sizeof(temp) || count == 0) @@ -75,11 +92,28 @@ temp[count] = '\0'; - if ((sscanf(temp, "%d", &i) != 1) || (i != 1)) + if (kstrtol(temp, 0, &mode)) return -EINVAL; +#ifndef CONFIG_EVM_STATE_INTERFACE + if (mode != EVM_STATE_ENABLED) + return -EINVAL; +#else + if (mode != EVM_STATE_DISABLED && + mode != EVM_STATE_ENABLED && + mode != EVM_STATE_FIX) + return -EINVAL; +#endif + evm_init_key(); +#ifdef CONFIG_EVM_STATE_INTERFACE + if (mode && evm_initialized) + evm_enabled = evm_fixmode ? EVM_STATE_FIX : mode; + else + evm_enabled = EVM_STATE_DISABLED; +#endif + return count; } @@ -96,5 +130,6 @@ NULL, NULL, &evm_key_ops); if (!evm_init_tpm || IS_ERR(evm_init_tpm)) error = -EFAULT; + return error; }