Commit a19e3c22 authored by David Howells's avatar David Howells

Merge tag 'keys-preparse-1-20140722' into keys-next

Here are a set of changes that make all but encrypted and trusted keys use
preparsing.  Unfortunately, encrypted and trusted keys incorrectly use the
update op to alter a key, so other changes will need to be made for them.

These changes permit payload parsing when instantiating or updating a key to be
done before locks are taken and to determine the amount of quota that will be
required in advance.  The latter will make it possible to do LRU discard before
any locks are taken.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parents 32c2e675 f1dcde91
......@@ -1150,20 +1150,24 @@ The structure has a number of fields, some of which are mandatory:
const void *data;
size_t datalen;
size_t quotalen;
time_t expiry;
};
Before calling the method, the caller will fill in data and datalen with
the payload blob parameters; quotalen will be filled in with the default
quota size from the key type and the rest will be cleared.
quota size from the key type; expiry will be set to TIME_T_MAX and the
rest will be cleared.
If a description can be proposed from the payload contents, that should be
attached as a string to the description field. This will be used for the
key description if the caller of add_key() passes NULL or "".
The method can attach anything it likes to type_data[] and payload. These
are merely passed along to the instantiate() or update() operations.
are merely passed along to the instantiate() or update() operations. If
set, the expiry time will be applied to the key if it is instantiated from
this data.
The method should return 0 if success ful or a negative error code
The method should return 0 if successful or a negative error code
otherwise.
......@@ -1172,7 +1176,9 @@ The structure has a number of fields, some of which are mandatory:
This method is only required if the preparse() method is provided,
otherwise it is unused. It cleans up anything attached to the
description, type_data and payload fields of the key_preparsed_payload
struct as filled in by the preparse() method.
struct as filled in by the preparse() method. It will always be called
after preparse() returns successfully, even if instantiate() or update()
succeed.
(*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
......
......@@ -156,36 +156,13 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
pr_devel("==>%s()\n", __func__);
if (subtype) {
subtype->destroy(prep->payload);
subtype->destroy(prep->payload[0]);
module_put(subtype->owner);
}
kfree(prep->type_data[1]);
kfree(prep->description);
}
/*
* Instantiate a asymmetric_key defined key. The key was preparsed, so we just
* have to transfer the data here.
*/
static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
int ret;
pr_devel("==>%s()\n", __func__);
ret = key_payload_reserve(key, prep->quotalen);
if (ret == 0) {
key->type_data.p[0] = prep->type_data[0];
key->type_data.p[1] = prep->type_data[1];
key->payload.data = prep->payload;
prep->type_data[0] = NULL;
prep->type_data[1] = NULL;
prep->payload = NULL;
}
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
/*
* dispose of the data dangling from the corpse of a asymmetric key
*/
......@@ -205,7 +182,7 @@ struct key_type key_type_asymmetric = {
.name = "asymmetric",
.preparse = asymmetric_key_preparse,
.free_preparse = asymmetric_key_free_preparse,
.instantiate = asymmetric_key_instantiate,
.instantiate = generic_key_instantiate,
.match = asymmetric_key_match,
.destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe,
......
......@@ -177,7 +177,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
__module_get(public_key_subtype.owner);
prep->type_data[0] = &public_key_subtype;
prep->type_data[1] = cert->fingerprint;
prep->payload = cert->pub;
prep->payload[0] = cert->pub;
prep->description = desc;
prep->quotalen = 100;
......
......@@ -174,7 +174,9 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)
static struct key_type key_type_id_resolver = {
.name = "id_resolver",
.instantiate = user_instantiate,
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
......@@ -394,7 +396,9 @@ static const struct rpc_pipe_ops idmap_upcall_ops = {
static struct key_type key_type_id_resolver_legacy = {
.name = "id_legacy",
.instantiate = user_instantiate,
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
......
......@@ -16,7 +16,8 @@
extern struct key_type key_type_big_key;
extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
extern int big_key_preparse(struct key_preparsed_payload *prep);
extern void big_key_free_preparse(struct key_preparsed_payload *prep);
extern void big_key_revoke(struct key *key);
extern void big_key_destroy(struct key *key);
extern void big_key_describe(const struct key *big_key, struct seq_file *m);
......
......@@ -37,7 +37,8 @@ extern struct key_type key_type_logon;
struct key_preparsed_payload;
extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep);
extern int user_preparse(struct key_preparsed_payload *prep);
extern void user_free_preparse(struct key_preparsed_payload *prep);
extern int user_update(struct key *key, struct key_preparsed_payload *prep);
extern int user_match(const struct key *key, const void *criterion);
extern void user_revoke(struct key *key);
......
......@@ -41,10 +41,11 @@ struct key_construction {
struct key_preparsed_payload {
char *description; /* Proposed key description (or NULL) */
void *type_data[2]; /* Private key-type data */
void *payload; /* Proposed payload */
void *payload[2]; /* Proposed payload */
const void *data; /* Raw data */
size_t datalen; /* Raw datalen */
size_t quotalen; /* Quota length for proposed payload */
time_t expiry; /* Expiry time of key */
bool trusted; /* True if key is trusted */
};
......@@ -159,5 +160,7 @@ static inline int key_negate_and_link(struct key *key,
return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey);
}
extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
#endif /* CONFIG_KEYS */
#endif /* _LINUX_KEY_TYPE_H */
......@@ -8,6 +8,7 @@
#include <linux/key-type.h>
#include <keys/ceph-type.h>
#include <keys/user-type.h>
#include <linux/ceph/decode.h>
#include "crypto.h"
......@@ -423,8 +424,7 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
}
}
static int ceph_key_instantiate(struct key *key,
struct key_preparsed_payload *prep)
static int ceph_key_preparse(struct key_preparsed_payload *prep)
{
struct ceph_crypto_key *ckey;
size_t datalen = prep->datalen;
......@@ -435,10 +435,6 @@ static int ceph_key_instantiate(struct key *key,
if (datalen <= 0 || datalen > 32767 || !prep->data)
goto err;
ret = key_payload_reserve(key, datalen);
if (ret < 0)
goto err;
ret = -ENOMEM;
ckey = kmalloc(sizeof(*ckey), GFP_KERNEL);
if (!ckey)
......@@ -450,7 +446,8 @@ static int ceph_key_instantiate(struct key *key,
if (ret < 0)
goto err_ckey;
key->payload.data = ckey;
prep->payload[0] = ckey;
prep->quotalen = datalen;
return 0;
err_ckey:
......@@ -459,12 +456,15 @@ static int ceph_key_instantiate(struct key *key,
return ret;
}
static int ceph_key_match(const struct key *key, const void *description)
static void ceph_key_free_preparse(struct key_preparsed_payload *prep)
{
return strcmp(key->description, description) == 0;
struct ceph_crypto_key *ckey = prep->payload[0];
ceph_crypto_key_destroy(ckey);
kfree(ckey);
}
static void ceph_key_destroy(struct key *key) {
static void ceph_key_destroy(struct key *key)
{
struct ceph_crypto_key *ckey = key->payload.data;
ceph_crypto_key_destroy(ckey);
......@@ -473,8 +473,10 @@ static void ceph_key_destroy(struct key *key) {
struct key_type key_type_ceph = {
.name = "ceph",
.instantiate = ceph_key_instantiate,
.match = ceph_key_match,
.preparse = ceph_key_preparse,
.free_preparse = ceph_key_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.destroy = ceph_key_destroy,
};
......
......@@ -46,7 +46,7 @@ const struct cred *dns_resolver_cache;
#define DNS_ERRORNO_OPTION "dnserror"
/*
* Instantiate a user defined key for dns_resolver.
* Preparse instantiation data for a dns_resolver key.
*
* The data must be a NUL-terminated string, with the NUL char accounted in
* datalen.
......@@ -58,17 +58,15 @@ const struct cred *dns_resolver_cache;
* "ip1,ip2,...#foo=bar"
*/
static int
dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
dns_resolver_preparse(struct key_preparsed_payload *prep)
{
struct user_key_payload *upayload;
unsigned long derrno;
int ret;
size_t datalen = prep->datalen, result_len = 0;
int datalen = prep->datalen, result_len = 0;
const char *data = prep->data, *end, *opt;
kenter("%%%d,%s,'%*.*s',%zu",
key->serial, key->description,
(int)datalen, (int)datalen, data, datalen);
kenter("'%*.*s',%u", datalen, datalen, data, datalen);
if (datalen <= 1 || !data || data[datalen - 1] != '\0')
return -EINVAL;
......@@ -95,8 +93,7 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
opt_len = next_opt - opt;
if (!opt_len) {
printk(KERN_WARNING
"Empty option to dns_resolver key %d\n",
key->serial);
"Empty option to dns_resolver key\n");
return -EINVAL;
}
......@@ -125,30 +122,28 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
goto bad_option_value;
kdebug("dns error no. = %lu", derrno);
key->type_data.x[0] = -derrno;
prep->type_data[0] = ERR_PTR(-derrno);
continue;
}
bad_option_value:
printk(KERN_WARNING
"Option '%*.*s' to dns_resolver key %d:"
"Option '%*.*s' to dns_resolver key:"
" bad/missing value\n",
opt_nlen, opt_nlen, opt, key->serial);
opt_nlen, opt_nlen, opt);
return -EINVAL;
} while (opt = next_opt + 1, opt < end);
}
/* don't cache the result if we're caching an error saying there's no
* result */
if (key->type_data.x[0]) {
kleave(" = 0 [h_error %ld]", key->type_data.x[0]);
if (prep->type_data[0]) {
kleave(" = 0 [h_error %ld]", PTR_ERR(prep->type_data[0]));
return 0;
}
kdebug("store result");
ret = key_payload_reserve(key, result_len);
if (ret < 0)
return -EINVAL;
prep->quotalen = result_len;
upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
if (!upayload) {
......@@ -159,12 +154,22 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
upayload->datalen = result_len;
memcpy(upayload->data, data, result_len);
upayload->data[result_len] = '\0';
rcu_assign_pointer(key->payload.data, upayload);
prep->payload[0] = upayload;
kleave(" = 0");
return 0;
}
/*
* Clean up the preparse data
*/
static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
{
pr_devel("==>%s()\n", __func__);
kfree(prep->payload[0]);
}
/*
* The description is of the form "[<type>:]<domain_name>"
*
......@@ -234,7 +239,9 @@ static long dns_resolver_read(const struct key *key,
struct key_type key_type_dns_resolver = {
.name = "dns_resolver",
.instantiate = dns_resolver_instantiate,
.preparse = dns_resolver_preparse,
.free_preparse = dns_resolver_free_preparse,
.instantiate = generic_key_instantiate,
.match = dns_resolver_match,
.revoke = user_revoke,
.destroy = user_destroy,
......
This diff is collapsed.
......@@ -34,7 +34,9 @@ MODULE_LICENSE("GPL");
struct key_type key_type_big_key = {
.name = "big_key",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.instantiate = big_key_instantiate,
.preparse = big_key_preparse,
.free_preparse = big_key_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.revoke = big_key_revoke,
.destroy = big_key_destroy,
......@@ -43,11 +45,11 @@ struct key_type key_type_big_key = {
};
/*
* Instantiate a big key
* Preparse a big key
*/
int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
int big_key_preparse(struct key_preparsed_payload *prep)
{
struct path *path = (struct path *)&key->payload.data2;
struct path *path = (struct path *)&prep->payload;
struct file *file;
ssize_t written;
size_t datalen = prep->datalen;
......@@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
goto error;
/* Set an arbitrary quota */
ret = key_payload_reserve(key, 16);
if (ret < 0)
goto error;
prep->quotalen = 16;
key->type_data.x[1] = datalen;
prep->type_data[1] = (void *)(unsigned long)datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) {
/* Create a shmem file to store the data in. This will permit the data
......@@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
file = shmem_kernel_file_setup("", datalen, 0);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto err_quota;
goto error;
}
written = kernel_write(file, prep->data, prep->datalen, 0);
......@@ -93,23 +93,32 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
} else {
/* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto err_quota;
}
if (!data)
return -ENOMEM;
key->payload.data = memcpy(data, prep->data, prep->datalen);
prep->payload[0] = memcpy(data, prep->data, prep->datalen);
}
return 0;
err_fput:
fput(file);
err_quota:
key_payload_reserve(key, 0);
error:
return ret;
}
/*
* Clear preparsement.
*/
void big_key_free_preparse(struct key_preparsed_payload *prep)
{
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&prep->payload;
path_put(path);
} else {
kfree(prep->payload[0]);
}
}
/*
* dispose of the links from a revoked keyring
* - called with the key sem write-locked
......
......@@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key,
goto out;
}
rcu_assign_keypointer(key, epayload);
prep->payload[0] = epayload;
out:
kfree(datablob);
return ret;
......
......@@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key,
/* disable the authorisation key */
if (authkey)
key_revoke(authkey);
if (prep->expiry != TIME_T_MAX) {
key->expiry = prep->expiry;
key_schedule_gc(prep->expiry + key_gc_delay);
}
}
}
......@@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key,
prep.data = data;
prep.datalen = datalen;
prep.quotalen = key->type->def_datalen;
prep.expiry = TIME_T_MAX;
if (key->type->preparse) {
ret = key->type->preparse(&prep);
if (ret < 0)
......@@ -488,7 +494,7 @@ int key_instantiate_and_link(struct key *key,
if (keyring) {
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0)
goto error_free_preparse;
goto error;
}
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
......@@ -496,10 +502,9 @@ int key_instantiate_and_link(struct key *key,
if (keyring)
__key_link_end(keyring, &key->index_key, edit);
error_free_preparse:
error:
if (key->type->preparse)
key->type->free_preparse(&prep);
error:
return ret;
}
......@@ -811,11 +816,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
prep.datalen = plen;
prep.quotalen = index_key.type->def_datalen;
prep.trusted = flags & KEY_ALLOC_TRUSTED;
prep.expiry = TIME_T_MAX;
if (index_key.type->preparse) {
ret = index_key.type->preparse(&prep);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_put_type;
goto error_free_prep;
}
if (!index_key.description)
index_key.description = prep.description;
......@@ -941,6 +947,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
prep.data = payload;
prep.datalen = plen;
prep.quotalen = key->type->def_datalen;
prep.expiry = TIME_T_MAX;
if (key->type->preparse) {
ret = key->type->preparse(&prep);
if (ret < 0)
......@@ -956,9 +963,9 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
up_write(&key->sem);
error:
if (key->type->preparse)
key->type->free_preparse(&prep);
error:
return ret;
}
EXPORT_SYMBOL(key_update);
......@@ -1023,6 +1030,38 @@ void key_invalidate(struct key *key)
}
EXPORT_SYMBOL(key_invalidate);
/**
* generic_key_instantiate - Simple instantiation of a key from preparsed data
* @key: The key to be instantiated
* @prep: The preparsed data to load.
*
* Instantiate a key from preparsed data. We assume we can just copy the data
* in directly and clear the old pointers.
*
* This can be pointed to directly by the key type instantiate op pointer.
*/
int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
int ret;
pr_devel("==>%s()\n", __func__);
ret = key_payload_reserve(key, prep->quotalen);
if (ret == 0) {
key->type_data.p[0] = prep->type_data[0];
key->type_data.p[1] = prep->type_data[1];
rcu_assign_keypointer(key, prep->payload[0]);
key->payload.data2[1] = prep->payload[1];
prep->type_data[0] = NULL;
prep->type_data[1] = NULL;
prep->payload[0] = NULL;
prep->payload[1] = NULL;
}
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL(generic_key_instantiate);
/**
* register_key_type - Register a type of key.
* @ktype: The new key type.
......
......@@ -73,6 +73,8 @@ static inline unsigned keyring_hash(const char *desc)
* can be treated as ordinary keys in addition to having their own special
* operations.
*/
static int keyring_preparse(struct key_preparsed_payload *prep);
static void keyring_free_preparse(struct key_preparsed_payload *prep);
static int keyring_instantiate(struct key *keyring,
struct key_preparsed_payload *prep);
static void keyring_revoke(struct key *keyring);
......@@ -84,6 +86,8 @@ static long keyring_read(const struct key *keyring,
struct key_type key_type_keyring = {
.name = "keyring",
.def_datalen = 0,
.preparse = keyring_preparse,
.free_preparse = keyring_free_preparse,
.instantiate = keyring_instantiate,
.match = user_match,
.revoke = keyring_revoke,
......@@ -122,6 +126,21 @@ static void keyring_publish_name(struct key *keyring)
}
}
/*
* Preparse a keyring payload
*/
static int keyring_preparse(struct key_preparsed_payload *prep)
{
return prep->datalen != 0 ? -EINVAL : 0;
}
/*
* Free a preparse of a user defined key payload
*/
static void keyring_free_preparse(struct key_preparsed_payload *prep)
{
}
/*
* Initialise a keyring.
*
......@@ -130,17 +149,10 @@ static void keyring_publish_name(struct key *keyring)
static int keyring_instantiate(struct key *keyring,
struct key_preparsed_payload *prep)
{
int ret;
ret = -EINVAL;
if (prep->datalen == 0) {
assoc_array_init(&keyring->keys);
/* make the keyring available by name if it has one */
keyring_publish_name(keyring);
ret = 0;
}
return ret;
assoc_array_init(&keyring->keys);
/* make the keyring available by name if it has one */
keyring_publish_name(keyring);
return 0;
}
/*
......
......@@ -20,6 +20,8 @@
#include "internal.h"
#include <keys/user-type.h>
static int request_key_auth_preparse(struct key_preparsed_payload *);
static void request_key_auth_free_preparse(struct key_preparsed_payload *);
static int request_key_auth_instantiate(struct key *,
struct key_preparsed_payload *);
static void request_key_auth_describe(const struct key *, struct seq_file *);
......@@ -33,6 +35,8 @@ static long request_key_auth_read(const struct key *, char __user *, size_t);
struct key_type key_type_request_key_auth = {
.name = ".request_key_auth",
.def_datalen = sizeof(struct request_key_auth),
.preparse = request_key_auth_preparse,
.free_preparse = request_key_auth_free_preparse,
.instantiate = request_key_auth_instantiate,
.describe = request_key_auth_describe,
.revoke = request_key_auth_revoke,
......@@ -40,6 +44,15 @@ struct key_type key_type_request_key_auth = {
.read = request_key_auth_read,
};
int request_key_auth_preparse(struct key_preparsed_payload *prep)
{
return 0;
}
void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
{
}
/*
* Instantiate a request-key authorisation key.
*/
......
......@@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc);
struct key_type key_type_user = {
.name = "user",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.instantiate = user_instantiate,
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.update = user_update,
.match = user_match,
.revoke = user_revoke,
......@@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user);
struct key_type key_type_logon = {
.name = "logon",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.instantiate = user_instantiate,
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.update = user_update,
.match = user_match,
.revoke = user_revoke,
......@@ -58,38 +62,37 @@ struct key_type key_type_logon = {
EXPORT_SYMBOL_GPL(key_type_logon);
/*
* instantiate a user defined key
* Preparse a user defined key payload
*/
int user_instantiate(struct key *key, struct key_preparsed_payload *prep)
int user_preparse(struct key_preparsed_payload *prep)
{
struct user_key_payload *upayload;
size_t datalen = prep->datalen;
int ret;
ret = -EINVAL;
if (datalen <= 0 || datalen > 32767 || !prep->data)
goto error;
ret = key_payload_reserve(key, datalen);
if (ret < 0)
goto error;
return -EINVAL;
ret = -ENOMEM;
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
if (!upayload)
goto error;
return -ENOMEM;
/* attach the data */
prep->quotalen = datalen;
prep->payload[0] = upayload;
upayload->datalen = datalen;
memcpy(upayload->data, prep->data, datalen);
rcu_assign_keypointer(key, upayload);
ret = 0;
error:
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(user_preparse);
EXPORT_SYMBOL_GPL(user_instantiate);
/*
* Free a preparse of a user defined key payload
*/
void user_free_preparse(struct key_preparsed_payload *prep)
{
kfree(prep->payload[0]);
}
EXPORT_SYMBOL_GPL(user_free_preparse);
/*
* update a user defined key
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment