Merge remote-tracking branch 'keys/keys-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 01:36:05 +0000 (11:36 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 01:36:05 +0000 (11:36 +1000)
33 files changed:
Documentation/crypto/asymmetric-keys.txt
Documentation/security/keys.txt
certs/Kconfig
certs/Makefile
certs/blacklist.c [new file with mode: 0644]
certs/blacklist.h [new file with mode: 0644]
certs/blacklist_hashes.c [new file with mode: 0644]
certs/blacklist_nohashes.c [new file with mode: 0644]
crypto/asymmetric_keys/Kconfig
crypto/asymmetric_keys/Makefile
crypto/asymmetric_keys/asymmetric_keys.h
crypto/asymmetric_keys/asymmetric_type.c
crypto/asymmetric_keys/pkcs7_parser.c
crypto/asymmetric_keys/pkcs7_parser.h
crypto/asymmetric_keys/pkcs7_verify.c
crypto/asymmetric_keys/pkcs8.asn1 [new file with mode: 0644]
crypto/asymmetric_keys/pkcs8_parser.c [new file with mode: 0644]
crypto/asymmetric_keys/public_key.c
crypto/asymmetric_keys/signature.c
crypto/asymmetric_keys/x509_cert_parser.c
crypto/asymmetric_keys/x509_parser.h
crypto/asymmetric_keys/x509_public_key.c
include/crypto/public_key.h
include/keys/asymmetric-subtype.h
include/keys/system_keyring.h
include/linux/key-type.h
include/linux/keyctl.h [new file with mode: 0644]
include/uapi/linux/keyctl.h
security/keys/Makefile
security/keys/compat.c
security/keys/internal.h
security/keys/keyctl.c
security/keys/keyctl_pkey.c [new file with mode: 0644]

index 2b7816dea370a2f4a30404f5bbc8542f1f231561..b33a13ce8c37d0210a112e8b272495540e16b252 100644 (file)
@@ -182,6 +182,10 @@ and looks like the following:
 
                void (*describe)(const struct key *key, struct seq_file *m);
                void (*destroy)(void *payload);
+               int (*query)(const struct kernel_pkey_params *params,
+                            struct kernel_pkey_query *info);
+               int (*eds_op)(struct kernel_pkey_params *params,
+                             const void *in, void *out);
                int (*verify_signature)(const struct key *key,
                                        const struct public_key_signature *sig);
        };
@@ -206,12 +210,22 @@ There are a number of operations defined by the subtype:
      asymmetric key will look after freeing the fingerprint and releasing the
      reference on the subtype module.
 
- (3) verify_signature().
+ (3) query().
 
-     Optional.  These are the entry points for the key usage operations.
-     Currently there is only the one defined.  If not set, the caller will be
-     given -ENOTSUPP.  The subtype may do anything it likes to implement an
-     operation, including offloading to hardware.
+     Mandatory.  This is a function for querying the capabilities of a key.
+
+ (4) eds_op().
+
+     Optional.  This is the entry point for the encryption, decryption and
+     signature creation operations (which are distinguished by the operation ID
+     in the parameter struct).  The subtype may do anything it likes to
+     implement an operation, including offloading to hardware.
+
+ (5) verify_signature().
+
+     Optional.  This is the entry point for signature verification.  The
+     subtype may do anything it likes to implement an operation, including
+     offloading to hardware.
 
 
 ==========================
@@ -233,6 +247,8 @@ Examples of blob formats for which parsers could be implemented include:
  - X.509 ASN.1 stream.
  - Pointer to TPM key.
  - Pointer to UEFI key.
+ - PKCS#8 private key [RFC 5208].
+ - PKCS#5 encrypted private key [RFC 2898].
 
 During key instantiation each parser in the list is tried until one doesn't
 return -EBADMSG.
index 3849814bfe6dd22e501407b887e717fd4a8dc2fa..6844dcbba1ba48bc2156aba0262554a53f8dd894 100644 (file)
@@ -857,6 +857,117 @@ The keyctl syscall functions are:
      supported, error ENOKEY if the key could not be found, or error
      EACCES if the key is not readable by the caller.
 
+
+ (*) Query an asymmetric key.
+
+       long keyctl(KEYCTL_PKEY_QUERY,
+                   key_serial_t key_id, unsigned long reserved,
+                   struct keyctl_pkey_query *info);
+
+     Get information about an asymmetric key.  The information is returned in
+     the keyctl_pkey_query struct:
+
+       __u32   supported_ops;
+
+     A bit mask of flags indicating which ops are supported.  This is
+     constructed from a bitwise-OR of:
+
+       KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}
+
+       __u32   key_size;
+
+     The size in bits of the key.
+
+       __u16   max_data_size;
+       __u16   max_sig_size;
+       __u16   max_enc_size;
+       __u16   max_dec_size;
+
+     The maximum sizes in bytes of a blob of data to be signed, a signature
+     blob, a blob to be encrypted and a blob to be decrypted.
+
+     reserved must be set to 0.  This is intended for future use to hand over
+     one or more passphrases needed unlock a key.
+
+     If successful, 0 is returned.  If the key is not an asymmetric key,
+     EOPNOTSUPP is returned.
+
+
+ (*) Encrypt, decrypt, sign or verify a blob using an asymmetric key.
+
+       long keyctl(KEYCTL_PKEY_ENCRYPT,
+                   const struct keyctl_pkey_params *params,
+                   const char *info,
+                   const void *in,
+                   void *out);
+
+       long keyctl(KEYCTL_PKEY_DECRYPT,
+                   const struct keyctl_pkey_params *params,
+                   const char *info,
+                   const void *in,
+                   void *out);
+
+       long keyctl(KEYCTL_PKEY_SIGN,
+                   const struct keyctl_pkey_params *params,
+                   const char *info,
+                   const void *in,
+                   void *out);
+
+       long keyctl(KEYCTL_PKEY_VERIFY,
+                   const struct keyctl_pkey_params *params,
+                   const char *info,
+                   const void *in,
+                   const void *in2);
+
+     Use an asymmetric key to perform a public-key cryptographic operation a
+     blob of data.  For encryption and verification, the asymmetric key may
+     only need the public parts to be available, but for decryption and signing
+     the private parts are required also.
+
+     The parameter block pointed to by params contains a number of integer
+     values:
+
+       __s32           key_id;
+       __u32           in_len;
+       __u32           out_len;
+       __u32           in2_len;
+
+     key_id is the ID of the asymmetric key to be used.  in_len and in2_len
+     indicate the amount of data in the in and in2 buffers and out_len
+     indicates the size of the out buffer as appropriate for the above
+     operations.
+
+     For a given operation, the in and out buffers are used as follows:
+
+       Operation ID            in,in_len       out,out_len     in2,in2_len
+       ======================= =============== =============== ===============
+       KEYCTL_PKEY_ENCRYPT     Raw data        Encrypted data  -
+       KEYCTL_PKEY_DECRYPT     Encrypted data  Raw data        -
+       KEYCTL_PKEY_SIGN        Raw data        Signature       -
+       KEYCTL_PKEY_VERIFY      Raw data        -               Signature
+
+     info is a string of key=value pairs that supply supplementary information.
+     These include:
+
+       enc=<encoding>  The encoding of the encrypted/signature blob.  This can
+                       be "pkcs1" for RSASSA-PKCS1-v1.5 or RSAES-PKCS1-v1.5;
+                       "pss" for "RSASSA-PSS"; "oaep" for "RSAES-OAEP".  If
+                       omitted or is "raw", the raw output of the encryption
+                       function is specified.
+
+       hash=<algo>     If the data buffer contains the output of a hash
+                       function and the encoding includes some indication of
+                       which hash function was used, the hash function can be
+                       specified with this, eg. "hash=sha256".
+
+     The __spare space in the parameter block must be set to 0.  This is
+     intended, amongst other things, to allow the passing of passphrases
+     required to unlock a key.
+
+     If successful, encrypt, decrypt and sign all return the amount of data
+     written into the output buffer.  Verification returns 0 on success.
+
+
 ===============
 KERNEL SERVICES
 ===============
@@ -1432,6 +1543,112 @@ The structure has a number of fields, some of which are mandatory:
         The authorisation key.
 
 
