blob: b77eb53047882ad26de8de4892e8568a4b792326 [file] [log] [blame]
/* Asymmetric public-key cryptography key type
*
* See Documentation/security/asymmetric-keys.txt
*
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "asymmetric_keys.h"
MODULE_LICENSE("GPL");
static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
/*
* Match asymmetric keys on (part of) their name
* We have some shorthand methods for matching keys. We allow:
*
* "<desc>" - request a key by description
* "id:<id>" - request a key matching the ID
* "<subtype>:<id>" - request a key of a subtype
*/
static int asymmetric_key_match(const struct key *key, const void *description)
{
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
const char *spec = description;
const char *id, *kid;
ptrdiff_t speclen;
size_t idlen, kidlen;
if (!subtype || !spec || !*spec)
return 0;
/* See if the full key description matches as is */
if (key->description && strcmp(key->description, description) == 0)
return 1;
/* All tests from here on break the criterion description into a
* specifier, a colon and then an identifier.
*/
id = strchr(spec, ':');
if (!id)
return 0;
speclen = id - spec;
id++;
/* Anything after here requires a partial match on the ID string */
kid = asymmetric_key_id(key);
if (!kid)
return 0;
idlen = strlen(id);
kidlen = strlen(kid);
if (idlen > kidlen)
return 0;
kid += kidlen - idlen;
if (strcasecmp(id, kid) != 0)
return 0;
if (speclen == 2 &&
memcmp(spec, "id", 2) == 0)
return 1;
if (speclen == subtype->name_len &&
memcmp(spec, subtype->name, speclen) == 0)
return 1;
return 0;
}
/*
* Describe the asymmetric key
*/
static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
{
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
const char *kid = asymmetric_key_id(key);
size_t n;
seq_puts(m, key->description);
if (subtype) {
seq_puts(m, ": ");
subtype->describe(key, m);
if (kid) {
seq_putc(m, ' ');
n = strlen(kid);
if (n <= 8)
seq_puts(m, kid);
else
seq_puts(m, kid + n - 8);
}
seq_puts(m, " [");
/* put something here to indicate the key's capabilities */
seq_putc(m, ']');
}
}
/*
* Preparse a asymmetric payload to get format the contents appropriately for the
* internal payload to cut down on the number of scans of the data performed.
*
* We also generate a proposed description from the contents of the key that
* can be used to name the key if the user doesn't want to provide one.
*/
static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
{
struct asymmetric_key_parser *parser;
int ret;
pr_devel("==>%s()\n", __func__);
if (prep->datalen == 0)
return -EINVAL;
down_read(&asymmetric_key_parsers_sem);
ret = -EBADMSG;
list_for_each_entry(parser, &asymmetric_key_parsers, link) {
pr_debug("Trying parser '%s'\n", parser->name);
ret = parser->parse(prep);
if (ret != -EBADMSG) {
pr_debug("Parser recognised the format (ret %d)\n",
ret);
break;
}
}
up_read(&asymmetric_key_parsers_sem);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
/*
* Clean up the preparse data
*/
static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
{
struct asymmetric_key_subtype *subtype = prep->type_data[0];
pr_devel("==>%s()\n", __func__);
if (subtype) {
subtype->destroy(prep->payload);
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
*/
static void asymmetric_key_destroy(struct key *key)
{
struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
if (subtype) {
subtype->destroy(key->payload.data);
module_put(subtype->owner);
key->type_data.p[0] = NULL;
}
kfree(key->type_data.p[1]);
key->type_data.p[1] = NULL;
}
struct key_type key_type_asymmetric = {
.name = "asymmetric",
.preparse = asymmetric_key_preparse,
.free_preparse = asymmetric_key_free_preparse,
.instantiate = asymmetric_key_instantiate,
.match = asymmetric_key_match,
.destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe,
.def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
};
EXPORT_SYMBOL_GPL(key_type_asymmetric);
/**
* register_asymmetric_key_parser - Register a asymmetric key blob parser
* @parser: The parser to register
*/
int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
{
struct asymmetric_key_parser *cursor;
int ret;
down_write(&asymmetric_key_parsers_sem);
list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
if (strcmp(cursor->name, parser->name) == 0) {
pr_err("Asymmetric key parser '%s' already registered\n",
parser->name);
ret = -EEXIST;
goto out;
}
}
list_add_tail(&parser->link, &asymmetric_key_parsers);
pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
ret = 0;
out:
up_write(&asymmetric_key_parsers_sem);
return ret;
}
EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
/**
* unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
* @parser: The parser to unregister
*/
void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
{
down_write(&asymmetric_key_parsers_sem);
list_del(&parser->link);
up_write(&asymmetric_key_parsers_sem);
pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
}
EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
/*
* Module stuff
*/
static int __init asymmetric_key_init(void)
{
return register_key_type(&key_type_asymmetric);
}
static void __exit asymmetric_key_cleanup(void)
{
unregister_key_type(&key_type_asymmetric);
}
module_init(asymmetric_key_init);
module_exit(asymmetric_key_cleanup);