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;
 }