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