275 lines
6.7 KiB
C
275 lines
6.7 KiB
C
/* 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,
|
|
};
|
|
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);
|