aboutsummaryrefslogtreecommitdiff
path: root/security/keys/key.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/keys/key.c')
-rw-r--r--security/keys/key.c114
1 files changed, 82 insertions, 32 deletions
diff --git a/security/keys/key.c b/security/keys/key.c
index 50d96d4e06f..1d039af99f5 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -412,8 +412,7 @@ EXPORT_SYMBOL(key_payload_reserve);
* key_construction_mutex.
*/
static int __key_instantiate_and_link(struct key *key,
- const void *data,
- size_t datalen,
+ struct key_preparsed_payload *prep,
struct key *keyring,
struct key *authkey,
unsigned long *_prealloc)
@@ -431,7 +430,7 @@ static int __key_instantiate_and_link(struct key *key,
/* can't instantiate twice */
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
/* instantiate the key */
- ret = key->type->instantiate(key, data, datalen);
+ ret = key->type->instantiate(key, prep);
if (ret == 0) {
/* mark the key as being instantiated */
@@ -482,22 +481,37 @@ int key_instantiate_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
+ struct key_preparsed_payload prep;
unsigned long prealloc;
int ret;
+ memset(&prep, 0, sizeof(prep));
+ prep.data = data;
+ prep.datalen = datalen;
+ prep.quotalen = key->type->def_datalen;
+ if (key->type->preparse) {
+ ret = key->type->preparse(&prep);
+ if (ret < 0)
+ goto error;
+ }
+
if (keyring) {
ret = __key_link_begin(keyring, key->type, key->description,
&prealloc);
if (ret < 0)
- return ret;
+ goto error_free_preparse;
}
- ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
+ ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
&prealloc);
if (keyring)
__key_link_end(keyring, key->type, prealloc);
+error_free_preparse:
+ if (key->type->preparse)
+ key->type->free_preparse(&prep);
+error:
return ret;
}
@@ -706,7 +720,7 @@ void key_type_put(struct key_type *ktype)
* if we get an error.
*/
static inline key_ref_t __key_update(key_ref_t key_ref,
- const void *payload, size_t plen)
+ struct key_preparsed_payload *prep)
{
struct key *key = key_ref_to_ptr(key_ref);
int ret;
@@ -722,7 +736,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
down_write(&key->sem);
- ret = key->type->update(key, payload, plen);
+ ret = key->type->update(key, prep);
if (ret == 0)
/* updating a negative key instantiates it */
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
@@ -774,6 +788,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
unsigned long flags)
{
unsigned long prealloc;
+ struct key_preparsed_payload prep;
const struct cred *cred = current_cred();
struct key_type *ktype;
struct key *keyring, *key = NULL;
@@ -789,8 +804,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
key_ref = ERR_PTR(-EINVAL);
- if (!ktype->match || !ktype->instantiate)
- goto error_2;
+ if (!ktype->match || !ktype->instantiate ||
+ (!description && !ktype->preparse))
+ goto error_put_type;
keyring = key_ref_to_ptr(keyring_ref);
@@ -798,18 +814,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring)
- goto error_2;
+ goto error_put_type;
+
+ memset(&prep, 0, sizeof(prep));
+ prep.data = payload;
+ prep.datalen = plen;
+ prep.quotalen = ktype->def_datalen;
+ if (ktype->preparse) {
+ ret = ktype->preparse(&prep);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_put_type;
+ }
+ if (!description)
+ description = prep.description;
+ key_ref = ERR_PTR(-EINVAL);
+ if (!description)
+ goto error_free_prep;
+ }
ret = __key_link_begin(keyring, ktype, description, &prealloc);
- if (ret < 0)
- goto error_2;
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_free_prep;
+ }
/* if we're going to allocate a new key, we're going to have
* to modify the keyring */
ret = key_permission(keyring_ref, KEY_WRITE);
if (ret < 0) {
key_ref = ERR_PTR(ret);
- goto error_3;
+ goto error_link_end;
}
/* if it's possible to update this type of key, search for an existing
@@ -840,25 +875,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
perm, flags);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
- goto error_3;
+ goto error_link_end;
}
/* instantiate it and link it into the target keyring */
- ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
- &prealloc);
+ ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
if (ret < 0) {
key_put(key);
key_ref = ERR_PTR(ret);
- goto error_3;
+ goto error_link_end;
}
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
- error_3:
+error_link_end:
__key_link_end(keyring, ktype, prealloc);
- error_2:
+error_free_prep:
+ if (ktype->preparse)
+ ktype->free_preparse(&prep);
+error_put_type:
key_type_put(ktype);
- error:
+error:
return key_ref;
found_matching_key:
@@ -866,10 +903,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
* - we can drop the locks first as we have the key pinned
*/
__key_link_end(keyring, ktype, prealloc);
- key_type_put(ktype);
- key_ref = __key_update(key_ref, payload, plen);
- goto error;
+ key_ref = __key_update(key_ref, &prep);
+ goto error_free_prep;
}
EXPORT_SYMBOL(key_create_or_update);
@@ -888,6 +924,7 @@ EXPORT_SYMBOL(key_create_or_update);
*/
int key_update(key_ref_t key_ref, const void *payload, size_t plen)
{
+ struct key_preparsed_payload prep;
struct key *key = key_ref_to_ptr(key_ref);
int ret;
@@ -900,18 +937,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
/* attempt to update it if supported */
ret = -EOPNOTSUPP;
- if (key->type->update) {
- down_write(&key->sem);
-
- ret = key->type->update(key, payload, plen);
- if (ret == 0)
- /* updating a negative key instantiates it */
- clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+ if (!key->type->update)
+ goto error;
- up_write(&key->sem);
+ memset(&prep, 0, sizeof(prep));
+ prep.data = payload;
+ prep.datalen = plen;
+ prep.quotalen = key->type->def_datalen;
+ if (key->type->preparse) {
+ ret = key->type->preparse(&prep);
+ if (ret < 0)
+ goto error;
}
- error:
+ down_write(&key->sem);
+
+ ret = key->type->update(key, &prep);
+ if (ret == 0)
+ /* updating a negative key instantiates it */
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+
+ up_write(&key->sem);
+
+ if (key->type->preparse)
+ key->type->free_preparse(&prep);
+error:
return ret;
}
EXPORT_SYMBOL(key_update);