diff mbox

[V2] EVM: Allow userspace to signal an RSA key has been loaded

Message ID 20171011191014.4426-1-mjg59@google.com (mailing list archive)
State New, archived
Headers show

Commit Message

Matthew Garrett Oct. 11, 2017, 7:10 p.m. UTC
EVM will only perform validation once a key has been loaded. This key
may either be a symmetric trusted key (for HMAC validation and creation)
or the public half of an asymmetric key (for digital signature
validation). The /sys/kernel/security/evm interface allows userland to
signal that a symmetric key has been loaded, but does not allow userland
to signal that an asymmetric public key has been loaded.

This patch extends the interface to permit userspace to pass a bitmask
of loaded key types. It also allows userspace to block loading of an
asymmetric key in order to avoid a compromised system from being able to
load an additional key type later.

Signed-off-by: Matthew Garrett <mjg59@google.com>
---
 Documentation/ABI/testing/evm      | 48 +++++++++++++++++++++++++++-----------
 security/integrity/evm/evm.h       |  3 +++
 security/integrity/evm/evm_secfs.c | 29 +++++++++++++----------
 3 files changed, 54 insertions(+), 26 deletions(-)

Comments

Mimi Zohar Oct. 15, 2017, 2:27 p.m. UTC | #1
On Wed, 2017-10-11 at 12:10 -0700, Matthew Garrett wrote:
> EVM will only perform validation once a key has been loaded. This key
> may either be a symmetric trusted key (for HMAC validation and creation)
> or the public half of an asymmetric key (for digital signature
> validation). The /sys/kernel/security/evm interface allows userland to
> signal that a symmetric key has been loaded, but does not allow userland
> to signal that an asymmetric public key has been loaded.
> 
> This patch extends the interface to permit userspace to pass a bitmask
> of loaded key types. It also allows userspace to block loading of an
> asymmetric key in order to avoid a compromised system from being able to
> load an additional key type later.

I assume you mean "block loading of a symmetric key".  Other than this
and a trailing blank line, the patch looks good.  If you don't have
objections, I'll fix these two things.

Mimi  

> 
> Signed-off-by: Matthew Garrett <mjg59@google.com>
> ---
>  Documentation/ABI/testing/evm      | 48 +++++++++++++++++++++++++++-----------
>  security/integrity/evm/evm.h       |  3 +++
>  security/integrity/evm/evm_secfs.c | 29 +++++++++++++----------
>  3 files changed, 54 insertions(+), 26 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm
> index 8374d4557e5d..d2782afb0d96 100644
> --- a/Documentation/ABI/testing/evm
> +++ b/Documentation/ABI/testing/evm
> @@ -7,17 +7,37 @@ Description:
>  		HMAC-sha1 value across the extended attributes, storing the
>  		value as the extended attribute 'security.evm'.
> 
> -		EVM depends on the Kernel Key Retention System to provide it
> -		with a trusted/encrypted key for the HMAC-sha1 operation.
> -		The key is loaded onto the root's keyring using keyctl.  Until
> -		EVM receives notification that the key has been successfully
> -		loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
> -		can not create or validate the 'security.evm' xattr, but
> -		returns INTEGRITY_UNKNOWN.  Loading the key 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/keys-trusted-encrypted.txt.  (A sample dracut
> -		patch, which loads the trusted/encrypted key and enables
> -		EVM, is available from http://linux-ima.sourceforge.net/#EVM.)
> +		EVM supports two classes of security.evm. The first is
> +		an HMAC-sha1 generated locally with a
> +		trusted/encrypted key stored in the Kernel Key
> +		Retention System. The second is a digital signature
> +		generated either locally or remotely using an
> +		asymmetric key. These keys are loaded onto root's
> +		keyring using keyctl, and EVM is then enabled by
> +		echoing a value to <securityfs>/evm:
> +
> +		1: enable HMAC validation and creation
> +		2: enable digital signature validation
> +		3: enable HMAC and digital signature validation and HMAC
> +		   creation
> +
> +		Further writes will be blocked if HMAC support is enabled or
> +		if bit 32 is set:
> +
> +		echo 0x80000002 ><securityfs>/evm
> +
> +		will enable digital signature validation and block
> +		further writes to <securityfs>/evm.
> +
> +		Until this is done, 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/keys-trusted-encrypted.txt. Both dracut
> +		(via 97masterkey and 98integrity) and systemd (via
> +		core/ima-setup) have support for loading keys at boot
> +		time.
> +
> diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
> index 2ff02459fcfd..946efffcc389 100644
> --- a/security/integrity/evm/evm.h
> +++ b/security/integrity/evm/evm.h
> @@ -23,6 +23,9 @@
> 
>  #define EVM_INIT_HMAC	0x0001
>  #define EVM_INIT_X509	0x0002
> +#define EVM_SETUP       0x80000000 /* userland has signaled key load */
> +
> +#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP)
> 
>  extern int evm_initialized;
>  extern char *evm_hmac;
> diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
> index c8dccd54d501..319cf16d6603 100644
> --- a/security/integrity/evm/evm_secfs.c
> +++ b/security/integrity/evm/evm_secfs.c
> @@ -40,7 +40,7 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
>  	if (*ppos != 0)
>  		return 0;
> 
> -	sprintf(temp, "%d", evm_initialized);
> +	sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP));
>  	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
> 
>  	return rc;
> @@ -61,24 +61,29 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
>  static ssize_t evm_write_key(struct file *file, const char __user *buf,
>  			     size_t count, loff_t *ppos)
>  {
> -	char temp[80];
> -	int i;
> +	int i, ret;
> 
> -	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC))
> +	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP))
>  		return -EPERM;
> 
> -	if (count >= sizeof(temp) || count == 0)
> -		return -EINVAL;
> -
> -	if (copy_from_user(temp, buf, count) != 0)
> -		return -EFAULT;
> +	ret = kstrtoint_from_user(buf, count, 0, &i);
> 
> -	temp[count] = '\0';
> +	if (ret)
> +		return ret;
> 
> -	if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
> +	/* Reject invalid values */
> +	if (!i || (i & ~EVM_INIT_MASK) != 0)
>  		return -EINVAL;
> 
> -	evm_init_key();
> +	if (i & EVM_INIT_HMAC) {
> +		ret = evm_init_key();
> +		if (ret != 0)
> +			return ret;
> +		/* Forbid further writes after the symmetric key is loaded */
> +		i |= EVM_SETUP;
> +	}
> +
> +	evm_initialized |= i;
> 
>  	return count;
>  }
Matthew Garrett Oct. 16, 2017, 5:58 p.m. UTC | #2
On Sun, Oct 15, 2017 at 7:27 AM, Mimi Zohar <zohar@linux.vnet.ibm.com> wrote:
> On Wed, 2017-10-11 at 12:10 -0700, Matthew Garrett wrote:
>> EVM will only perform validation once a key has been loaded. This key
>> may either be a symmetric trusted key (for HMAC validation and creation)
>> or the public half of an asymmetric key (for digital signature
>> validation). The /sys/kernel/security/evm interface allows userland to
>> signal that a symmetric key has been loaded, but does not allow userland
>> to signal that an asymmetric public key has been loaded.
>>
>> This patch extends the interface to permit userspace to pass a bitmask
>> of loaded key types. It also allows userspace to block loading of an
>> asymmetric key in order to avoid a compromised system from being able to
>> load an additional key type later.
>
> I assume you mean "block loading of a symmetric key".  Other than this
> and a trailing blank line, the patch looks good.  If you don't have
> objections, I'll fix these two things.