+ (*) int (*asym_eds_op)(struct kernel_pkey_params *params,
+                       const void *in, void *out);
+     int (*asym_verify_signature)(struct kernel_pkey_params *params,
+                                 const void *in, const void *in2);
+
+     These methods are optional.  If provided the first allows a key to be
+     used to encrypt, decrypt or sign a blob of data, and the second allows a
+     key to verify a signature.
+
+     In all cases, the following information is provided in the params block:
+
+       struct kernel_pkey_params {
+               struct key      *key;
+               const char      *encoding;
+               const char      *hash_algo;
+               char            *info;
+               __u32           in_len;
+               union {
+                       __u32   out_len;
+                       __u32   in2_len;
+               };
+               enum kernel_pkey_operation op : 8;
+       };
+
+     This includes the key to be used; an optional string indicating the
+     encoding to use (for instance, "pkcs1" may be used with an RSA key to
+     indicate RSASSA-PKCS1-v1.5 or RSAES-PKCS1-v1.5 encoding); the name of the
+     hash algorithm used to generate the data for a signature (if appropriate);
+     the sizes of the input and output (or second input) buffers; and the ID of
+     the operation to be performed.
+
+     For a given operation ID, the input and output buffers are used as
+     follows:
+
+       Operation ID            in,in_len       out,out_len     in2,in2_len
+       ======================= =============== =============== ===============
+       kernel_pkey_encrypt     Raw data        Encrypted data  -
+       kernel_pkey_decrypt     Encrypted data  Raw data        -
+       kernel_pkey_sign        Raw data        Signature       -
+       kernel_pkey_verify      Raw data        -               Signature
+
+     asym_eds_op() deals with encryption, decryption and signature creation as
+     specified by params->op.  Note that params->op is also set for
+     asym_verify_signature().
+
+     Encrypting and signature creation both take raw data in the input buffer
+     and return the encrypted result in the output buffer.  Padding may have
+     been added if an encoding was set.  In the case of signature creation,
+     depending on the encoding, the padding created may need to indicate the
+     digest algorithm - the name of which should be supplied in hash_algo.
+
+     Decryption takes encrypted data in the input buffer and returns the raw
+     data in the output buffer.  Padding will get checked and stripped off if
+     an encoding was set.
+
+     Verification takes raw data in the input buffer and the signature in the
+     second input buffer and checks that the one matches the other.  Padding
+     will be validated.  Depending on the encoding, the digest algorithm used
+     to generate the raw data may need to be indicated in hash_algo.
+
+     If successful, asym_eds_op() should return the number of bytes written
+     into the output buffer.  asym_verify_signature() should return 0.
+
+     A variety of errors may be returned, including EOPNOTSUPP if the operation
+     is not supported; EKEYREJECTED if verification fails; ENOPKG if the
+     required crypto isn't available.
+
+
+ (*) int (*asym_query)(const struct kernel_pkey_params *params,
+                      struct kernel_pkey_query *info);
+
+     This method is optional.  If provided it allows information about the
+     public or asymmetric key held in the key to be determined.
+
+     The parameter block is as for asym_eds_op() and co. but in_len and out_len
+     are unused.  The encoding and hash_algo fields should be used to reduce
+     the returned buffer/data sizes as appropriate.
+
+     If successful, the following information is filled in:
+
+       struct kernel_pkey_query {
+               __u32           supported_ops;
+               __u32           key_size;
+               __u16           max_data_size;
+               __u16           max_sig_size;
+               __u16           max_enc_size;
+               __u16           max_dec_size;
+       };
+
+     The supported_ops field will contain a bitmask indicating what operations
+     are supported by the key, including encryption of a blob, decryption of a
+     blob, signing a blob and verifying the signature on a blob.  The following
+     constants are defined for this:
+
+       KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}
+
+     The key_size field is the size of the key in bits.  max_data_size and
+     max_sig_size are the maximum raw data and signature sizes for creation and
+     verification of a signature; max_enc_size and max_dec_size are the maximum
+     raw data and signature sizes for encryption and decryption.  The
+     max_*_size fields are measured in bytes.
+
+     If successful, 0 will be returned.  If the key doesn't support this,
+     EOPNOTSUPP will be returned.
+
+
 ============================
 REQUEST-KEY CALLBACK SERVICE
 ============================
index fc5955f5fc8a92c0fb20dcf528b000e0d6ed0ed7..6ce51ede9e9b4617f62a5e2f3cc3aeda75f9a77f 100644 (file)
@@ -64,4 +64,22 @@ config SECONDARY_TRUSTED_KEYRING
          those keys are not blacklisted and are vouched for by a key built
          into the kernel or already in the secondary trusted keyring.
 
+config SYSTEM_BLACKLIST_KEYRING
+       bool "Provide system-wide ring of blacklisted keys"
+       depends on KEYS
+       help
+         Provide a system keyring to which blacklisted keys can be added.
+         Keys in the keyring are considered entirely untrusted.  Keys in this
+         keyring are used by the module signature checking to reject loading
+         of modules signed with a blacklisted key.
+
+config SYSTEM_BLACKLIST_HASH_LIST
+       string "Hashes to be preloaded into the system blacklist keyring"
+       depends on SYSTEM_BLACKLIST_KEYRING
+       help
+         If set, this option should be the filename of a list of hashes in the
+         form "<hash>", "<hash>", ... .  This will be included into a C
+         wrapper to incorporate the list into the kernel.  Each <hash> should
+         be a string of hex digits.
+
 endmenu
index 2773c4afa24c0af0d8bfd73ab05a01606c292a7d..4119bb376ea11d5fd7d648dd3d56b2a6d295c23f 100644 (file)
@@ -3,6 +3,12 @@
 #
 
 obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
+obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o
+ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"")
+obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
+else
+obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
+endif
 
 ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
 
