On Tue, 2016-01-19 at 11:32 +0000, David Howells wrote:
> Move the X.509 trust validation code out to its own file so that it can be
> generalised.
> 
> Signed-off-by: David Howells <[email protected]>

Reviewed-by:  Mimi Zohar <[email protected]>

> ---
> 
>  crypto/asymmetric_keys/Makefile           |    2 
>  crypto/asymmetric_keys/public_key_trust.c |  195 
> +++++++++++++++++++++++++++++
>  crypto/asymmetric_keys/x509_parser.h      |    6 +
>  crypto/asymmetric_keys/x509_public_key.c  |  170 -------------------------
>  4 files changed, 202 insertions(+), 171 deletions(-)
>  create mode 100644 crypto/asymmetric_keys/public_key_trust.c
> 
> diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
> index cd1406f9b14a..3f291bbf7b74 100644
> --- a/crypto/asymmetric_keys/Makefile
> +++ b/crypto/asymmetric_keys/Makefile
> @@ -6,7 +6,7 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
> 
>  asymmetric_keys-y := asymmetric_type.o signature.o
> 
> -obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
> +obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o 
> public_key_trust.o
>  obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
> 
>  #
> diff --git a/crypto/asymmetric_keys/public_key_trust.c 
> b/crypto/asymmetric_keys/public_key_trust.c
> new file mode 100644
> index 000000000000..9febe612e659
> --- /dev/null
> +++ b/crypto/asymmetric_keys/public_key_trust.c
> @@ -0,0 +1,195 @@
> +/* Instantiate a public key crypto key from an X.509 Certificate
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells ([email protected])
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#define pr_fmt(fmt) "X.509: "fmt
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/mpi.h>
> +#include <linux/asn1_decoder.h>
> +#include <keys/asymmetric-subtype.h>
> +#include <keys/asymmetric-parser.h>
> +#include <keys/system_keyring.h>
> +#include <crypto/hash.h>
> +#include "asymmetric_keys.h"
> +#include "public_key.h"
> +#include "x509_parser.h"
> +
> +static bool use_builtin_keys;
> +static struct asymmetric_key_id *ca_keyid;
> +
> +#ifndef MODULE
> +static struct {
> +     struct asymmetric_key_id id;
> +     unsigned char data[10];
> +} cakey;
> +
> +static int __init ca_keys_setup(char *str)
> +{
> +     if (!str)               /* default system keyring */
> +             return 1;
> +
> +     if (strncmp(str, "id:", 3) == 0) {
> +             struct asymmetric_key_id *p = &cakey.id;
> +             size_t hexlen = (strlen(str) - 3) / 2;
> +             int ret;
> +
> +             if (hexlen == 0 || hexlen > sizeof(cakey.data)) {
> +                     pr_err("Missing or invalid ca_keys id\n");
> +                     return 1;
> +             }
> +
> +             ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen);
> +             if (ret < 0)
> +                     pr_err("Unparsable ca_keys id hex string\n");
> +             else
> +                     ca_keyid = p;   /* owner key 'id:xxxxxx' */
> +     } else if (strcmp(str, "builtin") == 0) {
> +             use_builtin_keys = true;
> +     }
> +
> +     return 1;
> +}
> +__setup("ca_keys=", ca_keys_setup);
> +#endif
> +
> +/**
> + * x509_request_asymmetric_key - Request a key by X.509 certificate params.
> + * @keyring: The keys to search.
> + * @id: The issuer & serialNumber to look for or NULL.
> + * @skid: The subjectKeyIdentifier to look for or NULL.
> + * @partial: Use partial match if true, exact if false.
> + *
> + * Find a key in the given keyring by identifier.  The preferred identifier 
> is
> + * the issuer + serialNumber and the fallback identifier is the
> + * subjectKeyIdentifier.  If both are given, the lookup is by the former, but
> + * the latter must also match.
> + */
> +struct key *x509_request_asymmetric_key(struct key *keyring,
> +                                     const struct asymmetric_key_id *id,
> +                                     const struct asymmetric_key_id *skid,
> +                                     bool partial)
> +{
> +     struct key *key;
> +     key_ref_t ref;
> +     const char *lookup;
> +     char *req, *p;
> +     int len;
> +
> +     if (id) {
> +             lookup = id->data;
> +             len = id->len;
> +     } else {
> +             lookup = skid->data;
> +             len = skid->len;
> +     }
> +
> +     /* Construct an identifier "id:<keyid>". */
> +     p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
> +     if (!req)
> +             return ERR_PTR(-ENOMEM);
> +
> +     if (partial) {
> +             *p++ = 'i';
> +             *p++ = 'd';
> +     } else {
> +             *p++ = 'e';
> +             *p++ = 'x';
> +     }
> +     *p++ = ':';
> +     p = bin2hex(p, lookup, len);
> +     *p = 0;
> +
> +     pr_debug("Look up: \"%s\"\n", req);
> +
> +     ref = keyring_search(make_key_ref(keyring, 1),
> +                          &key_type_asymmetric, req);
> +     if (IS_ERR(ref))
> +             pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
> +     kfree(req);
> +
> +     if (IS_ERR(ref)) {
> +             switch (PTR_ERR(ref)) {
> +                     /* Hide some search errors */
> +             case -EACCES:
> +             case -ENOTDIR:
> +             case -EAGAIN:
> +                     return ERR_PTR(-ENOKEY);
> +             default:
> +                     return ERR_CAST(ref);
> +             }
> +     }
> +
> +     key = key_ref_to_ptr(ref);
> +     if (id && skid) {
> +             const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
> +             if (!kids->id[1]) {
> +                     pr_debug("issuer+serial match, but expected SKID 
> missing\n");
> +                     goto reject;
> +             }
> +             if (!asymmetric_key_id_same(skid, kids->id[1])) {
> +                     pr_debug("issuer+serial match, but SKID does not\n");
> +                     goto reject;
> +             }
> +     }
> +
> +     pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
> +     return key;
> +
> +reject:
> +     key_put(key);
> +     return ERR_PTR(-EKEYREJECTED);
> +}
> +EXPORT_SYMBOL_GPL(x509_request_asymmetric_key);
> +
> +/*
> + * Check the new certificate against the ones in the trust keyring.  If one 
> of
> + * those is the signing key and validates the new certificate, then mark the
> + * new certificate as being trusted.
> + *
> + * Return 0 if the new certificate was successfully validated, 1 if we 
> couldn't
> + * find a matching parent certificate in the trusted list and an error if 
> there
> + * is a matching certificate but the signature check fails.
> + */
> +int x509_validate_trust(struct x509_certificate *cert,
> +                     struct key *trust_keyring)
> +{
> +     struct public_key_signature *sig = cert->sig;
> +     struct key *key;
> +     int ret = 1;
> +
> +     if (!sig->auth_ids[0] && !sig->auth_ids[1])
> +             return 1;
> +
> +     if (!trust_keyring)
> +             return -EOPNOTSUPP;
> +     if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
> +             return -EPERM;
> +     if (cert->unsupported_sig)
> +             return -ENOPKG;
> +
> +     key = x509_request_asymmetric_key(trust_keyring,
> +                                       sig->auth_ids[0], sig->auth_ids[1],
> +                                       false);
> +     if (IS_ERR(key))
> +             return PTR_ERR(key);
> +
> +     if (!use_builtin_keys ||
> +         test_bit(KEY_FLAG_BUILTIN, &key->flags)) {
> +             ret = public_key_verify_signature(
> +                     key->payload.data[asym_crypto], cert->sig);
> +             if (ret == -ENOPKG)
> +                     cert->unsupported_sig = true;
> +     }
> +     key_put(key);
> +     return ret;
> +}
> diff --git a/crypto/asymmetric_keys/x509_parser.h 
> b/crypto/asymmetric_keys/x509_parser.h
> index e373e7483812..0cf670b196c8 100644
> --- a/crypto/asymmetric_keys/x509_parser.h
> +++ b/crypto/asymmetric_keys/x509_parser.h
> @@ -59,3 +59,9 @@ extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
>   */
>  extern int x509_get_sig_params(struct x509_certificate *cert);
>  extern int x509_check_for_self_signed(struct x509_certificate *cert);
> +
> +/*
> + * public_key_trust.c
> + */
> +extern int x509_validate_trust(struct x509_certificate *cert,
> +                            struct key *trust_keyring);
> diff --git a/crypto/asymmetric_keys/x509_public_key.c 
> b/crypto/asymmetric_keys/x509_public_key.c
> index 00aef0d121b2..7397edb6cefb 100644
> --- a/crypto/asymmetric_keys/x509_public_key.c
> +++ b/crypto/asymmetric_keys/x509_public_key.c
> @@ -24,133 +24,6 @@
>  #include "public_key.h"
>  #include "x509_parser.h"
> 
> -static bool use_builtin_keys;
> -static struct asymmetric_key_id *ca_keyid;
> -
> -#ifndef MODULE
> -static struct {
> -     struct asymmetric_key_id id;
> -     unsigned char data[10];
> -} cakey;
> -
> -static int __init ca_keys_setup(char *str)
> -{
> -     if (!str)               /* default system keyring */
> -             return 1;
> -
> -     if (strncmp(str, "id:", 3) == 0) {
> -             struct asymmetric_key_id *p = &cakey.id;
> -             size_t hexlen = (strlen(str) - 3) / 2;
> -             int ret;
> -
> -             if (hexlen == 0 || hexlen > sizeof(cakey.data)) {
> -                     pr_err("Missing or invalid ca_keys id\n");
> -                     return 1;
> -             }
> -
> -             ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen);
> -             if (ret < 0)
> -                     pr_err("Unparsable ca_keys id hex string\n");
> -             else
> -                     ca_keyid = p;   /* owner key 'id:xxxxxx' */
> -     } else if (strcmp(str, "builtin") == 0) {
> -             use_builtin_keys = true;
> -     }
> -
> -     return 1;
> -}
> -__setup("ca_keys=", ca_keys_setup);
> -#endif
> -
> -/**
> - * x509_request_asymmetric_key - Request a key by X.509 certificate params.
> - * @keyring: The keys to search.
> - * @id: The issuer & serialNumber to look for or NULL.
> - * @skid: The subjectKeyIdentifier to look for or NULL.
> - * @partial: Use partial match if true, exact if false.
> - *
> - * Find a key in the given keyring by identifier.  The preferred identifier 
> is
> - * the issuer + serialNumber and the fallback identifier is the
> - * subjectKeyIdentifier.  If both are given, the lookup is by the former, but
> - * the latter must also match.
> - */
> -struct key *x509_request_asymmetric_key(struct key *keyring,
> -                                     const struct asymmetric_key_id *id,
> -                                     const struct asymmetric_key_id *skid,
> -                                     bool partial)
> -{
> -     struct key *key;
> -     key_ref_t ref;
> -     const char *lookup;
> -     char *req, *p;
> -     int len;
> -
> -     if (id) {
> -             lookup = id->data;
> -             len = id->len;
> -     } else {
> -             lookup = skid->data;
> -             len = skid->len;
> -     }
> -
> -     /* Construct an identifier "id:<keyid>". */
> -     p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
> -     if (!req)
> -             return ERR_PTR(-ENOMEM);
> -
> -     if (partial) {
> -             *p++ = 'i';
> -             *p++ = 'd';
> -     } else {
> -             *p++ = 'e';
> -             *p++ = 'x';
> -     }
> -     *p++ = ':';
> -     p = bin2hex(p, lookup, len);
> -     *p = 0;
> -
> -     pr_debug("Look up: \"%s\"\n", req);
> -
> -     ref = keyring_search(make_key_ref(keyring, 1),
> -                          &key_type_asymmetric, req);
> -     if (IS_ERR(ref))
> -             pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
> -     kfree(req);
> -
> -     if (IS_ERR(ref)) {
> -             switch (PTR_ERR(ref)) {
> -                     /* Hide some search errors */
> -             case -EACCES:
> -             case -ENOTDIR:
> -             case -EAGAIN:
> -                     return ERR_PTR(-ENOKEY);
> -             default:
> -                     return ERR_CAST(ref);
> -             }
> -     }
> -
> -     key = key_ref_to_ptr(ref);
> -     if (id && skid) {
> -             const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
> -             if (!kids->id[1]) {
> -                     pr_debug("issuer+serial match, but expected SKID 
> missing\n");
> -                     goto reject;
> -             }
> -             if (!asymmetric_key_id_same(skid, kids->id[1])) {
> -                     pr_debug("issuer+serial match, but SKID does not\n");
> -                     goto reject;
> -             }
> -     }
> -
> -     pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
> -     return key;
> -
> -reject:
> -     key_put(key);
> -     return ERR_PTR(-EKEYREJECTED);
> -}
> -EXPORT_SYMBOL_GPL(x509_request_asymmetric_key);
> -
>  /*
>   * Set up the signature parameters in an X.509 certificate.  This involves
>   * digesting the signed data and extracting the signature.
> @@ -294,49 +167,6 @@ not_self_signed:
>  }
> 
>  /*
> - * Check the new certificate against the ones in the trust keyring.  If one 
> of
> - * those is the signing key and validates the new certificate, then mark the
> - * new certificate as being trusted.
> - *
> - * Return 0 if the new certificate was successfully validated, 1 if we 
> couldn't
> - * find a matching parent certificate in the trusted list and an error if 
> there
> - * is a matching certificate but the signature check fails.
> - */
> -static int x509_validate_trust(struct x509_certificate *cert,
> -                            struct key *trust_keyring)
> -{
> -     struct public_key_signature *sig = cert->sig;
> -     struct key *key;
> -     int ret = 1;
> -
> -     if (!sig->auth_ids[0] && !sig->auth_ids[1])
> -             return 1;
> -
> -     if (!trust_keyring)
> -             return -EOPNOTSUPP;
> -     if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
> -             return -EPERM;
> -     if (cert->unsupported_sig)
> -             return -ENOPKG;
> -
> -     key = x509_request_asymmetric_key(trust_keyring,
> -                                       sig->auth_ids[0], sig->auth_ids[1],
> -                                       false);
> -     if (IS_ERR(key))
> -             return PTR_ERR(key);
> -
> -     if (!use_builtin_keys ||
> -         test_bit(KEY_FLAG_BUILTIN, &key->flags)) {
> -             ret = public_key_verify_signature(
> -                     key->payload.data[asym_crypto], cert->sig);
> -             if (ret == -ENOPKG)
> -                     cert->unsupported_sig = true;
> -     }
> -     key_put(key);
> -     return ret;
> -}
> -
> -/*
>   * Attempt to parse a data blob for a key as an X509 certificate.
>   */
>  static int x509_key_preparse(struct key_preparsed_payload *prep)


Reply via email to