Sorry, yes. That works for me - thank you!
diff mbox

Patch

diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm
index 8374d4557e5d..d2782afb0d96 100644
--- a/Documentation/ABI/testing/evm
+++ b/Documentation/ABI/testing/evm
@@ -7,17 +7,37 @@  Description:
 		HMAC-sha1 value across the extended attributes, storing the
 		value as the extended attribute 'security.evm'.
 
-		EVM depends on the Kernel Key Retention System to provide it
-		with a trusted/encrypted key for the HMAC-sha1 operation.
-		The key is loaded onto the root's keyring using keyctl.  Until
-		EVM receives notification that the key has been successfully
-		loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
-		can not create or validate the 'security.evm' xattr, but
-		returns INTEGRITY_UNKNOWN.  Loading the key 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/keys-trusted-encrypted.txt.  (A sample dracut
-		patch, which loads the trusted/encrypted key and enables
-		EVM, is available from http://linux-ima.sourceforge.net/#EVM.)
+		EVM supports two classes of security.evm. The first is
+		an HMAC-sha1 generated locally with a
+		trusted/encrypted key stored in the Kernel Key
+		Retention System. The second is a digital signature
+		generated either locally or remotely using an
+		asymmetric key. These keys are loaded onto root's
+		keyring using keyctl, and EVM is then enabled by
+		echoing a value to <securityfs>/evm:
+
+		1: enable HMAC validation and creation
+		2: enable digital signature validation
+		3: enable HMAC and digital signature validation and HMAC
+		   creation
+
+		Further writes will be blocked if HMAC support is enabled or
+		if bit 32 is set:
+
+		echo 0x80000002 ><securityfs>/evm
+
+		will enable digital signature validation and block
+		further writes to <securityfs>/evm.
+
+		Until this is done, 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/keys-trusted-encrypted.txt. Both dracut
+		(via 97masterkey and 98integrity) and systemd (via
+		core/ima-setup) have support for loading keys at boot
+		time.
+
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 2ff02459fcfd..946efffcc389 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -23,6 +23,9 @@ 
 
 #define EVM_INIT_HMAC	0x0001
 #define EVM_INIT_X509	0x0002
+#define EVM_SETUP       0x80000000 /* userland has signaled key load */
+
+#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP)
 
 extern int evm_initialized;
 extern char *evm_hmac;
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index c8dccd54d501..319cf16d6603 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -40,7 +40,7 @@  static ssize_t evm_read_key(struct file *filp, char __user *buf,
 	if (*ppos != 0)
 		return 0;
 
-	sprintf(temp, "%d", evm_initialized);
+	sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP));
 	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
 
 	return rc;
@@ -61,24 +61,29 @@  static ssize_t evm_read_key(struct file *filp, char __user *buf,
 static ssize_t evm_write_key(struct file *file, const char __user *buf,
 			     size_t count, loff_t *ppos)
 {
-	char temp[80];
-	int i;
+	int i, ret;
 
-	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC))
+	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP))
 		return -EPERM;
 
-	if (count >= sizeof(temp) || count == 0)
-		return -EINVAL;
-
-	if (copy_from_user(temp, buf, count) != 0)
-		return -EFAULT;
+	ret = kstrtoint_from_user(buf, count, 0, &i);
 
-	temp[count] = '\0';
+	if (ret)
+		return ret;
 
-	if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
+	/* Reject invalid values */
+	if (!i || (i & ~EVM_INIT_MASK) != 0)
 		return -EINVAL;
 
-	evm_init_key();
+	if (i & EVM_INIT_HMAC) {
+		ret = evm_init_key();
+		if (ret != 0)
+			return ret;
+		/* Forbid further writes after the symmetric key is loaded */
+		i |= EVM_SETUP;
+	}
+
+	evm_initialized |= i;
 
 	return count;
 }