Commit d3e4f419 authored by David Howells's avatar David Howells

Merge tag 'keys-pkcs7-20140916' into keys-next

Changes for next to improve the matching of asymmetric keys and to improve the
handling of PKCS#7 certificates:

 (1) Provide a method to preparse the data supplied for matching a key.  This
     permits they key type to extract out the bits it needs for matching once
     only.

     Further, the type of search (direct lookup or iterative) can be set and
     the function used to actually check the match can be set by preparse
     rather than being hard coded for the type.

 (2) Improves asymmetric keys identification.

     Keys derived from X.509 certs now get labelled with IDs derived from their
     issuer and certificate number (required to match PKCS#7) and from their
     SKID and subject (required to match X.509).

     IDs are now binary and match criterion preparsing is provided so that
     criteria can be turned into binary blobs to make matching faster.

 (3) Improves PKCS#7 message handling to permit PKCS#7 messages without X.509
     cert lists to be matched to trusted keys, thereby allowing minimally sized
     PKCS#7 certs to be used.

 (4) Improves PKCS#7 message handling to better handle certificate chains that
     are broken due to unsupported crypto that can otherwise by used to
     intersect a trust keyring.

These must go on top of the PKCS#7 parser cleanup fixes.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parents 1c9c115c 757932e6
......@@ -888,11 +888,11 @@ payload contents" for more information.
const char *callout_info);
This is used to request a key or keyring with a description that matches
the description specified according to the key type's match function. This
permits approximate matching to occur. If callout_string is not NULL, then
/sbin/request-key will be invoked in an attempt to obtain the key from
userspace. In that case, callout_string will be passed as an argument to
the program.
the description specified according to the key type's match_preparse()
method. This permits approximate matching to occur. If callout_string is
not NULL, then /sbin/request-key will be invoked in an attempt to obtain
the key from userspace. In that case, callout_string will be passed as an
argument to the program.
Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be
returned.
......@@ -1170,7 +1170,7 @@ The structure has a number of fields, some of which are mandatory:
The method should return 0 if successful or a negative error code
otherwise.
(*) void (*free_preparse)(struct key_preparsed_payload *prep);
This method is only required if the preparse() method is provided,
......@@ -1225,16 +1225,55 @@ The structure has a number of fields, some of which are mandatory:
It is safe to sleep in this method.
(*) int (*match)(const struct key *key, const void *desc);
(*) int (*match_preparse)(struct key_match_data *match_data);
This method is optional. It is called when a key search is about to be
performed. It is given the following structure:
This method is called to match a key against a description. It should
return non-zero if the two match, zero if they don't.
struct key_match_data {
bool (*cmp)(const struct key *key,
const struct key_match_data *match_data);
const void *raw_data;
void *preparsed;
unsigned lookup_type;
};
This method should not need to lock the key in any way. The type and
description can be considered invariant, and the payload should not be
accessed (the key may not yet be instantiated).
On entry, raw_data will be pointing to the criteria to be used in matching
a key by the caller and should not be modified. (*cmp)() will be pointing
to the default matcher function (which does an exact description match
against raw_data) and lookup_type will be set to indicate a direct lookup.
It is not safe to sleep in this method; the caller may hold spinlocks.
The following lookup_type values are available:
[*] KEYRING_SEARCH_LOOKUP_DIRECT - A direct lookup hashes the type and
description to narrow down the search to a small number of keys.
[*] KEYRING_SEARCH_LOOKUP_ITERATE - An iterative lookup walks all the
keys in the keyring until one is matched. This must be used for any
search that's not doing a simple direct match on the key description.
The method may set cmp to point to a function of its choice that does some
other form of match, may set lookup_type to KEYRING_SEARCH_LOOKUP_ITERATE
and may attach something to the preparsed pointer for use by (*cmp)().
(*cmp)() should return true if a key matches and false otherwise.
If preparsed is set, it may be necessary to use the match_free() method to
clean it up.
The method should return 0 if successful or a negative error code
otherwise.
It is permitted to sleep in this method, but (*cmp)() may not sleep as
locks will be held over it.
If match_preparse() is not provided, keys of this type will be matched
exactly by their description.
(*) void (*match_free)(struct key_match_data *match_data);
This method is optional. If given, it called to clean up
match_data->preparsed after a successful call to match_preparse().
(*) void (*revoke)(struct key *key);
......
......@@ -9,9 +9,13 @@
* 2 of the Licence, or (at your option) any later version.
*/
int asymmetric_keyid_match(const char *kid, const char *id);
extern bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids,
const struct asymmetric_key_id *match_id);
static inline const char *asymmetric_key_id(const struct key *key)
extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
static inline
const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
{
return key->type_data.p[1];
}
......@@ -15,6 +15,7 @@
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include "asymmetric_keys.h"
MODULE_LICENSE("GPL");
......@@ -22,85 +23,165 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
/*
* Match asymmetric key id with partial match
* @id: key id to match in a form "id:<id>"
/**
* asymmetric_key_generate_id: Construct an asymmetric key ID
* @val_1: First binary blob
* @len_1: Length of first binary blob
* @val_2: Second binary blob
* @len_2: Length of second binary blob
*
* Construct an asymmetric key ID from a pair of binary blobs.
*/
int asymmetric_keyid_match(const char *kid, const char *id)
struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
size_t len_1,
const void *val_2,
size_t len_2)
{
size_t idlen, kidlen;
struct asymmetric_key_id *kid;
kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
GFP_KERNEL);
if (!kid)
return ERR_PTR(-ENOMEM);
kid->len = len_1 + len_2;
memcpy(kid->data, val_1, len_1);
memcpy(kid->data + len_1, val_2, len_2);
return kid;
}
EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
if (!kid || !id)
return 0;
/**
* asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
* @kid_1, @kid_2: The key IDs to compare
*/
bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2)
{
if (!kid1 || !kid2)
return false;
if (kid1->len != kid2->len)
return false;
return memcmp(kid1->data, kid2->data, kid1->len) == 0;
}
EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
/* make it possible to use id as in the request: "id:<id>" */
if (strncmp(id, "id:", 3) == 0)
id += 3;
/**
* asymmetric_match_key_ids - Search asymmetric key IDs
* @kids: The list of key IDs to check
* @match_id: The key ID we're looking for
*/
bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids,
const struct asymmetric_key_id *match_id)
{
if (!kids || !match_id)
return false;
if (asymmetric_key_id_same(kids->id[0], match_id))
return true;
if (asymmetric_key_id_same(kids->id[1], match_id))
return true;
return false;
}
EXPORT_SYMBOL_GPL(asymmetric_match_key_ids);
/* Anything after here requires a partial match on the ID string */
idlen = strlen(id);
kidlen = strlen(kid);
if (idlen > kidlen)
return 0;
/**
* asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
* @id: The ID as a hex string.
*/
struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
{
struct asymmetric_key_id *match_id;
const char *p;
ptrdiff_t hexlen;
if (!*id)
return ERR_PTR(-EINVAL);
for (p = id; *p; p++)
if (!isxdigit(*p))
return ERR_PTR(-EINVAL);
hexlen = p - id;
if (hexlen & 1)
return ERR_PTR(-EINVAL);
match_id = kmalloc(sizeof(struct asymmetric_key_id) + hexlen / 2,
GFP_KERNEL);
if (!match_id)
return ERR_PTR(-ENOMEM);
match_id->len = hexlen / 2;
(void)hex2bin(match_id->data, id, hexlen / 2);
return match_id;
}
kid += kidlen - idlen;
if (strcasecmp(id, kid) != 0)
return 0;
/*
* Match asymmetric keys by ID.
*/
static bool asymmetric_key_cmp(const struct key *key,
const struct key_match_data *match_data)
{
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
const struct asymmetric_key_id *match_id = match_data->preparsed;
return 1;
return asymmetric_match_key_ids(kids, match_id);
}
EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
/*
* Match asymmetric keys on (part of) their name
* We have some shorthand methods for matching keys. We allow:
* Preparse the match criterion. If we don't set lookup_type and cmp,
* the default will be an exact match on the key description.
*
* "<desc>" - request a key by description
* "id:<id>" - request a key matching the ID
* "<subtype>:<id>" - request a key of a subtype
* There are some specifiers for matching key IDs rather than by the key
* description:
*
* "id:<id>" - request a key by any available ID
*
* These have to be searched by iteration rather than by direct lookup because
* the key is hashed according to its description.
*/
static int asymmetric_key_match(const struct key *key, const void *description)
static int asymmetric_key_match_preparse(struct key_match_data *match_data)
{
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
const char *spec = description;
struct asymmetric_key_id *match_id;
const char *spec = match_data->raw_data;
const char *id;
ptrdiff_t speclen;
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++;
if (!spec || !*spec)
return -EINVAL;
if (spec[0] == 'i' &&
spec[1] == 'd' &&
spec[2] == ':') {
id = spec + 3;
} else {
goto default_match;
}
if (speclen == 2 && memcmp(spec, "id", 2) == 0)
return asymmetric_keyid_match(asymmetric_key_id(key), id);
match_id = asymmetric_key_hex_to_key_id(id);
if (!match_id)
return -ENOMEM;
if (speclen == subtype->name_len &&
memcmp(spec, subtype->name, speclen) == 0)
return 1;
match_data->preparsed = match_id;
match_data->cmp = asymmetric_key_cmp;
match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
return 0;
default_match:
return 0;
}
/*
* Free the preparsed the match criterion.
*/
static void asymmetric_key_match_free(struct key_match_data *match_data)
{
kfree(match_data->preparsed);
}
/*
* 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;
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
const struct asymmetric_key_id *kid;
const unsigned char *p;
int n;
seq_puts(m, key->description);
......@@ -108,13 +189,16 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
seq_puts(m, ": ");
subtype->describe(key, m);
if (kid) {
if (kids && kids->id[0]) {
kid = kids->id[0];
seq_putc(m, ' ');
n = strlen(kid);
if (n <= 8)
seq_puts(m, kid);
else
seq_puts(m, kid + n - 8);
n = kid->len;
p = kid->data;
if (n > 8) {
p += n - 8;
n = 8;
}
seq_printf(m, "%*phN", n, p);
}
seq_puts(m, " [");
......@@ -165,6 +249,7 @@ static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
{
struct asymmetric_key_subtype *subtype = prep->type_data[0];
struct asymmetric_key_ids *kids = prep->type_data[1];
pr_devel("==>%s()\n", __func__);
......@@ -172,7 +257,11 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
subtype->destroy(prep->payload[0]);
module_put(subtype->owner);
}
kfree(prep->type_data[1]);
if (kids) {
kfree(kids->id[0]);
kfree(kids->id[1]);
kfree(kids);
}
kfree(prep->description);
}
......@@ -182,13 +271,20 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
static void asymmetric_key_destroy(struct key *key)
{
struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
struct asymmetric_key_ids *kids = key->type_data.p[1];
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;
if (kids) {
kfree(kids->id[0]);
kfree(kids->id[1]);
kfree(kids);
key->type_data.p[1] = NULL;
}
}
struct key_type key_type_asymmetric = {
......@@ -196,10 +292,10 @@ struct key_type key_type_asymmetric = {
.preparse = asymmetric_key_preparse,
.free_preparse = asymmetric_key_free_preparse,
.instantiate = generic_key_instantiate,
.match = asymmetric_key_match,
.match_preparse = asymmetric_key_match_preparse,
.match_free = asymmetric_key_match_free,
.destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe,
.def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
};
EXPORT_SYMBOL_GPL(key_type_asymmetric);
......
......@@ -72,11 +72,9 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)
*/
static struct key_type key_type_pkcs7 = {
.name = "pkcs7_test",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = pkcs7_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
......
......@@ -29,6 +29,10 @@ struct pkcs7_parse_context {
enum OID last_oid; /* Last OID encountered */
unsigned x509_index;
unsigned sinfo_index;
const void *raw_serial;
unsigned raw_serial_size;
unsigned raw_issuer_size;
const void *raw_issuer;
};
/*
......@@ -39,6 +43,7 @@ static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
if (sinfo) {
mpi_free(sinfo->sig.mpi[0]);
kfree(sinfo->sig.digest);
kfree(sinfo->signing_cert_id);
kfree(sinfo);
}
}
......@@ -251,10 +256,10 @@ int pkcs7_extract_cert(void *context, size_t hdrlen,
if (IS_ERR(x509))
return PTR_ERR(x509);
pr_debug("Got cert for %s\n", x509->subject);
pr_debug("- fingerprint %s\n", x509->fingerprint);
x509->index = ++ctx->x509_index;
pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
*ctx->ppcerts = x509;
ctx->ppcerts = &x509->next;
return 0;
......@@ -343,8 +348,8 @@ int pkcs7_sig_note_serial(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
ctx->sinfo->raw_serial = value;
ctx->sinfo->raw_serial_size = vlen;
ctx->raw_serial = value;
ctx->raw_serial_size = vlen;
return 0;
}
......@@ -356,8 +361,8 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
ctx->sinfo->raw_issuer = value;
ctx->sinfo->raw_issuer_size = vlen;
ctx->raw_issuer = value;
ctx->raw_issuer_size = vlen;
return 0;
}
......@@ -390,10 +395,21 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
ctx->sinfo->index = ++ctx->sinfo_index;
*ctx->ppsinfo = ctx->sinfo;
ctx->ppsinfo = &ctx->sinfo->next;
struct pkcs7_signed_info *sinfo = ctx->sinfo;
struct asymmetric_key_id *kid;
/* Generate cert issuer + serial number key ID */
kid = asymmetric_key_generate_id(ctx->raw_serial,
ctx->raw_serial_size,
ctx->raw_issuer,
ctx->raw_issuer_size);
if (IS_ERR(kid))
return PTR_ERR(kid);
sinfo->signing_cert_id = kid;
sinfo->index = ++ctx->sinfo_index;
*ctx->ppsinfo = sinfo;
ctx->ppsinfo = &sinfo->next;
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
if (!ctx->sinfo)
return -ENOMEM;
......
......@@ -23,6 +23,7 @@ struct pkcs7_signed_info {
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
unsigned index;
bool trusted;
bool unsupported_crypto; /* T if not usable due to missing crypto */
/* Message digest - the digest of the Content Data (or NULL) */
const void *msgdigest;
......@@ -33,10 +34,7 @@ struct pkcs7_signed_info {
const void *authattrs;
/* Issuing cert serial number and issuer's name */
const void *raw_serial;
unsigned raw_serial_size;
unsigned raw_issuer_size;
const void *raw_issuer;
struct asymmetric_key_id *signing_cert_id;
/* Message signature.
*
......
......@@ -35,6 +35,11 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
kenter(",%u,", sinfo->index);
if (sinfo->unsupported_crypto) {
kleave(" = -ENOPKG [cached]");
return -ENOPKG;
}
for (x509 = sinfo->signer; x509; x509 = x509->signer) {
if (x509->seen) {
if (x509->verified) {
......@@ -49,15 +54,17 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* Look to see if this certificate is present in the trusted
* keys.
*/
key = x509_request_asymmetric_key(trust_keyring, x509->subject,
x509->fingerprint);
if (!IS_ERR(key))
key = x509_request_asymmetric_key(trust_keyring, x509->id);
if (!IS_ERR(key)) {
/* One of the X.509 certificates in the PKCS#7 message
* is apparently the same as one we already trust.
* Verify that the trusted variant can also validate
* the signature on the descendant.
*/
pr_devel("sinfo %u: Cert %u as key %x\n",
sinfo->index, x509->index, key_serial(key));
goto matched;
}
if (key == ERR_PTR(-ENOMEM))
return -ENOMEM;
......@@ -77,16 +84,34 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* No match - see if the root certificate has a signer amongst the
* trusted keys.
*/
if (!last || !last->issuer || !last->authority) {
kleave(" = -ENOKEY [no backref]");
return -ENOKEY;
if (last && last->authority) {
key = x509_request_asymmetric_key(trust_keyring, last->authority);
if (!IS_ERR(key)) {
x509 = last;
pr_devel("sinfo %u: Root cert %u signer is key %x\n",
sinfo->index, x509->index, key_serial(key));
goto matched;
}
if (PTR_ERR(key) != -ENOKEY)
return PTR_ERR(key);
}
/* As a last resort, see if we have a trusted public key that matches
* the signed info directly.
*/
key = x509_request_asymmetric_key(trust_keyring,
sinfo->signing_cert_id);
if (!IS_ERR(key)) {
pr_devel("sinfo %u: Direct signer is key %x\n",
sinfo->index, key_serial(key));
x509 = NULL;
goto matched;
}
if (PTR_ERR(key) != -ENOKEY)
return PTR_ERR(key);
key = x509_request_asymmetric_key(trust_keyring, last->issuer,
last->authority);
if (IS_ERR(key))
return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY;
x509 = last;
kleave(" = -ENOKEY [no backref]");
return -ENOKEY;
matched:
ret = verify_signature(key, sig);
......@@ -100,10 +125,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
}
verified:
x509->verified = true;
for (p = sinfo->signer; p != x509; p = p->signer) {
p->verified = true;
p->trusted = trusted;
if (x509) {
x509->verified = true;
for (p = sinfo->signer; p != x509; p = p->signer) {
p->verified = true;
p->trusted = trusted;
}
}
sinfo->trusted = trusted;
kleave(" = 0");
......@@ -141,24 +168,28 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
{
struct pkcs7_signed_info *sinfo;
struct x509_certificate *p;
int cached_ret = 0, ret;
int cached_ret = -ENOKEY;
int ret;
for (p = pkcs7->certs; p; p = p->next)
p->seen = false;
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
if (ret < 0) {
if (ret == -ENOPKG) {
switch (ret) {
case -ENOKEY:
continue;
case -ENOPKG:
if (cached_ret == -ENOKEY)
cached_ret = -ENOPKG;
} else if (ret == -ENOKEY) {
if (cached_ret == 0)
cached_ret = -ENOKEY;
} else {
return ret;
}
continue;
case 0:
*_trusted |= sinfo->trusted;
cached_ret = 0;
continue;
default:
return ret;
}
*_trusted |= sinfo->trusted;
}
return cached_ret;
......
......@@ -131,8 +131,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
struct x509_certificate *x509;
unsigned certix = 1;
kenter("%u,%u,%u",
sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size);
kenter("%u", sinfo->index);
for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) {
/* I'm _assuming_ that the generator of the PKCS#7 message will
......@@ -140,21 +139,11 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
* PKCS#7 message - but I can't be 100% sure of that. It's
* possible this will need element-by-element comparison.
*/
if (x509->raw_serial_size != sinfo->raw_serial_size ||
memcmp(x509->raw_serial, sinfo->raw_serial,
sinfo->raw_serial_size) != 0)
if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
continue;
pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
sinfo->index, certix);
if (x509->raw_issuer_size != sinfo->raw_issuer_size ||
memcmp(x509->raw_issuer, sinfo->raw_issuer,
sinfo->raw_issuer_size) != 0) {
pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n",
sinfo->index);
continue;
}
if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
sinfo->index);
......@@ -164,9 +153,14 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
sinfo->signer = x509;
return 0;
}
pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial);
return -ENOKEY;
/* The relevant X.509 cert isn't found here, but it might be found in
* the trust keyring.
*/
pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
sinfo->index,
sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
return 0;
}
/*
......@@ -184,15 +178,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
p->seen = false;
for (;;) {
pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint);
pr_debug("verify %s: %*phN\n",
x509->subject,
x509->raw_serial_size, x509->raw_serial);
x509->seen = true;
ret = x509_get_sig_params(x509);
if (ret < 0)
return ret;
goto maybe_missing_crypto_in_x509;
pr_debug("- issuer %s\n", x509->issuer);
if (x509->authority)
pr_debug("- authkeyid %s\n", x509->authority);
pr_debug("- authkeyid %*phN\n",
x509->authority->len, x509->authority->data);
if (!x509->authority ||
strcmp(x509->subject, x509->issuer) == 0) {
......@@ -209,7 +206,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
ret = x509_check_signature(x509->pub, x509);
if (ret < 0)
return ret;
goto maybe_missing_crypto_in_x509;
x509->signer = x509;
pr_debug("- self-signed\n");
return 0;
......@@ -218,13 +215,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
/* Look through the X.509 certificates in the PKCS#7 message's
* list to see if the next one is there.
*/
pr_debug("- want %s\n", x509->authority);
pr_debug("- want %*phN\n",
x509->authority->len, x509->authority->data);
for (p = pkcs7->certs; p; p = p->next) {
pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint);
if (p->raw_subject_size == x509->raw_issuer_size &&
strcmp(p->fingerprint, x509->authority) == 0 &&
memcmp(p->raw_subject, x509->raw_issuer,
x509->raw_issuer_size) == 0)
if (!p->skid)
continue;
pr_debug("- cmp [%u] %*phN\n",
p->index, p->skid->len, p->skid->data);
if (asymmetric_key_id_same(p->skid, x509->authority))
goto found_issuer;
}
......@@ -233,7 +231,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
return 0;
found_issuer:
pr_debug("- issuer %s\n", p->subject);
pr_debug("- subject %s\n", p->subject);
if (p->seen) {
pr_warn("Sig %u: X.509 chain contains loop\n",
sinfo->index);
......@@ -250,6 +248,17 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
x509 = p;
might_sleep();
}
maybe_missing_crypto_in_x509:
/* Just prune the certificate chain at this point if we lack some
* crypto module to go further. Note, however, we don't want to set
* sinfo->missing_crypto as the signed info block may still be
* validatable against an X.509 cert lower in the chain that we have a
* trusted copy of.
*/
if (ret == -ENOPKG)
return 0;
return ret;
}
/*
......@@ -269,11 +278,14 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
if (ret < 0)
return ret;
/* Find the key for the signature */
/* Find the key for the signature if there is one */
ret = pkcs7_find_key(pkcs7, sinfo);
if (ret < 0)
return ret;
if (!sinfo->signer)
return 0;
pr_devel("Using X.509[%u] for sig %u\n",
sinfo->signer->index, sinfo->index);
......@@ -291,11 +303,33 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
/**
* pkcs7_verify - Verify a PKCS#7 message
* @pkcs7: The PKCS#7 message to be verified
*
* Verify a PKCS#7 message is internally consistent - that is, the data digest
* matches the digest in the AuthAttrs and any signature in the message or one
* of the X.509 certificates it carries that matches another X.509 cert in the
* message can be verified.
*
* This does not look to match the contents of the PKCS#7 message against any
* external public keys.
*
* Returns, in order of descending priority:
*
* (*) -EKEYREJECTED if a signature failed to match for which we found an
* appropriate X.509 certificate, or:
*
* (*) -EBADMSG if some part of the message was invalid, or:
*
* (*) -ENOPKG if none of the signature chains are verifiable because suitable
* crypto modules couldn't be found, or:
*
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
* (note that a signature chain may be of zero length), or:
*/
int pkcs7_verify(struct pkcs7_message *pkcs7)
{
struct pkcs7_signed_info *sinfo;
struct x509_certificate *x509;
int enopkg = -ENOPKG;
int ret, n;
kenter("");
......@@ -304,18 +338,24 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
ret = x509_get_sig_params(x509);
if (ret < 0)
return ret;
pr_debug("X.509[%u] %s\n", n, x509->authority);
pr_debug("X.509[%u] %*phN\n",
n, x509->authority->len, x509->authority->data);
}
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_verify_one(pkcs7, sinfo);
if (ret < 0) {
if (ret == -ENOPKG) {
sinfo->unsupported_crypto = true;
continue;
}
kleave(" = %d", ret);
return ret;
}
enopkg = 0;
}
kleave(" = 0");
return 0;
kleave(" = %d", enopkg);
return enopkg;
}
EXPORT_SYMBOL_GPL(pkcs7_verify);
......@@ -46,7 +46,8 @@ void x509_free_certificate(struct x509_certificate *cert)
public_key_destroy(cert->pub);
kfree(cert->issuer);
kfree(cert->subject);
kfree(cert->fingerprint);
kfree(cert->id);
kfree(cert->skid);
kfree(cert->authority);
kfree(cert->sig.digest);
mpi_free(cert->sig.rsa.s);
......@@ -62,6 +63,7 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
{
struct x509_certificate *cert;
struct x509_parse_context *ctx;
struct asymmetric_key_id *kid;
long ret;
ret = -ENOMEM;
......@@ -89,6 +91,17 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
if (ret < 0)
goto error_decode;
/* Generate cert issuer + serial number key ID */
kid = asymmetric_key_generate_id(cert->raw_serial,
cert->raw_serial_size,
cert->raw_issuer,
cert->raw_issuer_size);
if (IS_ERR(kid)) {
ret = PTR_ERR(kid);
goto error_decode;
}
cert->id = kid;
kfree(ctx);
return cert;
......@@ -407,36 +420,34 @@ int x509_process_extension(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
struct asymmetric_key_id *kid;
const unsigned char *v = value;
char *f;
int i;
pr_debug("Extension: %u\n", ctx->last_oid);
if (ctx->last_oid == OID_subjectKeyIdentifier) {
/* Get hold of the key fingerprint */
if (vlen < 3)
if (ctx->cert->skid || vlen < 3)
return -EBADMSG;
if (v[0] != ASN1_OTS || v[1] != vlen - 2)
return -EBADMSG;
v += 2;
vlen -= 2;
f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
if (!f)
return -ENOMEM;
for (i = 0; i < vlen; i++)
sprintf(f + i * 2, "%02x", v[i]);
pr_debug("fingerprint %s\n", f);
ctx->cert->fingerprint = f;
kid = asymmetric_key_generate_id(v, vlen,
ctx->cert->raw_subject,
ctx->cert->raw_subject_size);
if (IS_ERR(kid))
return PTR_ERR(kid);
ctx->cert->skid = kid;
pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
return 0;
}
if (ctx->last_oid == OID_authorityKeyIdentifier) {
size_t key_len;
/* Get hold of the CA key fingerprint */
if (vlen < 5)
if (ctx->cert->authority || vlen < 5)
return -EBADMSG;
/* Authority Key Identifier must be a Constructed SEQUENCE */
......@@ -454,7 +465,7 @@ int x509_process_extension(void *context, size_t hdrlen,
v[3] > vlen - 4)
return -EBADMSG;
key_len = v[3];
vlen = v[3];
v += 4;
} else {
/* Long Form length */
......@@ -476,17 +487,17 @@ int x509_process_extension(void *context, size_t hdrlen,
v[sub + 1] > vlen - 4 - sub)
return -EBADMSG;
key_len = v[sub + 1];
vlen = v[sub + 1];
v += (sub + 2);
}
f = kmalloc(key_len * 2 + 1, GFP_KERNEL);
if (!f)
return -ENOMEM;
for (i = 0; i < key_len; i++)
sprintf(f + i * 2, "%02x", v[i]);
pr_debug("authority %s\n", f);
ctx->cert->authority = f;
kid = asymmetric_key_generate_id(v, vlen,
ctx->cert->raw_issuer,
ctx->cert->raw_issuer_size);
if (IS_ERR(kid))
return PTR_ERR(kid);
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->authority = kid;
return 0;
}
......
......@@ -19,8 +19,9 @@ struct x509_certificate {
struct public_key_signature sig; /* Signature parameters */
char *issuer; /* Name of certificate issuer */
char *subject; /* Name of certificate subject */
char *fingerprint; /* Key fingerprint as hex */
char *authority; /* Authority key fingerprint as hex */
struct asymmetric_key_id *id; /* Issuer + serial number */
struct asymmetric_key_id *skid; /* Subject key identifier */
struct asymmetric_key_id *authority; /* Authority key identifier */
struct tm valid_from;
struct tm valid_to;
const void *tbs; /* Signed data */
......@@ -37,6 +38,7 @@ struct x509_certificate {
bool seen; /* Infinite recursion prevention */
bool verified;
bool trusted;
bool unsupported_crypto; /* T if can't be verified due to missing crypto */
};
/*
......
......@@ -25,7 +25,7 @@
#include "x509_parser.h"
static bool use_builtin_keys;
static char *ca_keyid;
static struct asymmetric_key_id *ca_keyid;
#ifndef MODULE
static int __init ca_keys_setup(char *str)
......@@ -33,10 +33,16 @@ static int __init ca_keys_setup(char *str)
if (!str) /* default system keyring */
return 1;
if (strncmp(str, "id:", 3) == 0)
ca_keyid = str; /* owner key 'id:xxxxxx' */
else if (strcmp(str, "builtin") == 0)
if (strncmp(str, "id:", 3) == 0) {
struct asymmetric_key_id *p;
p = asymmetric_key_hex_to_key_id(str);
if (p == ERR_PTR(-EINVAL))
pr_err("Unparsable hex string in ca_keys\n");
else if (!IS_ERR(p))
ca_keyid = p; /* owner key 'id:xxxxxx' */
} else if (strcmp(str, "builtin") == 0) {
use_builtin_keys = true;
}
return 1;
}
......@@ -46,31 +52,28 @@ __setup("ca_keys=", ca_keys_setup);
/**
* x509_request_asymmetric_key - Request a key by X.509 certificate params.
* @keyring: The keys to search.
* @subject: The name of the subject to whom the key belongs.
* @key_id: The subject key ID as a hex string.
* @kid: The key ID.
*
* Find a key in the given keyring by subject name and key ID. These might,
* for instance, be the issuer name and the authority key ID of an X.509
* certificate that needs to be verified.
*/
struct key *x509_request_asymmetric_key(struct key *keyring,
const char *subject,
const char *key_id)
const struct asymmetric_key_id *kid)
{
key_ref_t key;
size_t subject_len = strlen(subject), key_id_len = strlen(key_id);
char *id;
char *id, *p;
/* Construct an identifier "<subjname>:<keyid>". */
id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL);
/* Construct an identifier "id:<keyid>". */
p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL);
if (!id)
return ERR_PTR(-ENOMEM);
memcpy(id, subject, subject_len);
id[subject_len + 0] = ':';
id[subject_len + 1] = ' ';
memcpy(id + subject_len + 2, key_id, key_id_len);
id[subject_len + 2 + key_id_len] = 0;
*p++ = 'i';
*p++ = 'd';
*p++ = ':';
p = bin2hex(p, kid->data, kid->len);
*p = 0;
pr_debug("Look up: \"%s\"\n", id);
......@@ -112,6 +115,8 @@ int x509_get_sig_params(struct x509_certificate *cert)
pr_devel("==>%s()\n", __func__);
if (cert->unsupported_crypto)
return -ENOPKG;
if (cert->sig.rsa.s)
return 0;
......@@ -124,8 +129,13 @@ int x509_get_sig_params(struct x509_certificate *cert)
* big the hash operational data will be.
*/
tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
if (IS_ERR(tfm)) {
if (PTR_ERR(tfm) == -ENOENT) {
cert->unsupported_crypto = true;
return -ENOPKG;
}
return PTR_ERR(tfm);
}
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
digest_size = crypto_shash_digestsize(tfm);
......@@ -172,6 +182,8 @@ int x509_check_signature(const struct public_key *pub,
return ret;
ret = public_key_verify_signature(pub, &cert->sig);
if (ret == -ENOPKG)
cert->unsupported_crypto = true;
pr_debug("Cert Verification: %d\n", ret);
return ret;
}
......@@ -195,11 +207,10 @@ static int x509_validate_trust(struct x509_certificate *cert,
if (!trust_keyring)
return -EOPNOTSUPP;
if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid))
if (ca_keyid && !asymmetric_key_id_same(cert->authority, ca_keyid))
return -EPERM;
key = x509_request_asymmetric_key(trust_keyring,
cert->issuer, cert->authority);
key = x509_request_asymmetric_key(trust_keyring, cert->authority);
if (!IS_ERR(key)) {
if (!use_builtin_keys
|| test_bit(KEY_FLAG_BUILTIN, &key->flags))
......@@ -214,9 +225,11 @@ static int x509_validate_trust(struct x509_certificate *cert,
*/
static int x509_key_preparse(struct key_preparsed_payload *prep)
{
struct asymmetric_key_ids *kids;
struct x509_certificate *cert;
const char *q;
size_t srlen, sulen;
char *desc = NULL;
char *desc = NULL, *p;
int ret;
cert = x509_cert_parse(prep->data, prep->datalen);
......@@ -249,19 +262,12 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
pkey_algo_name[cert->sig.pkey_algo],
hash_algo_name[cert->sig.pkey_hash_algo]);
if (!cert->fingerprint) {
pr_warn("Cert for '%s' must have a SubjKeyId extension\n",
cert->subject);
ret = -EKEYREJECTED;
goto error_free_cert;
}
cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key if it appears to be self-signed */
if (!cert->authority ||
strcmp(cert->fingerprint, cert->authority) == 0) {
asymmetric_key_id_same(cert->skid, cert->authority)) {
ret = x509_check_signature(cert->pub, cert); /* self-signed */
if (ret < 0)
goto error_free_cert;
......@@ -273,31 +279,47 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
/* Propose a description */
sulen = strlen(cert->subject);
srlen = strlen(cert->fingerprint);
srlen = cert->raw_serial_size;
q = cert->raw_serial;
if (srlen > 1 && *q == 0) {
srlen--;
q++;
}
ret = -ENOMEM;
desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL);
desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL);
if (!desc)
goto error_free_cert;
memcpy(desc, cert->subject, sulen);
desc[sulen] = ':';
desc[sulen + 1] = ' ';
memcpy(desc + sulen + 2, cert->fingerprint, srlen);
desc[sulen + 2 + srlen] = 0;
p = memcpy(desc, cert->subject, sulen);
p += sulen;
*p++ = ':';
*p++ = ' ';
p = bin2hex(p, q, srlen);
*p = 0;
kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
if (!kids)
goto error_free_desc;
kids->id[0] = cert->id;
kids->id[1] = cert->skid;
/* We're pinning the module by being linked against it */
__module_get(public_key_subtype.owner);
prep->type_data[0] = &public_key_subtype;
prep->type_data[1] = cert->fingerprint;
prep->type_data[1] = kids;
prep->payload[0] = cert->pub;
prep->description = desc;
prep->quotalen = 100;
/* We've finished with the certificate */
cert->pub = NULL;
cert->fingerprint = NULL;
cert->id = NULL;
cert->skid = NULL;
desc = NULL;
ret = 0;
error_free_desc:
kfree(desc);
error_free_cert:
x509_free_certificate(cert);
return ret;
......
......@@ -62,7 +62,6 @@ cifs_spnego_key_destroy(struct key *key)
struct key_type cifs_spnego_key_type = {
.name = "cifs.spnego",
.instantiate = cifs_spnego_key_instantiate,
.match = user_match,
.destroy = cifs_spnego_key_destroy,
.describe = user_describe,
};
......
......@@ -84,7 +84,6 @@ static struct key_type cifs_idmap_key_type = {
.instantiate = cifs_idmap_key_instantiate,
.destroy = cifs_idmap_key_destroy,
.describe = user_describe,
.match = user_match,
};
static char *
......
......@@ -177,7 +177,6 @@ static struct key_type key_type_id_resolver = {
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
......@@ -401,7 +400,6 @@ static struct key_type key_type_id_resolver_legacy = {
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
......
......@@ -15,6 +15,7 @@
#define _LINUX_PUBLIC_KEY_H
#include <linux/mpi.h>
#include <keys/asymmetric-type.h>
#include <crypto/hash_info.h>
enum pkey_algo {
......@@ -98,8 +99,8 @@ struct key;
extern int verify_signature(const struct key *key,
const struct public_key_signature *sig);
struct asymmetric_key_id;
extern struct key *x509_request_asymmetric_key(struct key *keyring,
const char *issuer,
const char *key_id);
const struct asymmetric_key_id *kid);
#endif /* _LINUX_PUBLIC_KEY_H */
......@@ -18,6 +18,44 @@
extern struct key_type key_type_asymmetric;
/*
* Identifiers for an asymmetric key ID. We have three ways of looking up a
* key derived from an X.509 certificate:
*
* (1) Serial Number & Issuer. Non-optional. This is the only valid way to
* map a PKCS#7 signature to an X.509 certificate.
*
* (2) Issuer & Subject Unique IDs. Optional. These were the original way to
* match X.509 certificates, but have fallen into disuse in favour of (3).
*
* (3) Auth & Subject Key Identifiers. Optional. SKIDs are only provided on
* CA keys that are intended to sign other keys, so don't appear in end
* user certificates unless forced.
*
* We could also support an PGP key identifier, which is just a SHA1 sum of the
* public key and certain parameters, but since we don't support PGP keys at
* the moment, we shall ignore those.
*
* What we actually do is provide a place where binary identifiers can be
* stashed and then compare against them when checking for an id match.
*/
struct asymmetric_key_id {
unsigned short len;
unsigned char data[];
};
struct asymmetric_key_ids {
void *id[2];
};
extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2);
extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
size_t len_1,
const void *val_2,
size_t len_2);
/*
* The payload is at the discretion of the subtype.
*/
......
......@@ -40,7 +40,6 @@ struct key_preparsed_payload;
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);
extern void user_destroy(struct key *key);
extern void user_describe(const struct key *user, struct seq_file *m);
......
......@@ -500,6 +500,7 @@ static inline char * __deprecated pack_hex_byte(char *buf, u8 byte)
extern int hex_to_bin(char ch);
extern int __must_check hex2bin(u8 *dst, const char *src, size_t count);
extern char *bin2hex(char *dst, const void *src, size_t count);
int mac_pton(const char *s, u8 *mac);
......
......@@ -52,6 +52,24 @@ struct key_preparsed_payload {
typedef int (*request_key_actor_t)(struct key_construction *key,
const char *op, void *aux);
/*
* Preparsed matching criterion.
*/
struct key_match_data {
/* Comparison function, defaults to exact description match, but can be
* overridden by type->match_preparse(). Should return true if a match
* is found and false if not.
*/
bool (*cmp)(const struct key *key,
const struct key_match_data *match_data);
const void *raw_data; /* Raw match data */
void *preparsed; /* For ->match_preparse() to stash stuff */
unsigned lookup_type; /* Type of lookup for this search. */
#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */
#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */
};
/*
* kernel managed key type definition
*/
......@@ -65,11 +83,6 @@ struct key_type {
*/
size_t def_datalen;
/* Default key search algorithm. */
unsigned def_lookup_type;
#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */
#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */
/* vet a description */
int (*vet_description)(const char *description);
......@@ -96,8 +109,15 @@ struct key_type {
*/
int (*update)(struct key *key, struct key_preparsed_payload *prep);
/* match a key against a description */
int (*match)(const struct key *key, const void *desc);
/* Preparse the data supplied to ->match() (optional). The
* data to be preparsed can be found in match_data->raw_data.
* The lookup type can also be set by this function.
*/
int (*match_preparse)(struct key_match_data *match_data);
/* Free preparsed match data (optional). This should be supplied it
* ->match_preparse() is supplied. */
void (*match_free)(struct key_match_data *match_data);
/* clear some of the data from a key on revokation (optional)
* - the key's semaphore will be write-locked by the caller
......
......@@ -58,6 +58,22 @@ int hex2bin(u8 *dst, const char *src, size_t count)
}
EXPORT_SYMBOL(hex2bin);
/**
* bin2hex - convert binary data to an ascii hexadecimal string
* @dst: ascii hexadecimal result
* @src: binary data
* @count: binary data length
*/
char *bin2hex(char *dst, const void *src, size_t count)
{
const unsigned char *_src = src;
while (count--)
dst = hex_byte_pack(dst, *_src++);
return dst;
}
EXPORT_SYMBOL(bin2hex);
/**
* hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
* @buf: data blob to dump
......
......@@ -476,7 +476,6 @@ struct key_type key_type_ceph = {
.preparse = ceph_key_preparse,
.free_preparse = ceph_key_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.destroy = ceph_key_destroy,
};
......
......@@ -176,11 +176,11 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
* The domain name may be a simple name or an absolute domain name (which
* should end with a period). The domain name is case-independent.
*/
static int
dns_resolver_match(const struct key *key, const void *description)
static bool dns_resolver_cmp(const struct key *key,
const struct key_match_data *match_data)
{
int slen, dlen, ret = 0;
const char *src = key->description, *dsp = description;
const char *src = key->description, *dsp = match_data->raw_data;
kenter("%s,%s", src, dsp);
......@@ -208,6 +208,16 @@ dns_resolver_match(const struct key *key, const void *description)
return ret;
}
/*
* Preparse the match criterion.
*/
static int dns_resolver_match_preparse(struct key_match_data *match_data)
{
match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
match_data->cmp = dns_resolver_cmp;
return 0;
}
/*
* Describe a DNS key
*/
......@@ -242,7 +252,7 @@ struct key_type key_type_dns_resolver = {
.preparse = dns_resolver_preparse,
.free_preparse = dns_resolver_free_preparse,
.instantiate = generic_key_instantiate,
.match = dns_resolver_match,
.match_preparse = dns_resolver_match_preparse,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = dns_resolver_describe,
......
......@@ -44,7 +44,6 @@ struct key_type key_type_rxrpc = {
.preparse = rxrpc_preparse,
.free_preparse = rxrpc_free_preparse,
.instantiate = generic_key_instantiate,
.match = user_match,
.destroy = rxrpc_destroy,
.describe = rxrpc_describe,
.read = rxrpc_read,
......@@ -61,7 +60,6 @@ struct key_type key_type_rxrpc_s = {
.preparse = rxrpc_preparse_s,
.free_preparse = rxrpc_free_preparse_s,
.instantiate = generic_key_instantiate,
.match = user_match,
.destroy = rxrpc_destroy_s,
.describe = rxrpc_describe,
};
......
......@@ -33,11 +33,9 @@ MODULE_LICENSE("GPL");
*/
struct key_type key_type_big_key = {
.name = "big_key",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.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,
.describe = big_key_describe,
......
......@@ -970,7 +970,6 @@ struct key_type key_type_encrypted = {
.name = "encrypted",
.instantiate = encrypted_instantiate,
.update = encrypted_update,
.match = user_match,
.destroy = encrypted_destroy,
.describe = user_describe,
.read = encrypted_read,
......
......@@ -107,20 +107,16 @@ extern int iterate_over_keyring(const struct key *keyring,
int (*func)(const struct key *key, void *data),
void *data);
typedef int (*key_match_func_t)(const struct key *, const void *);
struct keyring_search_context {
struct keyring_index_key index_key;
const struct cred *cred;
key_match_func_t match;
const void *match_data;
struct key_match_data match_data;
unsigned flags;
#define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */
#define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */
#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */
#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */
#define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */
#define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */
#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */
int (*iterator)(const void *object, void *iterator_data);
......@@ -131,6 +127,8 @@ struct keyring_search_context {
struct timespec now;
};
extern bool key_default_cmp(const struct key *key,
const struct key_match_data *match_data);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
struct keyring_search_context *ctx);
......@@ -152,7 +150,8 @@ extern struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags);
extern int lookup_user_key_possessed(const struct key *key, const void *target);
extern bool lookup_user_key_possessed(const struct key *key,
const struct key_match_data *match_data);
extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
key_perm_t perm);
#define KEY_LOOKUP_CREATE 0x01
......
......@@ -799,7 +799,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
key_ref = ERR_PTR(-EINVAL);
if (!index_key.type->match || !index_key.type->instantiate ||
if (!index_key.type->instantiate ||
(!index_key.description && !index_key.type->preparse))
goto error_put_type;
......
......@@ -89,7 +89,6 @@ struct key_type key_type_keyring = {
.preparse = keyring_preparse,
.free_preparse = keyring_free_preparse,
.instantiate = keyring_instantiate,
.match = user_match,
.revoke = keyring_revoke,
.destroy = keyring_destroy,
.describe = keyring_describe,
......@@ -511,6 +510,15 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
}
EXPORT_SYMBOL(keyring_alloc);
/*
* By default, we keys found by getting an exact match on their descriptions.
*/
bool key_default_cmp(const struct key *key,
const struct key_match_data *match_data)
{
return strcmp(key->description, match_data->raw_data) == 0;
}
/*
* Iteration function to consider each key found.
*/
......@@ -545,7 +553,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
}
/* keys that don't match */
if (!ctx->match(key, ctx->match_data)) {
if (!ctx->match_data.cmp(key, &ctx->match_data)) {
kleave(" = 0 [!match]");
return 0;
}
......@@ -585,8 +593,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
*/
static int search_keyring(struct key *keyring, struct keyring_search_context *ctx)
{
if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) ==
KEYRING_SEARCH_LOOKUP_DIRECT) {
if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_DIRECT) {
const void *object;
object = assoc_array_find(&keyring->keys,
......@@ -627,7 +634,7 @@ static bool search_nested_keyrings(struct key *keyring,
/* Check to see if this top-level keyring is what we are looking for
* and whether it is valid or not.
*/
if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE ||
if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE ||
keyring_compare_object(keyring, &ctx->index_key)) {
ctx->skipped_ret = 2;
ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
......@@ -885,16 +892,25 @@ key_ref_t keyring_search(key_ref_t keyring,
.index_key.type = type,
.index_key.description = description,
.cred = current_cred(),
.match = type->match,
.match_data = description,
.flags = (type->def_lookup_type |
KEYRING_SEARCH_DO_STATE_CHECK),
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.flags = KEYRING_SEARCH_DO_STATE_CHECK,
};
key_ref_t key;
int ret;
if (!ctx.match)
return ERR_PTR(-ENOKEY);
if (type->match_preparse) {
ret = type->match_preparse(&ctx.match_data);
if (ret < 0)
return ERR_PTR(ret);
}
return keyring_search_aux(keyring, &ctx);
key = keyring_search_aux(keyring, &ctx);
if (type->match_free)
type->match_free(&ctx.match_data);
return key;
}
EXPORT_SYMBOL(keyring_search);
......@@ -1014,7 +1030,7 @@ static int keyring_detect_cycle_iterator(const void *object,
/* We might get a keyring with matching index-key that is nonetheless a
* different keyring. */
if (key != ctx->match_data)
if (key != ctx->match_data.raw_data)
return 0;
ctx->result = ERR_PTR(-EDEADLK);
......@@ -1031,14 +1047,14 @@ static int keyring_detect_cycle_iterator(const void *object,
static int keyring_detect_cycle(struct key *A, struct key *B)
{
struct keyring_search_context ctx = {
.index_key = A->index_key,
.match_data = A,
.iterator = keyring_detect_cycle_iterator,
.flags = (KEYRING_SEARCH_LOOKUP_DIRECT |
KEYRING_SEARCH_NO_STATE_CHECK |
KEYRING_SEARCH_NO_UPDATE_TIME |
KEYRING_SEARCH_NO_CHECK_PERM |
KEYRING_SEARCH_DETECT_TOO_DEEP),
.index_key = A->index_key,
.match_data.raw_data = A,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.iterator = keyring_detect_cycle_iterator,
.flags = (KEYRING_SEARCH_NO_STATE_CHECK |
KEYRING_SEARCH_NO_UPDATE_TIME |
KEYRING_SEARCH_NO_CHECK_PERM |
KEYRING_SEARCH_DETECT_TOO_DEEP),
};
rcu_read_lock();
......
......@@ -194,10 +194,10 @@ static int proc_keys_show(struct seq_file *m, void *v)
.index_key.type = key->type,
.index_key.description = key->description,
.cred = current_cred(),
.match = lookup_user_key_possessed,
.match_data = key,
.flags = (KEYRING_SEARCH_NO_STATE_CHECK |
KEYRING_SEARCH_LOOKUP_DIRECT),
.match_data.cmp = lookup_user_key_possessed,
.match_data.raw_data = key,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.flags = KEYRING_SEARCH_NO_STATE_CHECK,
};
key_ref = make_key_ref(key, 0);
......
......@@ -489,9 +489,10 @@ key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
/*
* See if the key we're looking at is the target key.
*/
int lookup_user_key_possessed(const struct key *key, const void *target)
bool lookup_user_key_possessed(const struct key *key,
const struct key_match_data *match_data)
{
return key == target;
return key == match_data->raw_data;
}
/*
......@@ -516,9 +517,9 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_perm_t perm)
{
struct keyring_search_context ctx = {
.match = lookup_user_key_possessed,
.flags = (KEYRING_SEARCH_NO_STATE_CHECK |
KEYRING_SEARCH_LOOKUP_DIRECT),
.match_data.cmp = lookup_user_key_possessed,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.flags = KEYRING_SEARCH_NO_STATE_CHECK,
};
struct request_key_auth *rka;
struct key *key;
......@@ -673,7 +674,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
ctx.index_key.type = key->type;
ctx.index_key.description = key->description;
ctx.index_key.desc_len = strlen(key->description);
ctx.match_data = key;
ctx.match_data.raw_data = key;
kdebug("check possessed");
skey_ref = search_process_keyrings(&ctx);
kdebug("possessed=%p", skey_ref);
......
......@@ -531,9 +531,9 @@ struct key *request_key_and_link(struct key_type *type,
.index_key.type = type,
.index_key.description = description,
.cred = current_cred(),
.match = type->match,
.match_data = description,
.flags = KEYRING_SEARCH_LOOKUP_DIRECT,
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
};
struct key *key;
key_ref_t key_ref;
......@@ -543,6 +543,14 @@ struct key *request_key_and_link(struct key_type *type,
ctx.index_key.type->name, ctx.index_key.description,
callout_info, callout_len, aux, dest_keyring, flags);
if (type->match_preparse) {
ret = type->match_preparse(&ctx.match_data);
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
}
/* search all the process keyrings for a key */
key_ref = search_process_keyrings(&ctx);
......@@ -555,7 +563,7 @@ struct key *request_key_and_link(struct key_type *type,
if (ret < 0) {
key_put(key);
key = ERR_PTR(ret);
goto error;
goto error_free;
}
}
} else if (PTR_ERR(key_ref) != -EAGAIN) {
......@@ -565,12 +573,15 @@ struct key *request_key_and_link(struct key_type *type,
* should consult userspace if we can */
key = ERR_PTR(-ENOKEY);
if (!callout_info)
goto error;
goto error_free;
key = construct_key_and_link(&ctx, callout_info, callout_len,
aux, dest_keyring, flags);
}
error_free:
if (type->match_free)
type->match_free(&ctx.match_data);
error:
kleave(" = %p", key);
return key;
......
......@@ -246,9 +246,9 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
.index_key.type = &key_type_request_key_auth,
.index_key.description = description,
.cred = current_cred(),
.match = user_match,
.match_data = description,
.flags = KEYRING_SEARCH_LOOKUP_DIRECT,
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
};
struct key *authkey;
key_ref_t authkey_ref;
......
......@@ -1096,7 +1096,6 @@ struct key_type key_type_trusted = {
.name = "trusted",
.instantiate = trusted_instantiate,
.update = trusted_update,
.match = user_match,
.destroy = trusted_destroy,
.describe = user_describe,
.read = trusted_read,
......
......@@ -26,12 +26,10 @@ static int logon_vet_description(const char *desc);
*/
struct key_type key_type_user = {
.name = "user",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.update = user_update,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
......@@ -48,12 +46,10 @@ EXPORT_SYMBOL_GPL(key_type_user);
*/
struct key_type key_type_logon = {
.name = "logon",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = user_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.update = user_update,
.match = user_match,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
......@@ -138,16 +134,6 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
EXPORT_SYMBOL_GPL(user_update);
/*
* match users on their name
*/
int user_match(const struct key *key, const void *description)
{
return strcmp(key->description, description) == 0;
}
EXPORT_SYMBOL_GPL(user_match);
/*
* dispose of the links from a revoked keyring
* - called with the key sem write-locked
......
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