diff --git a/certs/blacklist.c b/certs/blacklist.c
new file mode 100644 (file)
index 0000000..3eddce0
--- /dev/null
@@ -0,0 +1,174 @@
+/* System hash blacklist.
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "blacklist: "fmt
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/key.h>
+#include <linux/key-type.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <keys/system_keyring.h>
+#include "blacklist.h"
+
+static struct key *blacklist_keyring;
+
+/*
+ * The description must be a type prefix, a colon and then an even number of
+ * hex digits.  The hash is kept in the description.
+ */
+static int blacklist_vet_description(const char *desc)
+{
+       int n = 0;
+
+       if (*desc == ':')
+               return -EINVAL;
+       for (; *desc; desc++)
+               if (*desc == ':')
+                       goto found_colon;
+       return -EINVAL;
+
+found_colon:
+       desc++;
+       for (; *desc; desc++) {
+               if (!isxdigit(*desc))
+                       return -EINVAL;
+               n++;
+       }
+
+       if (n == 0 || n & 1)
+               return -EINVAL;
+       return 0;
+}
+
+/*
+ * The hash to be blacklisted is expected to be in the description.  There will
+ * be no payload.
+ */
+static int blacklist_preparse(struct key_preparsed_payload *prep)
+{
+       if (prep->datalen > 0)
+               return -EINVAL;
+       return 0;
+}
+
+static void blacklist_free_preparse(struct key_preparsed_payload *prep)
+{
+}
+
+static void blacklist_describe(const struct key *key, struct seq_file *m)
+{
+       seq_puts(m, key->description);
+}
+
+static struct key_type key_type_blacklist = {
+       .name                   = "blacklist",
+       .vet_description        = blacklist_vet_description,
+       .preparse               = blacklist_preparse,
+       .free_preparse          = blacklist_free_preparse,
+       .instantiate            = generic_key_instantiate,
+       .describe               = blacklist_describe,
+};
+
+/**
+ * mark_hash_blacklisted - Add a hash to the system blacklist
+ * @hash - The hash as a hex string with a type prefix (eg. "tbs:23aa429783")
+ */
+int mark_hash_blacklisted(const char *hash)
+{
+       key_ref_t key;
+
+       key = key_create_or_update(make_key_ref(blacklist_keyring, true),
+                                  "blacklist",
+                                  hash,
+                                  NULL,
+                                  0,
+                                  ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                                   KEY_USR_VIEW),
+                                  KEY_ALLOC_NOT_IN_QUOTA |
+                                  KEY_ALLOC_BUILT_IN);
+       if (IS_ERR(key)) {
+               pr_err("Problem blacklisting hash (%ld)\n", PTR_ERR(key));
+               return PTR_ERR(key);
+       }
+       return 0;
+}
+
+/**
+ * is_hash_blacklisted - Determine if a hash is blacklisted
+ * @hash: The hash to be checked as a binary blob
+ * @hash_len: The length of the binary hash
+ * @type: Type of hash
+ */
+int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
+{
+       key_ref_t kref;
+       size_t type_len = strlen(type);
+       char *buffer, *p;
+       int ret = 0;
+
+       buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+       p = memcpy(buffer, type, type_len);
+       p += type_len;
+       *p++ = ':';
+       bin2hex(p, hash, hash_len);
+       p += hash_len * 2;
+       *p = 0;
+
+       kref = keyring_search(make_key_ref(blacklist_keyring, true),
+                             &key_type_blacklist, buffer);
+       if (!IS_ERR(kref)) {
+               key_ref_put(kref);
+               ret = -EKEYREJECTED;
+       }
+
+       kfree(buffer);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(is_hash_blacklisted);
+
+/*
+ * Intialise the blacklist
+ */
+static int __init blacklist_init(void)
+{
+       const char *const *bl;
+
+       if (register_key_type(&key_type_blacklist) < 0)
+               panic("Can't allocate system blacklist key type\n");
+
+       blacklist_keyring =
+               keyring_alloc(".blacklist",
+                             KUIDT_INIT(0), KGIDT_INIT(0),
+                             current_cred(),
+                             (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                             KEY_USR_VIEW | KEY_USR_READ |
+                             KEY_USR_SEARCH,
+                             KEY_ALLOC_NOT_IN_QUOTA |
+                             KEY_FLAG_KEEP,
+                             NULL, NULL);
+       if (IS_ERR(blacklist_keyring))
+               panic("Can't allocate system blacklist keyring\n");
+
+       for (bl = blacklist_hashes; *bl; bl++)
+               if (mark_hash_blacklisted(*bl) < 0)
+                       pr_err("- blacklisting failed\n");
+       return 0;
+}
+
+/*
+ * Must be initialised before we try and load the keys into the keyring.
+ */
+device_initcall(blacklist_init);
diff --git a/certs/blacklist.h b/certs/blacklist.h
new file mode 100644 (file)
index 0000000..150d82d
--- /dev/null
@@ -0,0 +1,3 @@
+#include <linux/kernel.h>
+
+extern const char __initdata *const blacklist_hashes[];
diff --git a/certs/blacklist_hashes.c b/certs/blacklist_hashes.c
new file mode 100644 (file)
index 0000000..5bd449f
--- /dev/null
@@ -0,0 +1,6 @@
+#include "blacklist.h"
+
+const char __initdata *const blacklist_hashes[] = {
+#include CONFIG_SYSTEM_BLACKLIST_HASH_LIST
+       , NULL
+};
diff --git a/certs/blacklist_nohashes.c b/certs/blacklist_nohashes.c
new file mode 100644 (file)
index 0000000..851de10
--- /dev/null
@@ -0,0 +1,5 @@
+#include "blacklist.h"
+
+const char __initdata *const blacklist_hashes[] = {
+       NULL
+};
index 331f6baf2df8ea39148a49f5f7408238adc2fb0f..338fbed30a32f8a6583b8062af99afe98e1252b4 100644 (file)
@@ -30,6 +30,16 @@ config X509_CERTIFICATE_PARSER
          data and provides the ability to instantiate a crypto key from a
          public key packet found inside the certificate.
 
+config PKCS8_PRIVATE_KEY_PARSER
+       tristate "PKCS#8 private key parser"
+       depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+       select ASN1
+       select OID_REGISTRY
+       help
+         This option provides support for parsing PKCS#8 format blobs for
+         private key data and provides the ability to instantiate a crypto key
+         from that data.
+
 config PKCS7_MESSAGE_PARSER
        tristate "PKCS#7 message parser"
        depends on X509_CERTIFICATE_PARSER
index 6516855bec18e665cb8c0a0de89bfb4d86164fb0..ac877577d677ccbf84613649bf6bc76b656b644a 100644 (file)
@@ -31,6 +31,19 @@ $(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h
 clean-files    += x509-asn1.c x509-asn1.h
 clean-files    += x509_akid-asn1.c x509_akid-asn1.h
 
+#
+# PKCS#8 private key handling
+#
+obj-$(CONFIG_PKCS8_PRIVATE_KEY_PARSER) += pkcs8_key_parser.o
+pkcs8_key_parser-y := \
+       pkcs8-asn1.o \
+       pkcs8_parser.o
+
+$(obj)/pkcs8_parser.o: $(obj)/pkcs8-asn1.h
+$(obj)/pkcs8-asn1.o: $(obj)/pkcs8-asn1.c $(obj)/pkcs8-asn1.h
+
+clean-files    += pkcs8-asn1.c pkcs8-asn1.h
+
 #
 # PKCS#7 message handling
 #
index ca8e9ac34ce621613d29de02ba051eba593e7fc6..7be1ccf4fa9f2234c290e9bcffef773f176354aa 100644 (file)
@@ -16,3 +16,6 @@ extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
 extern int __asymmetric_key_hex_to_key_id(const char *id,
                                          struct asymmetric_key_id *match_id,
                                          size_t hexlen);
+
+extern int asymmetric_key_eds_op(struct kernel_pkey_params *params,
+                                const void *in, void *out);
index 6600181d5d01b72e7cfe04fc3fb4054a9a2a108a..77aa44abd7a6bad05866957f31a4d76b3054316e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/ctype.h>
+#include <keys/user-type.h>
 #include "asymmetric_keys.h"
 
 MODULE_LICENSE("GPL");
@@ -451,15 +452,57 @@ static void asymmetric_key_destroy(struct key *key)
        asymmetric_key_free_kids(kids);
 }
 
+int asymmetric_key_eds_op(struct kernel_pkey_params *params,
+                         const void *in, void *out)
+{
+       const struct asymmetric_key_subtype *subtype;
+       struct key *key = params->key;
+       int ret;
+
+       pr_devel("==>%s()\n", __func__);
+
+       if (key->type != &key_type_asymmetric)
+               return -EINVAL;
+       subtype = asymmetric_key_subtype(key);
+       if (!subtype ||
+           !key->payload.data[0])
+               return -EINVAL;
+       if (!subtype->eds_op)
+               return -ENOTSUPP;
+
+       ret = subtype->eds_op(params, in, out);
+
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
+                                          const void *in, const void *in2)
+{
+       struct public_key_signature sig = {
+               .s_size         = params->in2_len,
+               .digest_size    = params->in_len,
+               .encoding       = params->encoding,
+               .hash_algo      = params->hash_algo,
+               .digest         = (void *)in,
+               .s              = (void *)in2,
+       };
+
+       return verify_signature(params->key, &sig);
+}
+
 struct key_type key_type_asymmetric = {
-       .name           = "asymmetric",
-       .preparse       = asymmetric_key_preparse,
-       .free_preparse  = asymmetric_key_free_preparse,
-       .instantiate    = generic_key_instantiate,
-       .match_preparse = asymmetric_key_match_preparse,
-       .match_free     = asymmetric_key_match_free,
-       .destroy        = asymmetric_key_destroy,
-       .describe       = asymmetric_key_describe,
+       .name                   = "asymmetric",
+       .preparse               = asymmetric_key_preparse,
+       .free_preparse          = asymmetric_key_free_preparse,
+       .instantiate            = generic_key_instantiate,
+       .match_preparse         = asymmetric_key_match_preparse,
+       .match_free             = asymmetric_key_match_free,
+       .destroy                = asymmetric_key_destroy,
+       .describe               = asymmetric_key_describe,
+       .asym_query             = query_asymmetric_key,
+       .asym_eds_op            = asymmetric_key_eds_op,
+       .asym_verify_signature  = asymmetric_key_verify_signature,
 };
 EXPORT_SYMBOL_GPL(key_type_asymmetric);
 
index af4cd864911752478ba5f3c2732273f9624d434f..5f0c6755a55ba758c819cea205cc8c1c3bc199b0 100644 (file)
@@ -261,6 +261,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
        switch (ctx->last_oid) {
        case OID_rsaEncryption:
                ctx->sinfo->sig->pkey_algo = "rsa";
+               ctx->sinfo->sig->encoding = "pkcs1";
                break;
        default:
                printk("Unsupported pkey algo: %u\n", ctx->last_oid);
index f4e81074f5e098839f037ed78d29cc3c19ba99b1..ac341e19e5303665cfc46daf442f4d94b5615636 100644 (file)
@@ -23,6 +23,7 @@ struct pkcs7_signed_info {
        struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
        unsigned        index;
        bool            unsupported_crypto;     /* T if not usable due to missing crypto */
+       bool            blacklisted;
 
        /* Message digest - the digest of the Content Data (or NULL) */
        const void      *msgdigest;
index 2ffd69769466082eaf55cdfe71fb67704e0364af..2d93d9eccb4d0c46d7b93306f50f590709d1b21f 100644 (file)
@@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                         x509->subject,
                         x509->raw_serial_size, x509->raw_serial);
                x509->seen = true;
+
+               if (x509->blacklisted) {
+                       /* If this cert is blacklisted, then mark everything
+                        * that depends on this as blacklisted too.
+                        */
+                       sinfo->blacklisted = true;
+                       for (p = sinfo->signer; p != x509; p = p->signer)
+                               p->blacklisted = true;
+                       pr_debug("- blacklisted\n");
+                       return 0;
+               }
+
                if (x509->unsupported_key)
                        goto unsupported_crypto_in_x509;
 
@@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
  *
  *  (*) -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 no signature chains were found to be blacklisted or to contain
+ *     unsupported crypto, 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:
+ *  (*) -EKEYREJECTED if a blacklisted key was encountered, or:
+ *
+ *  (*) -ENOPKG if none of the signature chains are verifiable because suitable
+ *     crypto modules couldn't be found.
  */
 int pkcs7_verify(struct pkcs7_message *pkcs7,
                 enum key_being_used_for usage)
 {
        struct pkcs7_signed_info *sinfo;
-       int enopkg = -ENOPKG;
+       int actual_ret = -ENOPKG;
        int ret;
 
        kenter("");
@@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
 
        for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
                ret = pkcs7_verify_one(pkcs7, sinfo);
+               if (sinfo->blacklisted && actual_ret == -ENOPKG)
+                       actual_ret = -EKEYREJECTED;
                if (ret < 0) {
                        if (ret == -ENOPKG) {
                                sinfo->unsupported_crypto = true;
@@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
                        kleave(" = %d", ret);
                        return ret;
                }
-               enopkg = 0;
+               actual_ret = 0;
        }
 
-       kleave(" = %d", enopkg);
-       return enopkg;
+       kleave(" = %d", actual_ret);
+       return actual_ret;
 }
 EXPORT_SYMBOL_GPL(pkcs7_verify);
 
diff --git a/crypto/asymmetric_keys/pkcs8.asn1 b/crypto/asymmetric_keys/pkcs8.asn1
new file mode 100644 (file)
index 0000000..702c41a
--- /dev/null
@@ -0,0 +1,24 @@
+--
+-- This is the unencrypted variant
+--
+PrivateKeyInfo ::= SEQUENCE {
+       version                 Version,
+       privateKeyAlgorithm     PrivateKeyAlgorithmIdentifier,
+       privateKey              PrivateKey,
+       attributes              [0] IMPLICIT Attributes OPTIONAL
+}
+
+Version ::= INTEGER  ({ pkcs8_note_version })
+
+PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier ({ pkcs8_note_algo })
+
+PrivateKey ::= OCTET STRING ({ pkcs8_note_key })
+
+Attributes ::= SET OF Attribute
+
+Attribute ::= ANY
+
+AlgorithmIdentifier ::= SEQUENCE {
+       algorithm   OBJECT IDENTIFIER ({ pkcs8_note_OID }),
+       parameters  ANY OPTIONAL
+}
diff --git a/crypto/asymmetric_keys/pkcs8_parser.c b/crypto/asymmetric_keys/pkcs8_parser.c
new file mode 100644 (file)
index 0000000..26a73c0
--- /dev/null
@@ -0,0 +1,184 @@
+/* PKCS#8 Private Key parser [RFC 5208].
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKCS8: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/public_key.h>
+#include "pkcs8-asn1.h"
+
+struct pkcs8_parse_context {
+       struct public_key *pub;
+       unsigned long   data;                   /* Start of data */
+       enum OID        last_oid;               /* Last OID encountered */
+       enum OID        algo_oid;               /* Algorithm OID */
+       u32             key_size;
+       const void      *key;
+};
+
+/*
+ * Note an OID when we find one for later processing when we know how to
+ * interpret it.
+ */
+int pkcs8_note_OID(void *context, size_t hdrlen,
+                  unsigned char tag,
+                  const void *value, size_t vlen)
+{
+       struct pkcs8_parse_context *ctx = context;
+
+       ctx->last_oid = look_up_OID(value, vlen);
+       if (ctx->last_oid == OID__NR) {
+               char buffer[50];
+
+               sprint_oid(value, vlen, buffer, sizeof(buffer));
+               pr_info("Unknown OID: [%lu] %s\n",
+                       (unsigned long)value - ctx->data, buffer);
+       }
+       return 0;
+}
+
+/*
+ * Note the version number of the ASN.1 blob.
+ */
+int pkcs8_note_version(void *context, size_t hdrlen,
+                      unsigned char tag,
+                      const void *value, size_t vlen)
+{
+       if (vlen != 1 || ((const u8 *)value)[0] != 0) {
+               pr_warn("Unsupported PKCS#8 version\n");
+               return -EBADMSG;
+       }
+       return 0;
+}
+
+/*
+ * Note the public algorithm.
+ */
+int pkcs8_note_algo(void *context, size_t hdrlen,
+                   unsigned char tag,
+                   const void *value, size_t vlen)
+{
+       struct pkcs8_parse_context *ctx = context;
+
+       if (ctx->last_oid != OID_rsaEncryption)
+               return -ENOPKG;
+
+       ctx->pub->pkey_algo = "rsa";
+       return 0;
+}
+
+/*
+ * Note the key data of the ASN.1 blob.
+ */
+int pkcs8_note_key(void *context, size_t hdrlen,
+                  unsigned char tag,
+                  const void *value, size_t vlen)
+{
+       struct pkcs8_parse_context *ctx = context;
+
+       ctx->key = value;
+       ctx->key_size = vlen;
+       return 0;
+}
+
+/*
+ * Parse a PKCS#8 private key blob.
+ */
+static struct public_key *pkcs8_parse(const void *data, size_t datalen)
+{
+       struct pkcs8_parse_context ctx;
+       struct public_key *pub;
+       long ret;
+
+       memset(&ctx, 0, sizeof(ctx));
+
+       ret = -ENOMEM;
+       ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+       if (!ctx.pub)
+               goto error;
+
+       ctx.data = (unsigned long)data;
+
+       /* Attempt to decode the private key */
+       ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
+       if (ret < 0)
+               goto error_decode;
+
+       ret = -ENOMEM;
+       pub = ctx.pub;
+       pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
+       if (!pub->key)
+               goto error_decode;
+
+       pub->keylen = ctx.key_size;
+       pub->key_is_private = true;
+       return pub;
+
+error_decode:
+       kfree(ctx.pub);
+error:
+       return ERR_PTR(ret);
+}
+
+/*
+ * Attempt to parse a data blob for a key as a PKCS#8 private key.
+ */
+static int pkcs8_key_preparse(struct key_preparsed_payload *prep)
+{
+       struct public_key *pub;
+
+       pub = pkcs8_parse(prep->data, prep->datalen);
+       if (IS_ERR(pub))
+               return PTR_ERR(pub);
+
+       pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
+       pub->id_type = "PKCS8";
+
+       /* We're pinning the module by being linked against it */
+       __module_get(public_key_subtype.owner);
+       prep->payload.data[asym_subtype] = &public_key_subtype;
+       prep->payload.data[asym_key_ids] = NULL;
+       prep->payload.data[asym_crypto] = pub;
+       prep->payload.data[asym_auth] = NULL;
+       prep->quotalen = 100;
+       return 0;
+}
+
+static struct asymmetric_key_parser pkcs8_key_parser = {
+       .owner  = THIS_MODULE,
+       .name   = "pkcs8",
+       .parse  = pkcs8_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pkcs8_key_init(void)
+{
+       return register_asymmetric_key_parser(&pkcs8_key_parser);
+}
+
+static void __exit pkcs8_key_exit(void)
+{
+       unregister_asymmetric_key_parser(&pkcs8_key_parser);
+}
+
+module_init(pkcs8_key_init);
+module_exit(pkcs8_key_exit);
+
+MODULE_DESCRIPTION("PKCS#8 certificate parser");
+MODULE_LICENSE("GPL");
index fd76b5fc3b3abe1919f193342108d87e8f1c0a93..8be2586028b675bf7b64b78590be0f2db751ffe8 100644 (file)
@@ -57,12 +57,96 @@ static void public_key_destroy(void *payload0, void *payload3)
        public_key_signature_free(payload3);
 }
 
+/*
+ * Determine the crypto algorithm name.
+ */
+static
+int software_key_determine_akcipher(const char *encoding,
+                                   const char *hash_algo,
+                                   const struct public_key *pkey,
+                                   char alg_name[CRYPTO_MAX_ALG_NAME])
+{
+       int n;
+
+       if (strcmp(encoding, "pkcs1") == 0) {
+               /* The data wangled by the RSA algorithm is typically padded
+                * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
+                * sec 8.2].
+                */
+               if (!hash_algo)
+                       n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+                                    "pkcs1pad(%s)",
+                                    pkey->pkey_algo);
+               else
+                       n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+                                    "pkcs1pad(%s,%s)",
+                                    pkey->pkey_algo, hash_algo);
+               return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
+       }
+
+       if (strcmp(encoding, "raw") == 0) {
+               strcpy(alg_name, pkey->pkey_algo);
+               return 0;
+       }
+
+       return -ENOPKG;
+}
+
+/*
+ * Query information about a key.
+ */
+static int software_key_query(const struct kernel_pkey_params *params,
+                             struct kernel_pkey_query *info)
+{
+       struct crypto_akcipher *tfm;
+       struct public_key *pkey = params->key->payload.data[asym_crypto];
+       char alg_name[CRYPTO_MAX_ALG_NAME];
+       int ret, len;
+
+       ret = software_key_determine_akcipher(params->encoding,
+                                             params->hash_algo,
+                                             pkey, alg_name);
+       if (ret < 0)
+               return ret;
+
+       tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+
+       if (pkey->key_is_private)
+               ret = crypto_akcipher_set_priv_key(tfm,
+                                                  pkey->key, pkey->keylen);
+       else
+               ret = crypto_akcipher_set_pub_key(tfm,
+                                                 pkey->key, pkey->keylen);
+       if (ret < 0)
+               goto error_free_tfm;
+
+       len = crypto_akcipher_maxsize(tfm);
+       info->key_size = len * 8;
+       info->max_data_size = len;
+       info->max_sig_size = len;
+       info->max_enc_size = len;
+       info->max_dec_size = len;
+       info->supported_ops = (KEYCTL_SUPPORTS_ENCRYPT |
+                              KEYCTL_SUPPORTS_VERIFY);
+       if (pkey->key_is_private)
+               info->supported_ops |= (KEYCTL_SUPPORTS_DECRYPT |
+                                       KEYCTL_SUPPORTS_SIGN);
+       ret = 0;
+
+error_free_tfm:
+       crypto_free_akcipher(tfm);
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+}
+
 struct public_key_completion {
        struct completion completion;
        int err;
 };
 
