All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: d.kasatkin@samsung.com, zohar@us.ibm.com
Cc: keyrings@linux-nfs.org, linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 9/9] KEYS: Fix encrypted key type update method
Date: Mon, 04 Nov 2013 16:23:21 +0000	[thread overview]
Message-ID: <20131104162321.10177.34033.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <20131104162216.10177.98067.stgit@warthog.procyon.org.uk>

The encrypted key type was using the update method to change the master key
used to encrypt a key without passing in all the requisite parameters to
completely replace the key contents (it was taking some parameters from the
old key contents).  Unfortunately, this has a number of problems:

 (1) Update is conceptually meant to be the same as add_key() except that it
     replaces the contents of the nominated key completely rather than creating
     a new key.

 (2) add_key() can call ->update() if it detects that the key exists.  This can
     cause the operation to fail if the caller embedded the wrong command in
     the payload when they called it.  The caller cannot know what the right
     command is without someway to lock the keyring.

 (3) keyctl_update() and add_key() can thus race with adding, linking,
     requesting and unlinking a key.

The best way to fix this is to offload this operation to keyctl_control() and
make encrypted_update() just replace the contents of a key entirely (or maybe
just not permit updates if this would be a problem).

Signed-off-by: David Howells <dhowells@redhat.com>
---

 security/keys/encrypted-keys/encrypted.c |  101 ++++++++++++++----------------
 1 file changed, 47 insertions(+), 54 deletions(-)

diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index f9e7b808fa47..c9e4fa5b1e2c 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -595,7 +595,7 @@ out:
 }
 
 /* Allocate memory for decrypted key and datablob. */
-static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
+static struct encrypted_key_payload *encrypted_key_alloc(size_t *_quotalen,
 							 const char *format,
 							 const char *master_desc,
 							 const char *datalen)
@@ -632,10 +632,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
 	datablob_len = format_len + 1 + strlen(master_desc) + 1
 	    + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
 
-	ret = key_payload_reserve(key, payload_datalen + datablob_len
-				  + HASH_SIZE + 1);
-	if (ret < 0)
-		return ERR_PTR(ret);
+	*_quotalen = payload_datalen + datablob_len + HASH_SIZE + 1;
 
 	epayload = kzalloc(sizeof(*epayload) + payload_datalen +
 			   datablob_len + HASH_SIZE + 1, GFP_KERNEL);
@@ -773,8 +770,7 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
  *
  * On success, return 0. Otherwise return errno.
  */
-static int encrypted_instantiate(struct key *key,
-				 struct key_preparsed_payload *prep)
+static int encrypted_preparse(struct key_preparsed_payload *prep)
 {
 	struct encrypted_key_payload *epayload = NULL;
 	char *datablob = NULL;
@@ -798,25 +794,55 @@ static int encrypted_instantiate(struct key *key,
 	if (ret < 0)
 		goto out;
 
-	epayload = encrypted_key_alloc(key, format, master_desc,
+	epayload = encrypted_key_alloc(&prep->quotalen, format, master_desc,
 				       decrypted_datalen);
 	if (IS_ERR(epayload)) {
 		ret = PTR_ERR(epayload);
 		goto out;
 	}
-	ret = encrypted_init(epayload, key->description, format, master_desc,
+	ret = encrypted_init(epayload, prep->description, format, master_desc,
 			     decrypted_datalen, hex_encoded_iv);
 	if (ret < 0) {
 		kfree(epayload);
 		goto out;
 	}
 
-	rcu_assign_keypointer(key, epayload);
+	prep->payload[0] = epayload;
+
 out:
 	kfree(datablob);
 	return ret;
 }
 
+/*
+ * encrypted_free_preparse - Clean up preparse data for an encrypted key
+ */
+static void encrypted_free_preparse(struct key_preparsed_payload *prep)
+{
+	struct encrypted_key_payload *epayload = prep->payload[0];
+
+	if (epayload) {
+		memset(epayload->decrypted_data, 0,
+		       epayload->decrypted_datalen);
+		kfree(epayload);
+	}
+}
+
+static int encrypted_instantiate(struct key *key,
+				 struct key_preparsed_payload *prep)
+{
+	struct encrypted_key_payload *epayload = prep->payload[0];
+	int ret;
+
+	ret = key_payload_reserve(key, prep->quotalen);
+	if (ret < 0)
+		return ret;
+
+	rcu_assign_keypointer(key, epayload);
+	prep->payload[0] = NULL;
+	return 0;
+}
+
 static void encrypted_rcu_free(struct rcu_head *rcu)
 {
 	struct encrypted_key_payload *epayload;
@@ -837,50 +863,15 @@ static void encrypted_rcu_free(struct rcu_head *rcu)
  */
 static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
 {
-	struct encrypted_key_payload *epayload = key->payload.data;
-	struct encrypted_key_payload *new_epayload;
-	char *buf;
-	char *new_master_desc = NULL;
-	const char *format = NULL;
-	size_t datalen = prep->datalen;
-	int ret = 0;
-
-	if (datalen <= 0 || datalen > 32767 || !prep->data)
-		return -EINVAL;
+	struct encrypted_key_payload *old;
+	struct encrypted_key_payload *new = prep->payload[0];
 
-	buf = kmalloc(datalen + 1, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	buf[datalen] = 0;
-	memcpy(buf, prep->data, datalen);
-	ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
-	if (ret < 0)
-		goto out;
+	old = rcu_dereference_protected(key->payload.rcudata, &key->sem);
 
-	ret = valid_master_desc(new_master_desc, epayload->master_desc);
-	if (ret < 0)
-		goto out;
-
-	new_epayload = encrypted_key_alloc(key, epayload->format,
-					   new_master_desc, epayload->datalen);
-	if (IS_ERR(new_epayload)) {
-		ret = PTR_ERR(new_epayload);
-		goto out;
-	}
-
-	__ekey_init(new_epayload, epayload->format, new_master_desc,
-		    epayload->datalen);
-
-	memcpy(new_epayload->iv, epayload->iv, ivsize);
-	memcpy(new_epayload->payload_data, epayload->payload_data,
-	       epayload->payload_datalen);
-
-	rcu_assign_keypointer(key, new_epayload);
-	call_rcu(&epayload->rcu, encrypted_rcu_free);
-out:
-	kfree(buf);
-	return ret;
+	rcu_assign_keypointer(key, new);
+	prep->payload[0] = NULL;
+	call_rcu(&old->rcu, encrypted_rcu_free);
+	return 0;
 }
 
 /*
@@ -893,7 +884,7 @@ static long encrypted_control(struct key *key, char *command,
 	struct encrypted_key_payload *epayload, *new_epayload;
 	char *new_master_desc = NULL;
 	const char *format = NULL;
-	size_t datalen;
+	size_t datalen, quotalen;
 	int ret;
 
 	if (memcmp(command, expected_command, sizeof(expected_command) - 1) != 0)
@@ -917,7 +908,7 @@ static long encrypted_control(struct key *key, char *command,
 		return ret;
 	}
 
-	new_epayload = encrypted_key_alloc(key, epayload->format,
+	new_epayload = encrypted_key_alloc(&quotalen, epayload->format,
 					   new_master_desc, epayload->datalen);
 	if (IS_ERR(new_epayload)) {
 		up_write(&key->sem);
@@ -1023,6 +1014,8 @@ static void encrypted_destroy(struct key *key)
 
 struct key_type key_type_encrypted = {
 	.name = "encrypted",
+	.preparse = encrypted_preparse,
+	.free_preparse = encrypted_free_preparse,
 	.instantiate = encrypted_instantiate,
 	.update = encrypted_update,
 	.match = user_match,


  parent reply	other threads:[~2013-11-04 17:26 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-11-04 16:22 [RFC][PATCH 0/9] encrypted keys & key control op David Howells
2013-11-04 16:22 ` [PATCH 1/9] KEYS: The RSA public key algorithm needs to select MPILIB David Howells
2013-11-04 16:22 ` [PATCH 2/9] KEYS: Provide a generic instantiation function David Howells
2013-11-04 16:22 ` [PATCH 3/9] KEYS: struct key_preparsed_payload should have two payload pointers David Howells
2013-11-04 16:22 ` [PATCH 4/9] KEYS: Allow expiry time to be set when preparsing a key David Howells
2013-11-04 16:22 ` [PATCH 5/9] KEYS: Call ->free_preparse() even after ->preparse() returns an error David Howells
2013-11-04 16:23 ` [PATCH 6/9] KEYS: Trusted: Use key preparsing David Howells
2013-11-13 16:49   ` Mimi Zohar
2013-11-14 15:50   ` David Howells
2013-11-04 16:23 ` [PATCH 7/9] KEYS: Add a keyctl function to alter/control a key in type-dependent way David Howells
2013-11-04 16:23 ` [PATCH 8/9] KEYS: Implement keyctl control for encrypted keys David Howells
2013-11-04 16:23 ` David Howells [this message]
2013-11-13 18:45   ` [PATCH 9/9] KEYS: Fix encrypted key type update method Mimi Zohar
2013-11-14 17:59   ` David Howells
2013-11-17  3:51     ` Mimi Zohar
2013-11-17  9:17     ` David Howells
2013-11-17 13:43       ` Mimi Zohar
2013-11-06 17:20 ` [RFC][PATCH 0/9] encrypted keys & key control op Dmitry Kasatkin
2013-11-06 17:42 ` David Howells
2013-11-11 12:14 ` Mimi Zohar
2013-11-11 16:32 ` Mimi Zohar
2013-11-11 22:34 ` David Howells
2013-11-12  0:26   ` Mimi Zohar
2013-11-12 16:19   ` David Howells
2013-11-11 22:35 ` David Howells

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20131104162321.10177.34033.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=d.kasatkin@samsung.com \
    --cc=keyrings@linux-nfs.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=zohar@us.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.