-static void public_key_verify_done(struct crypto_async_request *req, int err)
+static void public_key_crypto_done(struct crypto_async_request *req, int err)
 {
        struct public_key_completion *compl = req->data;
 
@@ -73,6 +157,84 @@ static void public_key_verify_done(struct crypto_async_request *req, int err)
        complete(&compl->completion);
 }
 
+/*
+ * Do encryption, decryption and signing ops.
+ */
+static int software_key_eds_op(struct kernel_pkey_params *params,
+                              const void *in, void *out)
+{
+       struct public_key_completion compl;
+       const struct public_key *pkey = params->key->payload.data[asym_crypto];
+       struct akcipher_request *req;
+       struct crypto_akcipher *tfm;
+       struct scatterlist in_sg, out_sg;
+       char alg_name[CRYPTO_MAX_ALG_NAME];
+       int ret;
+
+       pr_devel("==>%s()\n", __func__);
+
+       ret = software_key_determine_akcipher(params->encoding,
+                                             params->hash_algo,
+                                             pkey, alg_name);
+       if (ret < 0)
+               return ret;
+
+       tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+
+       req = akcipher_request_alloc(tfm, GFP_KERNEL);
+       if (!req)
+               goto error_free_tfm;
+
+       if (pkey->key_is_private)
+               ret = crypto_akcipher_set_priv_key(tfm,
+                                                  pkey->key, pkey->keylen);
+       else
+               ret = crypto_akcipher_set_pub_key(tfm,
+                                                 pkey->key, pkey->keylen);
+       if (ret)
+               goto error_free_req;
+
+       sg_init_one(&in_sg, in, params->in_len);
+       sg_init_one(&out_sg, out, params->out_len);
+       akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
+                                  params->out_len);
+       init_completion(&compl.completion);
+       akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+                                     CRYPTO_TFM_REQ_MAY_SLEEP,
+                                     public_key_crypto_done, &compl);
+
+       /* Perform the encryption calculation. */
+       switch (params->op) {
+       case kernel_pkey_encrypt:
+               ret = crypto_akcipher_encrypt(req);
+               break;
+       case kernel_pkey_decrypt:
+               ret = crypto_akcipher_decrypt(req);
+               break;
+       case kernel_pkey_sign:
+               ret = crypto_akcipher_sign(req);
+               break;
+       default:
+               BUG();
+       }
+       if (ret == -EINPROGRESS) {
+               wait_for_completion(&compl.completion);
+               ret = compl.err;
+       }
+
+       if (ret == 0)
+               ret = req->dst_len;
+
+error_free_req:
+       akcipher_request_free(req);
+error_free_tfm:
+       crypto_free_akcipher(tfm);
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+}
+
 /*
  * Verify a signature using a public key.
  */
@@ -83,8 +245,7 @@ int public_key_verify_signature(const struct public_key *pkey,
        struct crypto_akcipher *tfm;
        struct akcipher_request *req;
        struct scatterlist sig_sg, digest_sg;
-       const char *alg_name;
-       char alg_name_buf[CRYPTO_MAX_ALG_NAME];
+       char alg_name[CRYPTO_MAX_ALG_NAME];
        void *output;
        unsigned int outlen;
        int ret = -ENOMEM;
@@ -96,18 +257,11 @@ int public_key_verify_signature(const struct public_key *pkey,
        BUG_ON(!sig->digest);
        BUG_ON(!sig->s);
 
-       alg_name = sig->pkey_algo;
-       if (strcmp(sig->pkey_algo, "rsa") == 0) {
-               /* The data wangled by the RSA algorithm is typically padded
-                * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
-                * sec 8.2].
-                */
-               if (snprintf(alg_name_buf, CRYPTO_MAX_ALG_NAME,
-                            "pkcs1pad(rsa,%s)", sig->hash_algo
-                            ) >= CRYPTO_MAX_ALG_NAME)
-                       return -EINVAL;
-               alg_name = alg_name_buf;
-       }
+       ret = software_key_determine_akcipher(sig->encoding,
+                                             sig->hash_algo,
+                                             pkey, alg_name);
+       if (ret < 0)
+               return ret;
 
        tfm = crypto_alloc_akcipher(alg_name, 0, 0);
        if (IS_ERR(tfm))
@@ -117,7 +271,12 @@ int public_key_verify_signature(const struct public_key *pkey,
        if (!req)
                goto error_free_tfm;
 
-       ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
+       if (pkey->key_is_private)
+               ret = crypto_akcipher_set_priv_key(tfm,
+                                                  pkey->key, pkey->keylen);
+       else
+               ret = crypto_akcipher_set_pub_key(tfm,
+                                                 pkey->key, pkey->keylen);
        if (ret)
                goto error_free_req;
 
@@ -133,7 +292,7 @@ int public_key_verify_signature(const struct public_key *pkey,
        init_completion(&compl.completion);
        akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
                                      CRYPTO_TFM_REQ_MAY_SLEEP,
-                                     public_key_verify_done, &compl);
+                                     public_key_crypto_done, &compl);
 
        /* Perform the verification calculation.  This doesn't actually do the
         * verification, but rather calculates the hash expected by the
@@ -179,6 +338,8 @@ struct asymmetric_key_subtype public_key_subtype = {
        .name_len               = sizeof("public_key") - 1,
        .describe               = public_key_describe,
        .destroy                = public_key_destroy,
+       .query                  = software_key_query,
+       .eds_op                 = software_key_eds_op,
        .verify_signature       = public_key_verify_signature_2,
 };
 EXPORT_SYMBOL_GPL(public_key_subtype);
index 11b7ba1709041864868a6ca2b05680a20b5fb3f9..78545b446400042f7aa117cd1c251c0c2717c574 100644 (file)
@@ -16,7 +16,9 @@
 #include <linux/export.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/keyctl.h>
 #include <crypto/public_key.h>
+#include <keys/user-type.h>
 #include "asymmetric_keys.h"
 
 /*
@@ -36,6 +38,99 @@ void public_key_signature_free(struct public_key_signature *sig)
 }
 EXPORT_SYMBOL_GPL(public_key_signature_free);
 
+/**
+ * query_asymmetric_key - Get information about an aymmetric key.
+ * @params: Various parameters.
+ * @info: Where to put the information.
+ */
+int query_asymmetric_key(const struct kernel_pkey_params *params,
+                        struct kernel_pkey_query *info)
+{
+       const struct asymmetric_key_subtype *subtype;
+       struct key *key = params->key;
+       int ret;
+
+       pr_devel("==>%s()\n", __func__);
+
+       if (key->type != &key_type_asymmetric)
+               return -EINVAL;
+       subtype = asymmetric_key_subtype(key);
+       if (!subtype ||
+           !key->payload.data[0])
+               return -EINVAL;
+       if (!subtype->query)
+               return -ENOTSUPP;
+
+       ret = subtype->query(params, info);
+
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(query_asymmetric_key);
+
+/**
+ * encrypt_blob - Encrypt data using an asymmetric key
+ * @params: Various parameters
+ * @data: Data blob to be encrypted, length params->data_len
+ * @enc: Encrypted data buffer, length params->enc_len
+ *
+ * Encrypt the specified data blob using the private key specified by
+ * params->key.  The encrypted data is wrapped in an encoding if
+ * params->encoding is specified (eg. "pkcs1").
+ *
+ * Returns the length of the data placed in the encrypted data buffer or an
+ * error.
+ */
+int encrypt_blob(struct kernel_pkey_params *params,
+                const void *data, void *enc)
+{
+       params->op = kernel_pkey_encrypt;
+       return asymmetric_key_eds_op(params, data, enc);
+}
+EXPORT_SYMBOL_GPL(encrypt_blob);
+
+/**
+ * decrypt_blob - Decrypt data using an asymmetric key
+ * @params: Various parameters
+ * @enc: Encrypted data to be decrypted, length params->enc_len
+ * @data: Decrypted data buffer, length params->data_len
+ *
+ * Decrypt the specified data blob using the private key specified by
+ * params->key.  The decrypted data is wrapped in an encoding if
+ * params->encoding is specified (eg. "pkcs1").
+ *
+ * Returns the length of the data placed in the decrypted data buffer or an
+ * error.
+ */
+int decrypt_blob(struct kernel_pkey_params *params,
+                const void *enc, void *data)
+{
+       params->op = kernel_pkey_decrypt;
+       return asymmetric_key_eds_op(params, enc, data);
+}
+EXPORT_SYMBOL_GPL(decrypt_blob);
+
+/**
+ * create_signature - Sign some data using an asymmetric key
+ * @params: Various parameters
+ * @data: Data blob to be signed, length params->data_len
+ * @enc: Signature buffer, length params->enc_len
+ *
+ * Sign the specified data blob using the private key specified by params->key.
+ * The signature is wrapped in an encoding if params->encoding is specified
+ * (eg. "pkcs1").  If the encoding needs to know the digest type, this can be
+ * passed through params->hash_algo (eg. "sha1").
+ *
+ * Returns the length of the data placed in the signature buffer or an error.
+ */
+int create_signature(struct kernel_pkey_params *params,
+                    const void *data, void *enc)
+{
+       params->op = kernel_pkey_sign;
+       return asymmetric_key_eds_op(params, data, enc);
+}
+EXPORT_SYMBOL_GPL(create_signature);
+
 /**
  * verify_signature - Initiate the use of an asymmetric key to verify a signature
  * @key: The asymmetric key to verify against
index 865f46ea724f285046542fab1639a4377d2a2aa1..1f1899d5ab4335e21ea228177f02d554f925d9b0 100644 (file)
@@ -199,35 +199,32 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
 
        case OID_md4WithRSAEncryption:
                ctx->cert->sig->hash_algo = "md4";
-               ctx->cert->sig->pkey_algo = "rsa";
-               break;
+               goto rsa_pkcs1;
 
        case OID_sha1WithRSAEncryption:
                ctx->cert->sig->hash_algo = "sha1";
-               ctx->cert->sig->pkey_algo = "rsa";
-               break;
+               goto rsa_pkcs1;
 
        case OID_sha256WithRSAEncryption:
                ctx->cert->sig->hash_algo = "sha256";
-               ctx->cert->sig->pkey_algo = "rsa";
-               break;
+               goto rsa_pkcs1;
 
        case OID_sha384WithRSAEncryption:
                ctx->cert->sig->hash_algo = "sha384";
-               ctx->cert->sig->pkey_algo = "rsa";
-               break;
+               goto rsa_pkcs1;
 
        case OID_sha512WithRSAEncryption:
                ctx->cert->sig->hash_algo = "sha512";
-               ctx->cert->sig->pkey_algo = "rsa";
-               break;
+               goto rsa_pkcs1;
 
        case OID_sha224WithRSAEncryption:
                ctx->cert->sig->hash_algo = "sha224";
-               ctx->cert->sig->pkey_algo = "rsa";
-               break;
+               goto rsa_pkcs1;
        }
 
+rsa_pkcs1:
+       ctx->cert->sig->pkey_algo = "rsa";
+       ctx->cert->sig->encoding = "pkcs1";
        ctx->algo_oid = ctx->last_oid;
        return 0;
 }
index 05eef1c68881b9214af04857be0803aaf44c02cc..e373e74838120400b11ceec39e614aea3f4a08cd 100644 (file)
@@ -42,6 +42,7 @@ struct x509_certificate {
        bool            self_signed;            /* T if self-signed (check unsupported_sig too) */
        bool            unsupported_key;        /* T if key uses unsupported crypto */
        bool            unsupported_sig;        /* T if signature uses unsupported crypto */
+       bool            blacklisted;
 };
 
 /*
index fb732296cd36437950e9228baaecce4373a329eb..eea71dc9686c29fd2c3fe07d2c9fc0b213bc99b7 100644 (file)
@@ -84,6 +84,16 @@ int x509_get_sig_params(struct x509_certificate *cert)
                goto error_2;
        might_sleep();
        ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
+       if (ret < 0)
+               goto error_2;
+
+       ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
+       if (ret == -EKEYREJECTED) {
+               pr_err("Cert %*phN is blacklisted\n",
+                      sig->digest_size, sig->digest);
+               cert->blacklisted = true;
+               ret = 0;
+       }
 
 error_2:
        kfree(desc);
@@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
                         cert->sig->pkey_algo, cert->sig->hash_algo);
        }
 
+       /* Don't permit addition of blacklisted keys */
+       ret = -EKEYREJECTED;
+       if (cert->blacklisted)
+               goto error_free_cert;
+
        /* Propose a description */
        sulen = strlen(cert->subject);
        if (cert->raw_skid) {
index 882ca0e1e7a5967e1dde952c8e5ecdf616b0f2fd..c5e569b2a73e1f379d72abe555d076d962a20cae 100644 (file)
@@ -14,6 +14,8 @@
 #ifndef _LINUX_PUBLIC_KEY_H
 #define _LINUX_PUBLIC_KEY_H
 
+#include <linux/keyctl.h>
+
 /*
  * Cryptographic data for the public-key subtype of the asymmetric key type.
  *
@@ -23,6 +25,7 @@
 struct public_key {
        void *key;
        u32 keylen;
+       bool key_is_private;
        const char *id_type;
        const char *pkey_algo;
 };
@@ -40,6 +43,7 @@ struct public_key_signature {
        u8 digest_size;         /* Number of bytes in digest */
        const char *pkey_algo;
        const char *hash_algo;
+       const char *encoding;
 };
 
 extern void public_key_signature_free(struct public_key_signature *sig);
@@ -54,8 +58,14 @@ extern int restrict_link_by_signature(struct key *trust_keyring,
                                      const struct key_type *type,
                                      const union key_payload *payload);
 
-extern int verify_signature(const struct key *key,
-                           const struct public_key_signature *sig);
+extern int query_asymmetric_key(const struct kernel_pkey_params *,
+                               struct kernel_pkey_query *);
+
+extern int encrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int decrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int create_signature(struct kernel_pkey_params *, const void *, void *);
+extern int verify_signature(const struct key *,
+                           const struct public_key_signature *);
 
 int public_key_verify_signature(const struct public_key *pkey,
                                const struct public_key_signature *sig);
index 2480469ce8fb34a15e724cbd78f9b29fc8defb37..bd12733058c67096b60b09b21ed852baaf522e06 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/seq_file.h>
 #include <keys/asymmetric-type.h>
 
+struct kernel_pkey_query;
+struct kernel_pkey_params;
 struct public_key_signature;
 
 /*
@@ -34,6 +36,13 @@ struct asymmetric_key_subtype {
        /* Destroy a key of this subtype */
        void (*destroy)(void *payload_crypto, void *payload_auth);
 
+       int (*query)(const struct kernel_pkey_params *params,
+                    struct kernel_pkey_query *info);
+
+       /* Encrypt/decrypt/sign data */
+       int (*eds_op)(struct kernel_pkey_params *params,
+                     const void *in, void *out);
+
        /* Verify the signature on a key of this subtype (optional) */
        int (*verify_signature)(const struct key *key,
                                const struct public_key_signature *sig);
index fbd4647767e9162f10c5741825796c5c2061cef0..0d8762622ab99dbe83dbeb87943712586ee52942 100644 (file)
@@ -33,6 +33,18 @@ extern int restrict_link_by_builtin_and_secondary_trusted(
 #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
 #endif
 
+#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
+extern int mark_hash_blacklisted(const char *hash);
+extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
+                              const char *type);
+#else
+static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
+                                     const char *type)
+{
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_IMA_BLACKLIST_KEYRING
 extern struct key *ima_blacklist_keyring;
 
index eaee981c55584bc0aa8ee72365449e376713a536..63b2a92c208500ba3a53136565b627b21a60e0ba 100644 (file)
@@ -17,6 +17,9 @@
 
 #ifdef CONFIG_KEYS
 
+struct kernel_pkey_query;
+struct kernel_pkey_params;
+
 /*
  * key under-construction record
  * - passed to the request_key actor if supplied
@@ -147,6 +150,14 @@ struct key_type {
         */
        request_key_actor_t request_key;
 
+       /* Asymmetric key accessor functions. */
+       int (*asym_query)(const struct kernel_pkey_params *params,
+                         struct kernel_pkey_query *info);
+       int (*asym_eds_op)(struct kernel_pkey_params *params,
+                          const void *in, void *out);
+       int (*asym_verify_signature)(struct kernel_pkey_params *params,
+                                    const void *in, const void *in2);
+
        /* internal fields */
        struct list_head        link;           /* link in types list */
        struct lock_class_key   lock_class;     /* key->sem lock class */
diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h
new file mode 100644 (file)
index 0000000..e89b4a4
--- /dev/null
@@ -0,0 +1,46 @@
+/* keyctl kernel bits
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef __LINUX_KEYCTL_H
+#define __LINUX_KEYCTL_H
+
+#include <uapi/linux/keyctl.h>
+
+struct kernel_pkey_query {
+       __u32           supported_ops;  /* Which ops are supported */
+       __u32           key_size;       /* Size of the key in bits */
+       __u16           max_data_size;  /* Maximum size of raw data to sign in bytes */
+       __u16           max_sig_size;   /* Maximum size of signature in bytes */
+       __u16           max_enc_size;   /* Maximum size of encrypted blob in bytes */
+       __u16           max_dec_size;   /* Maximum size of decrypted blob in bytes */
+};
+
+enum kernel_pkey_operation {
+       kernel_pkey_encrypt,
+       kernel_pkey_decrypt,
+       kernel_pkey_sign,
+       kernel_pkey_verify,
+};
+
+struct kernel_pkey_params {
+       struct key      *key;
+       const char      *encoding;      /* Encoding (eg. "oaep" or NULL for raw) */
+       const char      *hash_algo;     /* Digest algorithm used (eg. "sha1") or NULL if N/A */
+       char            *info;          /* Modified info string to be released later */
+       __u32           in_len;         /* Input data size */
+       union {
+               __u32   out_len;        /* Output buffer size (enc/dec/sign) */
+               __u32   in2_len;        /* 2nd input data size (verify) */
+       };
+       enum kernel_pkey_operation op : 8;
+};
+
+#endif /* __LINUX_KEYCTL_H */
index 86eddd6241f36eb21897d65c6bb51f15bbb4ec58..93ebd25b142781ab54bc2e53a3ee401a75a65112 100644 (file)
 #define KEYCTL_INVALIDATE              21      /* invalidate a key */
 #define KEYCTL_GET_PERSISTENT          22      /* get a user's persistent keyring */
 #define KEYCTL_DH_COMPUTE              23      /* Compute Diffie-Hellman values */
+#define KEYCTL_PKEY_QUERY              24      /* Query public key parameters */
+#define KEYCTL_PKEY_ENCRYPT            25      /* Encrypt a blob using a public key */
+#define KEYCTL_PKEY_DECRYPT            26      /* Decrypt a blob using a public key */
+#define KEYCTL_PKEY_SIGN               27      /* Create a public key signature */
+#define KEYCTL_PKEY_VERIFY             28      /* Verify a public key signature */
 
 /* keyctl structures */
 struct keyctl_dh_params {
@@ -68,4 +73,29 @@ struct keyctl_dh_params {
        __s32 base;
 };
 
+#define KEYCTL_SUPPORTS_ENCRYPT                0x01
+#define KEYCTL_SUPPORTS_DECRYPT                0x02
+#define KEYCTL_SUPPORTS_SIGN           0x04
+#define KEYCTL_SUPPORTS_VERIFY         0x08
+
+struct keyctl_pkey_query {
+       __u32           supported_ops;  /* Which ops are supported */
+       __u32           key_size;       /* Size of the key in bits */
+       __u16           max_data_size;  /* Maximum size of raw data to sign in bytes */
+       __u16           max_sig_size;   /* Maximum size of signature in bytes */
+       __u16           max_enc_size;   /* Maximum size of encrypted blob in bytes */
+       __u16           max_dec_size;   /* Maximum size of decrypted blob in bytes */
+       __u32           __spare[10];
+};
+
+struct keyctl_pkey_params {
+       __s32           key_id;         /* Serial no. of public key to use */
+       __u32           in_len;         /* Input data size */
+       union {
+               __u32           out_len;        /* Output buffer size (encrypt/decrypt/sign) */
+               __u32           in2_len;        /* 2nd input data size (verify) */
+       };
+       __u32           __spare[7];
+};
+
 #endif /*  _LINUX_KEYCTL_H */
index 1fd4a16e6dafb6779f770690319918ad4341b9c7..981be73e938f48964b345c411ab89ab8aa5f8fb9 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
 obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
 obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
 
 #
 # Key types
index 36c80bf5b89c6f28a7c6010aa6b72f373bf4d1e1..519010ae3f4fd0f0762536bd679bd45ac098c14e 100644 (file)
@@ -136,6 +136,24 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
                return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
                                         arg4, compat_ptr(arg5));
 
+       case KEYCTL_PKEY_QUERY:
+               if (arg3 != 0)
+                       return -EINVAL;
+               return keyctl_pkey_query(arg2,
+                                        compat_ptr(arg4),
+                                        compat_ptr(arg5));
+
+       case KEYCTL_PKEY_ENCRYPT:
+       case KEYCTL_PKEY_DECRYPT:
+       case KEYCTL_PKEY_SIGN:
+               return keyctl_pkey_e_d_s(option,
+                                        compat_ptr(arg2), compat_ptr(arg3),
+                                        compat_ptr(arg4), compat_ptr(arg5));
+
+       case KEYCTL_PKEY_VERIFY:
+               return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3),
+                                         compat_ptr(arg4), compat_ptr(arg5));
+
        default:
                return -EOPNOTSUPP;
        }
index a705a7d92ad7a95ca513ae2d3da8a18879196a2a..7c34fab5be7012eb94c9bc4aba36e269cae35cd4 100644 (file)
@@ -270,6 +270,45 @@ static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
 }
 #endif
 
+#ifdef CONFIG_ASYMMETRIC_KEY_TYPE
+extern long keyctl_pkey_query(key_serial_t,
+                             const char __user *,
+                             struct keyctl_pkey_query __user *);
+
+extern long keyctl_pkey_verify(const struct keyctl_pkey_params __user *,
+                              const char __user *,
+                              const void __user *, const void __user *);
+
+extern long keyctl_pkey_e_d_s(int,
+                             const struct keyctl_pkey_params __user *,
+                             const char __user *,
+                             const void __user *, void __user *);
+#else
+static inline long keyctl_pkey_query(key_serial_t id,
+                                    const char __user *_info,
+                                    struct keyctl_pkey_query __user *_res)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_verify(const struct keyctl_pkey_params __user *params,
+                                     const char __user *_info,
+                                     const void __user *_in,
+                                     const void __user *_in2)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_e_d_s(int op,
+                                    const struct keyctl_pkey_params __user *params,
+                                    const char __user *_info,
+                                    const void __user *_in,
+                                    void __user *_out)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
 /*
  * Debugging key validation
  */
index d580ad06b792ff539f124a620a4c8791740f22dd..1fb1ac4723415af9566ba60684a2edfb165141d5 100644 (file)
@@ -1691,6 +1691,30 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                         (char __user *) arg3, (size_t) arg4,
                                         (void __user *) arg5);
 
+       case KEYCTL_PKEY_QUERY:
+               if (arg3 != 0)
+                       return -EINVAL;
+               return keyctl_pkey_query((key_serial_t)arg2,
+                                        (const char __user *)arg4,
+                                        (struct keyctl_pkey_query *)arg5);
+
+       case KEYCTL_PKEY_ENCRYPT:
+       case KEYCTL_PKEY_DECRYPT:
+       case KEYCTL_PKEY_SIGN:
+               return keyctl_pkey_e_d_s(
+                       option,
+                       (const struct keyctl_pkey_params __user *)arg2,
+                       (const char __user *)arg3,
+                       (const void __user *)arg4,
+                       (void __user *)arg5);
+
+       case KEYCTL_PKEY_VERIFY:
+               return keyctl_pkey_verify(
+                       (const struct keyctl_pkey_params __user *)arg2,
+                       (const char __user *)arg3,
+                       (const void __user *)arg4,
+                       (const void __user *)arg5);
+
        default:
                return -EOPNOTSUPP;
        }
diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c
new file mode 100644 (file)
index 0000000..7839788
--- /dev/null
@@ -0,0 +1,323 @@
+/* Public-key operation keyctls
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/parser.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
+{
+       kfree(params->info);
+       key_put(params->key);
+}
+
+enum {
+       Opt_err = -1,
+       Opt_enc,                /* "enc=<encoding>" eg. "enc=oaep" */
+       Opt_hash,               /* "hash=<digest-name>" eg. "hash=sha1" */
+};
+
+static const match_table_t param_keys = {
+       { Opt_enc,      "enc=%s" },
+       { Opt_hash,     "hash=%s" },
+       { Opt_err,      NULL }
+};
+
+/*
+ * Parse the information string which consists of key=val pairs.
+ */
+static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
+{
+       unsigned long token_mask = 0;
+       substring_t args[MAX_OPT_ARGS];
+       char *c = params->info, *p, *q;
+       int token;
+
+       while ((p = strsep(&c, " \t"))) {
+               if (*p == '\0' || *p == ' ' || *p == '\t')
+                       continue;
+               token = match_token(p, param_keys, args);
+               if (__test_and_set_bit(token, &token_mask))
+                       return -EINVAL;
+               q = args[0].from;
+               if (!q[0])
+                       return -EINVAL;
+
+               switch (token) {
+               case Opt_enc:
+                       params->encoding = q;
+                       break;
+
+               case Opt_hash:
+                       params->hash_algo = q;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Interpret parameters.  Callers must always call the free function
+ * on params, even if an error is returned.
+ */
+static int keyctl_pkey_params_get(key_serial_t id,
+                                 const char __user *_info,
+                                 struct kernel_pkey_params *params)
+{
+       key_ref_t key_ref;
+       void *p;
+       int ret;
+
+       memset(params, 0, sizeof(*params));
+       params->encoding = "raw";
+
+       p = strndup_user(_info, PAGE_SIZE);
+       if (IS_ERR(p))
+               return PTR_ERR(p);
+       params->info = p;
+
+       ret = keyctl_pkey_params_parse(params);
+       if (ret < 0)
+               return ret;
+
+       key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
+       if (IS_ERR(key_ref))
+               return PTR_ERR(key_ref);
+       params->key = key_ref_to_ptr(key_ref);
+
+       if (!params->key->type->asym_query)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+/*
+ * Get parameters from userspace.  Callers must always call the free function
+ * on params, even if an error is returned.
+ */
+static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
+                                   const char __user *_info,
+                                   int op,
+                                   struct kernel_pkey_params *params)
+{
+       struct keyctl_pkey_params uparams;
+       struct kernel_pkey_query info;
+       int ret;
+
+       memset(params, 0, sizeof(*params));
+       params->encoding = "raw";
+
+       if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
+               return -EFAULT;
+
+       ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
+       if (ret < 0)
+               return ret;
+
+       ret = params->key->type->asym_query(params, &info);
+       if (ret < 0)
+               return ret;
+
+       switch (op) {
+       case KEYCTL_PKEY_ENCRYPT:
+       case KEYCTL_PKEY_DECRYPT:
+               if (uparams.in_len  > info.max_enc_size ||
+                   uparams.out_len > info.max_dec_size)
+                       return -EINVAL;
+               break;
+       case KEYCTL_PKEY_SIGN:
+       case KEYCTL_PKEY_VERIFY:
+               if (uparams.in_len  > info.max_sig_size ||
+                   uparams.out_len > info.max_data_size)
+                       return -EINVAL;
+               break;
+       default:
+               BUG();
+       }
+
+       params->in_len  = uparams.in_len;
+       params->out_len = uparams.out_len;
+       return 0;
+}
+
+/*
+ * Query information about an asymmetric key.
+ */
+long keyctl_pkey_query(key_serial_t id,
+                      const char __user *_info,
+                      struct keyctl_pkey_query __user *_res)
+{
+       struct kernel_pkey_params params;
+       struct kernel_pkey_query res;
+       long ret;
+
+       memset(&params, 0, sizeof(params));
+
+       ret = keyctl_pkey_params_get(id, _info, &params);
+       if (ret < 0)
+               goto error;
+
+       ret = params.key->type->asym_query(&params, &res);
+       if (ret < 0)
+               goto error;
+
+       ret = -EFAULT;
+       if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
+           clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
+               ret = 0;
+
+error:
+       keyctl_pkey_params_free(&params);
+       return ret;
+}
+
+/*
+ * Encrypt/decrypt/sign
+ *
+ * Encrypt data, decrypt data or sign data using a public key.
+ *
+ * _info is a string of supplementary information in key=val format.  For
+ * instance, it might contain:
+ *
+ *     "enc=pkcs1 hash=sha256"
+ *
+ * where enc= specifies the encoding and hash= selects the OID to go in that
+ * particular encoding if required.  If enc= isn't supplied, it's assumed that
+ * the caller is supplying raw values.
+ *
+ * If successful, the amount of data written into the output buffer is
+ * returned.
+ */
+long keyctl_pkey_e_d_s(int op,
+                      const struct keyctl_pkey_params __user *_params,
+                      const char __user *_info,
+                      const void __user *_in,
+                      void __user *_out)
+{
+       struct kernel_pkey_params params;
+       void *in, *out;
+       long ret;
+
+       ret = keyctl_pkey_params_get_2(_params, _info, op, &params);
+       if (ret < 0)
+               goto error_params;
+
+       ret = -EOPNOTSUPP;
+       if (!params.key->type->asym_eds_op)
+               goto error_params;
+
+       switch (op) {
+       case KEYCTL_PKEY_ENCRYPT:
+               params.op = kernel_pkey_encrypt;
+               break;
+       case KEYCTL_PKEY_DECRYPT:
+               params.op = kernel_pkey_decrypt;
+               break;
+       case KEYCTL_PKEY_SIGN:
+               params.op = kernel_pkey_sign;
+               break;
+       default:
+               BUG();
+       }
+
+       in = memdup_user(_in, params.in_len);
+       if (IS_ERR(in)) {
+               ret = PTR_ERR(in);
+               goto error_params;
+       }
+
+       ret = -ENOMEM;
+       out = kmalloc(params.out_len, GFP_KERNEL);
+       if (!out)
+               goto error_in;
+
+       ret = params.key->type->asym_eds_op(&params, in, out);
+       if (ret < 0)
+               goto error_out;
+
+       if (copy_to_user(_out, out, ret) != 0)
+               ret = -EFAULT;
+
+error_out:
+       kfree(out);
+error_in:
+       kfree(in);
+error_params:
+       keyctl_pkey_params_free(&params);
+       return ret;
+}
+
+/*
+ * Verify a signature.
+ *
+ * Verify a public key signature using the given key, or if not given, search
+ * for a matching key.
+ *
+ * _info is a string of supplementary information in key=val format.  For
+ * instance, it might contain:
+ *
+ *     "enc=pkcs1 hash=sha256"
+ *
+ * where enc= specifies the signature blob encoding and hash= selects the OID
+ * to go in that particular encoding.  If enc= isn't supplied, it's assumed
+ * that the caller is supplying raw values.
+ *
+ * If successful, 0 is returned.
+ */
+long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
+                       const char __user *_info,
+                       const void __user *_in,
+                       const void __user *_in2)
+{
+       struct kernel_pkey_params params;
+       void *in, *in2;
+       long ret;
+
+       ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
+                                      &params);
+       if (ret < 0)
+               goto error_params;
+
+       ret = -EOPNOTSUPP;
+       if (!params.key->type->asym_verify_signature)
+               goto error_params;
+
+       in = memdup_user(_in, params.in_len);
+       if (IS_ERR(in)) {
+               ret = PTR_ERR(in);
+               goto error_params;
+       }
+
+       in2 = memdup_user(_in2, params.in2_len);
+       if (IS_ERR(in2)) {
+               ret = PTR_ERR(in2);
+               goto error_in;
+       }
+
+       params.op = kernel_pkey_verify;
+       ret = params.key->type->asym_verify_signature(&params, in, in2);
+
+       kfree(in2);
+error_in:
+       kfree(in);
+error_params:
+       keyctl_pkey_params_free(&params);
+       return ret;
+}
This page took 0.067039 seconds and 5 git commands to generate.