Merge tag 'modsign-pkcs7-20150812-3' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
This commit is contained in:
commit
e4fc02f24c
|
@ -97,6 +97,7 @@ GTAGS
|
|||
# Leavings from module signing
|
||||
#
|
||||
extra_certificates
|
||||
signing_key.pem
|
||||
signing_key.priv
|
||||
signing_key.x509
|
||||
x509.genkey
|
||||
|
|
|
@ -174,6 +174,11 @@ The output directory is often set using "O=..." on the commandline.
|
|||
|
||||
The value can be overridden in which case the default value is ignored.
|
||||
|
||||
KBUILD_SIGN_PIN
|
||||
--------------------------------------------------
|
||||
This variable allows a passphrase or PIN to be passed to the sign-file
|
||||
utility when signing kernel modules, if the private key requires such.
|
||||
|
||||
KBUILD_MODPOST_WARN
|
||||
--------------------------------------------------
|
||||
KBUILD_MODPOST_WARN can be set to avoid errors in case of undefined
|
||||
|
|
|
@ -89,6 +89,32 @@ This has a number of options available:
|
|||
their signatures checked without causing a dependency loop.
|
||||
|
||||
|
||||
(4) "File name or PKCS#11 URI of module signing key" (CONFIG_MODULE_SIG_KEY)
|
||||
|
||||
Setting this option to something other than its default of
|
||||
"signing_key.pem" will disable the autogeneration of signing keys and
|
||||
allow the kernel modules to be signed with a key of your choosing.
|
||||
The string provided should identify a file containing both a private
|
||||
key and its corresponding X.509 certificate in PEM form, or — on
|
||||
systems where the OpenSSL ENGINE_pkcs11 is functional — a PKCS#11 URI
|
||||
as defined by RFC7512. In the latter case, the PKCS#11 URI should
|
||||
reference both a certificate and a private key.
|
||||
|
||||
If the PEM file containing the private key is encrypted, or if the
|
||||
PKCS#11 token requries a PIN, this can be provided at build time by
|
||||
means of the KBUILD_SIGN_PIN variable.
|
||||
|
||||
|
||||
(5) "Additional X.509 keys for default system keyring" (CONFIG_SYSTEM_TRUSTED_KEYS)
|
||||
|
||||
This option can be set to the filename of a PEM-encoded file containing
|
||||
additional certificates which will be included in the system keyring by
|
||||
default.
|
||||
|
||||
Note that enabling module signing adds a dependency on the OpenSSL devel
|
||||
packages to the kernel build processes for the tool that does the signing.
|
||||
|
||||
|
||||
=======================
|
||||
GENERATING SIGNING KEYS
|
||||
=======================
|
||||
|
@ -100,11 +126,11 @@ it can be deleted or stored securely. The public key gets built into the
|
|||
kernel so that it can be used to check the signatures as the modules are
|
||||
loaded.
|
||||
|
||||
Under normal conditions, the kernel build will automatically generate a new
|
||||
keypair using openssl if one does not exist in the files:
|
||||
Under normal conditions, when CONFIG_MODULE_SIG_KEY is unchanged from its
|
||||
default, the kernel build will automatically generate a new keypair using
|
||||
openssl if one does not exist in the file:
|
||||
|
||||
signing_key.priv
|
||||
signing_key.x509
|
||||
signing_key.pem
|
||||
|
||||
during the building of vmlinux (the public part of the key needs to be built
|
||||
into vmlinux) using parameters in the:
|
||||
|
@ -135,8 +161,12 @@ kernel sources tree and the openssl command. The following is an example to
|
|||
generate the public/private key files:
|
||||
|
||||
openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
|
||||
-config x509.genkey -outform DER -out signing_key.x509 \
|
||||
-keyout signing_key.priv
|
||||
-config x509.genkey -outform PEM -out kernel_key.pem \
|
||||
-keyout kernel_key.pem
|
||||
|
||||
The full pathname for the resulting kernel_key.pem file can then be specified
|
||||
in the CONFIG_MODULE_SIG_KEY option, and the certificate and key therein will
|
||||
be used instead of an autogenerated keypair.
|
||||
|
||||
|
||||
=========================
|
||||
|
@ -152,10 +182,9 @@ in a keyring called ".system_keyring" that can be seen by:
|
|||
302d2d52 I------ 1 perm 1f010000 0 0 asymmetri Fedora kernel signing key: d69a84e6bce3d216b979e9505b3e3ef9a7118079: X509.RSA a7118079 []
|
||||
...
|
||||
|
||||
Beyond the public key generated specifically for module signing, any file
|
||||
placed in the kernel source root directory or the kernel build root directory
|
||||
whose name is suffixed with ".x509" will be assumed to be an X.509 public key
|
||||
and will be added to the keyring.
|
||||
Beyond the public key generated specifically for module signing, additional
|
||||
trusted certificates can be provided in a PEM-encoded file referenced by the
|
||||
CONFIG_SYSTEM_TRUSTED_KEYS configuration option.
|
||||
|
||||
Further, the architecture code may take public keys from a hardware store and
|
||||
add those in also (e.g. from the UEFI key database).
|
||||
|
@ -181,7 +210,7 @@ To manually sign a module, use the scripts/sign-file tool available in
|
|||
the Linux kernel source tree. The script requires 4 arguments:
|
||||
|
||||
1. The hash algorithm (e.g., sha256)
|
||||
2. The private key filename
|
||||
2. The private key filename or PKCS#11 URI
|
||||
3. The public key filename
|
||||
4. The kernel module to be signed
|
||||
|
||||
|
@ -194,6 +223,9 @@ The hash algorithm used does not have to match the one configured, but if it
|
|||
doesn't, you should make sure that hash algorithm is either built into the
|
||||
kernel or can be loaded without requiring itself.
|
||||
|
||||
If the private key requires a passphrase or PIN, it can be provided in the
|
||||
$KBUILD_SIGN_PIN environment variable.
|
||||
|
||||
|
||||
============================
|
||||
SIGNED MODULES AND STRIPPING
|
||||
|
|
8
Makefile
8
Makefile
|
@ -870,10 +870,10 @@ INITRD_COMPRESS-$(CONFIG_RD_LZ4) := lz4
|
|||
# export INITRD_COMPRESS := $(INITRD_COMPRESS-y)
|
||||
|
||||
ifdef CONFIG_MODULE_SIG_ALL
|
||||
MODSECKEY = ./signing_key.priv
|
||||
MODSECKEY = $(CONFIG_MODULE_SIG_KEY)
|
||||
MODPUBKEY = ./signing_key.x509
|
||||
export MODPUBKEY
|
||||
mod_sign_cmd = perl $(srctree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
|
||||
mod_sign_cmd = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
|
||||
else
|
||||
mod_sign_cmd = true
|
||||
endif
|
||||
|
@ -1173,8 +1173,8 @@ MRPROPER_DIRS += include/config usr/include include/generated \
|
|||
arch/*/include/generated .tmp_objdiff
|
||||
MRPROPER_FILES += .config .config.old .version .old_version \
|
||||
Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
|
||||
signing_key.priv signing_key.x509 x509.genkey \
|
||||
extra_certificates signing_key.x509.keyid \
|
||||
signing_key.pem signing_key.priv signing_key.x509 \
|
||||
x509.genkey extra_certificates signing_key.x509.keyid \
|
||||
signing_key.x509.signer vmlinux-gdb.py
|
||||
|
||||
# clean - Delete most, but leave enough to build external modules
|
||||
|
|
|
@ -536,7 +536,9 @@ static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
|
|||
int ret;
|
||||
|
||||
ret = verify_pefile_signature(kernel, kernel_len,
|
||||
system_trusted_keyring, &trusted);
|
||||
system_trusted_keyring,
|
||||
VERIFYING_KEXEC_PE_SIGNATURE,
|
||||
&trusted);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!trusted)
|
||||
|
|
|
@ -15,15 +15,21 @@ obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
|
|||
obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
|
||||
x509_key_parser-y := \
|
||||
x509-asn1.o \
|
||||
x509_akid-asn1.o \
|
||||
x509_rsakey-asn1.o \
|
||||
x509_cert_parser.o \
|
||||
x509_public_key.o
|
||||
|
||||
$(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h
|
||||
$(obj)/x509_cert_parser.o: \
|
||||
$(obj)/x509-asn1.h \
|
||||
$(obj)/x509_akid-asn1.h \
|
||||
$(obj)/x509_rsakey-asn1.h
|
||||
$(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
|
||||
$(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h
|
||||
$(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
|
||||
|
||||
clean-files += x509-asn1.c x509-asn1.h
|
||||
clean-files += x509_akid-asn1.c x509_akid-asn1.h
|
||||
clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
|
||||
|
||||
#
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/asymmetric-parser.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -20,6 +21,16 @@
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
|
||||
[VERIFYING_MODULE_SIGNATURE] = "mod sig",
|
||||
[VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig",
|
||||
[VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig",
|
||||
[VERIFYING_KEY_SIGNATURE] = "key sig",
|
||||
[VERIFYING_KEY_SELF_SIGNATURE] = "key self sig",
|
||||
[VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_being_used_for);
|
||||
|
||||
static LIST_HEAD(asymmetric_key_parsers);
|
||||
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
PKCS7ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType,
|
||||
contentType ContentType ({ pkcs7_check_content_type }),
|
||||
content [0] EXPLICIT SignedData OPTIONAL
|
||||
}
|
||||
|
||||
ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
|
||||
|
||||
SignedData ::= SEQUENCE {
|
||||
version INTEGER,
|
||||
version INTEGER ({ pkcs7_note_signeddata_version }),
|
||||
digestAlgorithms DigestAlgorithmIdentifiers,
|
||||
contentInfo ContentInfo,
|
||||
contentInfo ContentInfo ({ pkcs7_note_content }),
|
||||
certificates CHOICE {
|
||||
certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
|
||||
certSequence [2] IMPLICIT Certificates
|
||||
|
@ -21,7 +21,7 @@ SignedData ::= SEQUENCE {
|
|||
}
|
||||
|
||||
ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType,
|
||||
contentType ContentType ({ pkcs7_note_OID }),
|
||||
content [0] EXPLICIT Data OPTIONAL
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,8 @@ SignerInfos ::= CHOICE {
|
|||
}
|
||||
|
||||
SignerInfo ::= SEQUENCE {
|
||||
version INTEGER,
|
||||
issuerAndSerialNumber IssuerAndSerialNumber,
|
||||
version INTEGER ({ pkcs7_note_signerinfo_version }),
|
||||
sid SignerIdentifier, -- CMS variant, not PKCS#7
|
||||
digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
|
||||
authenticatedAttributes CHOICE {
|
||||
aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
|
||||
|
@ -88,6 +88,12 @@ SignerInfo ::= SEQUENCE {
|
|||
} OPTIONAL
|
||||
} ({ pkcs7_note_signed_info })
|
||||
|
||||
SignerIdentifier ::= CHOICE {
|
||||
-- RFC5652 sec 5.3
|
||||
issuerAndSerialNumber IssuerAndSerialNumber,
|
||||
subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier
|
||||
}
|
||||
|
||||
IssuerAndSerialNumber ::= SEQUENCE {
|
||||
issuer Name ({ pkcs7_sig_note_issuer }),
|
||||
serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial })
|
||||
|
@ -95,6 +101,8 @@ IssuerAndSerialNumber ::= SEQUENCE {
|
|||
|
||||
CertificateSerialNumber ::= INTEGER
|
||||
|
||||
SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid })
|
||||
|
||||
SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
|
||||
|
||||
AuthenticatedAttribute ::= SEQUENCE {
|
||||
|
@ -103,7 +111,7 @@ AuthenticatedAttribute ::= SEQUENCE {
|
|||
}
|
||||
|
||||
UnauthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
type OBJECT IDENTIFIER,
|
||||
values SET OF ANY
|
||||
}
|
||||
|
||||
|
|
|
@ -14,16 +14,26 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("PKCS#7 testing key type");
|
||||
|
||||
static unsigned pkcs7_usage;
|
||||
module_param_named(usage, pkcs7_usage, uint, S_IWUSR | S_IRUGO);
|
||||
MODULE_PARM_DESC(pkcs7_usage,
|
||||
"Usage to specify when verifying the PKCS#7 message");
|
||||
|
||||
/*
|
||||
* Preparse a PKCS#7 wrapped and validated data blob.
|
||||
*/
|
||||
static int pkcs7_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
enum key_being_used_for usage = pkcs7_usage;
|
||||
struct pkcs7_message *pkcs7;
|
||||
const void *data, *saved_prep_data;
|
||||
size_t datalen, saved_prep_datalen;
|
||||
|
@ -32,6 +42,11 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)
|
|||
|
||||
kenter("");
|
||||
|
||||
if (usage >= NR__KEY_BEING_USED_FOR) {
|
||||
pr_err("Invalid usage type %d\n", usage);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
saved_prep_data = prep->data;
|
||||
saved_prep_datalen = prep->datalen;
|
||||
pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
|
||||
|
@ -40,7 +55,7 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)
|
|||
goto error;
|
||||
}
|
||||
|
||||
ret = pkcs7_verify(pkcs7);
|
||||
ret = pkcs7_verify(pkcs7, usage);
|
||||
if (ret < 0)
|
||||
goto error_free;
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@ struct pkcs7_parse_context {
|
|||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer;
|
||||
const void *raw_skid;
|
||||
unsigned raw_skid_size;
|
||||
bool expect_skid;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -78,6 +81,30 @@ void pkcs7_free_message(struct pkcs7_message *pkcs7)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_free_message);
|
||||
|
||||
/*
|
||||
* Check authenticatedAttributes are provided or not provided consistently.
|
||||
*/
|
||||
static int pkcs7_check_authattrs(struct pkcs7_message *msg)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
bool want;
|
||||
|
||||
sinfo = msg->signed_infos;
|
||||
if (sinfo->authattrs) {
|
||||
want = true;
|
||||
msg->have_authattrs = true;
|
||||
}
|
||||
|
||||
for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
|
||||
if (!!sinfo->authattrs != want)
|
||||
goto inconsistent;
|
||||
return 0;
|
||||
|
||||
inconsistent:
|
||||
pr_warn("Inconsistently supplied authAttrs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pkcs7_parse_message - Parse a PKCS#7 message
|
||||
* @data: The raw binary ASN.1 encoded message to be parsed
|
||||
|
@ -110,6 +137,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = pkcs7_check_authattrs(ctx->msg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
msg = ctx->msg;
|
||||
ctx->msg = NULL;
|
||||
|
||||
|
@ -225,6 +256,100 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only support signed data [RFC2315 sec 9].
|
||||
*/
|
||||
int pkcs7_check_content_type(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
if (ctx->last_oid != OID_signed_data) {
|
||||
pr_warn("Only support pkcs7_signedData type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the SignedData version
|
||||
*/
|
||||
int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
unsigned version;
|
||||
|
||||
if (vlen != 1)
|
||||
goto unsupported;
|
||||
|
||||
ctx->msg->version = version = *(const u8 *)value;
|
||||
switch (version) {
|
||||
case 1:
|
||||
/* PKCS#7 SignedData [RFC2315 sec 9.1]
|
||||
* CMS ver 1 SignedData [RFC5652 sec 5.1]
|
||||
*/
|
||||
break;
|
||||
case 3:
|
||||
/* CMS ver 3 SignedData [RFC2315 sec 5.1] */
|
||||
break;
|
||||
default:
|
||||
goto unsupported;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unsupported:
|
||||
pr_warn("Unsupported SignedData version\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the SignerInfo version
|
||||
*/
|
||||
int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
unsigned version;
|
||||
|
||||
if (vlen != 1)
|
||||
goto unsupported;
|
||||
|
||||
version = *(const u8 *)value;
|
||||
switch (version) {
|
||||
case 1:
|
||||
/* PKCS#7 SignerInfo [RFC2315 sec 9.2]
|
||||
* CMS ver 1 SignerInfo [RFC5652 sec 5.3]
|
||||
*/
|
||||
if (ctx->msg->version != 1)
|
||||
goto version_mismatch;
|
||||
ctx->expect_skid = false;
|
||||
break;
|
||||
case 3:
|
||||
/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
|
||||
if (ctx->msg->version == 1)
|
||||
goto version_mismatch;
|
||||
ctx->expect_skid = true;
|
||||
break;
|
||||
default:
|
||||
goto unsupported;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unsupported:
|
||||
pr_warn("Unsupported SignerInfo version\n");
|
||||
return -EINVAL;
|
||||
version_mismatch:
|
||||
pr_warn("SignedData-SignerInfo version mismatch\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a certificate and store it in the context.
|
||||
*/
|
||||
|
@ -283,6 +408,25 @@ int pkcs7_note_certificate_list(void *context, size_t hdrlen,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the content type.
|
||||
*/
|
||||
int pkcs7_note_content(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
if (ctx->last_oid != OID_data &&
|
||||
ctx->last_oid != OID_msIndirectData) {
|
||||
pr_warn("Unsupported data type %d\n", ctx->last_oid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->msg->data_type = ctx->last_oid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the data from the message and store that and its content type OID in
|
||||
* the context.
|
||||
|
@ -298,45 +442,119 @@ int pkcs7_note_data(void *context, size_t hdrlen,
|
|||
ctx->msg->data = value;
|
||||
ctx->msg->data_len = vlen;
|
||||
ctx->msg->data_hdrlen = hdrlen;
|
||||
ctx->msg->data_type = ctx->last_oid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse authenticated attributes
|
||||
* Parse authenticated attributes.
|
||||
*/
|
||||
int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct pkcs7_signed_info *sinfo = ctx->sinfo;
|
||||
enum OID content_type;
|
||||
|
||||
pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_contentType:
|
||||
if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
content_type = look_up_OID(value, vlen);
|
||||
if (content_type != ctx->msg->data_type) {
|
||||
pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
|
||||
ctx->msg->data_type, sinfo->index,
|
||||
content_type);
|
||||
return -EBADMSG;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case OID_signingTime:
|
||||
if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
/* Should we check that the signing time is consistent
|
||||
* with the signer's X.509 cert?
|
||||
*/
|
||||
return x509_decode_time(&sinfo->signing_time,
|
||||
hdrlen, tag, value, vlen);
|
||||
|
||||
case OID_messageDigest:
|
||||
if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
if (tag != ASN1_OTS)
|
||||
return -EBADMSG;
|
||||
ctx->sinfo->msgdigest = value;
|
||||
ctx->sinfo->msgdigest_len = vlen;
|
||||
sinfo->msgdigest = value;
|
||||
sinfo->msgdigest_len = vlen;
|
||||
return 0;
|
||||
|
||||
case OID_smimeCapabilites:
|
||||
if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
if (ctx->msg->data_type != OID_msIndirectData) {
|
||||
pr_warn("S/MIME Caps only allowed with Authenticode\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
|
||||
* char URLs and cont[1] 8-bit char URLs.
|
||||
*
|
||||
* Microsoft StatementType seems to contain a list of OIDs that
|
||||
* are also used as extendedKeyUsage types in X.509 certs.
|
||||
*/
|
||||
case OID_msSpOpusInfo:
|
||||
if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
goto authenticode_check;
|
||||
case OID_msStatementType:
|
||||
if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
|
||||
goto repeated;
|
||||
authenticode_check:
|
||||
if (ctx->msg->data_type != OID_msIndirectData) {
|
||||
pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
/* I'm not sure how to validate these */
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
repeated:
|
||||
/* We permit max one item per AuthenticatedAttribute and no repeats */
|
||||
pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the set of auth attributes for digestion purposes [RFC2315 9.3]
|
||||
* Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]
|
||||
*/
|
||||
int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct pkcs7_signed_info *sinfo = ctx->sinfo;
|
||||
|
||||
if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
|
||||
!test_bit(sinfo_has_message_digest, &sinfo->aa_set) ||
|
||||
(ctx->msg->data_type == OID_msIndirectData &&
|
||||
!test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))) {
|
||||
pr_warn("Missing required AuthAttr\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (ctx->msg->data_type != OID_msIndirectData &&
|
||||
test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
|
||||
pr_warn("Unexpected Authenticode AuthAttr\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
|
||||
ctx->sinfo->authattrs = value - (hdrlen - 1);
|
||||
ctx->sinfo->authattrs_len = vlen + (hdrlen - 1);
|
||||
sinfo->authattrs = value - (hdrlen - 1);
|
||||
sinfo->authattrs_len = vlen + (hdrlen - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -366,6 +584,22 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuing cert's subjectKeyIdentifier
|
||||
*/
|
||||
int pkcs7_sig_note_skid(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
|
||||
|
||||
ctx->raw_skid = value;
|
||||
ctx->raw_skid_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the signature data
|
||||
*/
|
||||
|
@ -398,14 +632,27 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
|
|||
struct pkcs7_signed_info *sinfo = ctx->sinfo;
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
|
||||
pr_warn("Authenticode requires AuthAttrs\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* 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 (!ctx->expect_skid) {
|
||||
kid = asymmetric_key_generate_id(ctx->raw_serial,
|
||||
ctx->raw_serial_size,
|
||||
ctx->raw_issuer,
|
||||
ctx->raw_issuer_size);
|
||||
} else {
|
||||
kid = asymmetric_key_generate_id(ctx->raw_skid,
|
||||
ctx->raw_skid_size,
|
||||
"", 0);
|
||||
}
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
|
||||
pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
|
||||
|
||||
sinfo->signing_cert_id = kid;
|
||||
sinfo->index = ++ctx->sinfo_index;
|
||||
*ctx->ppsinfo = sinfo;
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
struct pkcs7_signed_info {
|
||||
struct pkcs7_signed_info *next;
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool trusted;
|
||||
bool unsupported_crypto; /* T if not usable due to missing crypto */
|
||||
unsigned index;
|
||||
bool trusted;
|
||||
bool unsupported_crypto; /* T if not usable due to missing crypto */
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
const void *msgdigest;
|
||||
|
@ -32,8 +32,18 @@ struct pkcs7_signed_info {
|
|||
/* Authenticated Attribute data (or NULL) */
|
||||
unsigned authattrs_len;
|
||||
const void *authattrs;
|
||||
unsigned long aa_set;
|
||||
#define sinfo_has_content_type 0
|
||||
#define sinfo_has_signing_time 1
|
||||
#define sinfo_has_message_digest 2
|
||||
#define sinfo_has_smime_caps 3
|
||||
#define sinfo_has_ms_opus_info 4
|
||||
#define sinfo_has_ms_statement_type 5
|
||||
time64_t signing_time;
|
||||
|
||||
/* Issuing cert serial number and issuer's name */
|
||||
/* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1]
|
||||
* or issuing cert's SKID [CMS ver 3].
|
||||
*/
|
||||
struct asymmetric_key_id *signing_cert_id;
|
||||
|
||||
/* Message signature.
|
||||
|
@ -50,6 +60,8 @@ struct pkcs7_message {
|
|||
struct x509_certificate *certs; /* Certificate list */
|
||||
struct x509_certificate *crl; /* Revocation list */
|
||||
struct pkcs7_signed_info *signed_infos;
|
||||
u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */
|
||||
bool have_authattrs; /* T if have authattrs */
|
||||
|
||||
/* Content Data (or NULL) */
|
||||
enum OID data_type; /* Type of Data */
|
||||
|
|
|
@ -54,7 +54,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
|||
/* Look to see if this certificate is present in the trusted
|
||||
* keys.
|
||||
*/
|
||||
key = x509_request_asymmetric_key(trust_keyring, x509->id,
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
x509->id, x509->skid,
|
||||
false);
|
||||
if (!IS_ERR(key)) {
|
||||
/* One of the X.509 certificates in the PKCS#7 message
|
||||
|
@ -85,8 +86,10 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
|||
/* No match - see if the root certificate has a signer amongst the
|
||||
* trusted keys.
|
||||
*/
|
||||
if (last && last->authority) {
|
||||
key = x509_request_asymmetric_key(trust_keyring, last->authority,
|
||||
if (last && (last->akid_id || last->akid_skid)) {
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
last->akid_id,
|
||||
last->akid_skid,
|
||||
false);
|
||||
if (!IS_ERR(key)) {
|
||||
x509 = last;
|
||||
|
@ -103,6 +106,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
|||
*/
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
sinfo->signing_cert_id,
|
||||
NULL,
|
||||
false);
|
||||
if (!IS_ERR(key)) {
|
||||
pr_devel("sinfo %u: Direct signer is key %x\n",
|
||||
|
|
|
@ -70,9 +70,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
|||
* message digest attribute amongst them which corresponds to the
|
||||
* digest we just calculated.
|
||||
*/
|
||||
if (sinfo->msgdigest) {
|
||||
if (sinfo->authattrs) {
|
||||
u8 tag;
|
||||
|
||||
if (!sinfo->msgdigest) {
|
||||
pr_warn("Sig %u: No messageDigest\n", sinfo->index);
|
||||
ret = -EKEYREJECTED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
|
||||
pr_debug("Sig %u: Invalid digest size (%u)\n",
|
||||
sinfo->index, sinfo->msgdigest_len);
|
||||
|
@ -170,6 +176,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
|||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct x509_certificate *x509 = sinfo->signer, *p;
|
||||
struct asymmetric_key_id *auth;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
@ -187,11 +194,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
|||
goto maybe_missing_crypto_in_x509;
|
||||
|
||||
pr_debug("- issuer %s\n", x509->issuer);
|
||||
if (x509->authority)
|
||||
pr_debug("- authkeyid %*phN\n",
|
||||
x509->authority->len, x509->authority->data);
|
||||
if (x509->akid_id)
|
||||
pr_debug("- authkeyid.id %*phN\n",
|
||||
x509->akid_id->len, x509->akid_id->data);
|
||||
if (x509->akid_skid)
|
||||
pr_debug("- authkeyid.skid %*phN\n",
|
||||
x509->akid_skid->len, x509->akid_skid->data);
|
||||
|
||||
if (!x509->authority ||
|
||||
if ((!x509->akid_id && !x509->akid_skid) ||
|
||||
strcmp(x509->subject, x509->issuer) == 0) {
|
||||
/* If there's no authority certificate specified, then
|
||||
* the certificate must be self-signed and is the root
|
||||
|
@ -215,21 +225,42 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
|||
/* Look through the X.509 certificates in the PKCS#7 message's
|
||||
* list to see if the next one is there.
|
||||
*/
|
||||
pr_debug("- want %*phN\n",
|
||||
x509->authority->len, x509->authority->data);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
if (!p->skid)
|
||||
continue;
|
||||
pr_debug("- cmp [%u] %*phN\n",
|
||||
p->index, p->skid->len, p->skid->data);
|
||||
if (asymmetric_key_id_same(p->skid, x509->authority))
|
||||
goto found_issuer;
|
||||
auth = x509->akid_id;
|
||||
if (auth) {
|
||||
pr_debug("- want %*phN\n", auth->len, auth->data);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
pr_debug("- cmp [%u] %*phN\n",
|
||||
p->index, p->id->len, p->id->data);
|
||||
if (asymmetric_key_id_same(p->id, auth))
|
||||
goto found_issuer_check_skid;
|
||||
}
|
||||
} else {
|
||||
auth = x509->akid_skid;
|
||||
pr_debug("- want %*phN\n", auth->len, auth->data);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
if (!p->skid)
|
||||
continue;
|
||||
pr_debug("- cmp [%u] %*phN\n",
|
||||
p->index, p->skid->len, p->skid->data);
|
||||
if (asymmetric_key_id_same(p->skid, auth))
|
||||
goto found_issuer;
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't find the root of this chain */
|
||||
pr_debug("- top\n");
|
||||
return 0;
|
||||
|
||||
found_issuer_check_skid:
|
||||
/* We matched issuer + serialNumber, but if there's an
|
||||
* authKeyId.keyId, that must match the CA subjKeyId also.
|
||||
*/
|
||||
if (x509->akid_skid &&
|
||||
!asymmetric_key_id_same(p->skid, x509->akid_skid)) {
|
||||
pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
|
||||
sinfo->index, x509->index, p->index);
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
found_issuer:
|
||||
pr_debug("- subject %s\n", p->subject);
|
||||
if (p->seen) {
|
||||
|
@ -289,6 +320,18 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
|||
pr_devel("Using X.509[%u] for sig %u\n",
|
||||
sinfo->signer->index, sinfo->index);
|
||||
|
||||
/* Check that the PKCS#7 signing time is valid according to the X.509
|
||||
* certificate. We can't, however, check against the system clock
|
||||
* since that may not have been set yet and may be wrong.
|
||||
*/
|
||||
if (test_bit(sinfo_has_signing_time, &sinfo->aa_set)) {
|
||||
if (sinfo->signing_time < sinfo->signer->valid_from ||
|
||||
sinfo->signing_time > sinfo->signer->valid_to) {
|
||||
pr_warn("Message signed outside of X.509 validity window\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify the PKCS#7 binary against the key */
|
||||
ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
|
||||
if (ret < 0)
|
||||
|
@ -303,6 +346,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
|||
/**
|
||||
* pkcs7_verify - Verify a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message to be verified
|
||||
* @usage: The use to which the key is being put
|
||||
*
|
||||
* 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
|
||||
|
@ -314,6 +358,9 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
|||
*
|
||||
* Returns, in order of descending priority:
|
||||
*
|
||||
* (*) -EKEYREJECTED if a key was selected that had a usage restriction at
|
||||
* odds with the specified usage, or:
|
||||
*
|
||||
* (*) -EKEYREJECTED if a signature failed to match for which we found an
|
||||
* appropriate X.509 certificate, or:
|
||||
*
|
||||
|
@ -325,7 +372,8 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
|||
* (*) 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,
|
||||
enum key_being_used_for usage)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *x509;
|
||||
|
@ -334,12 +382,48 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
|
|||
|
||||
kenter("");
|
||||
|
||||
switch (usage) {
|
||||
case VERIFYING_MODULE_SIGNATURE:
|
||||
if (pkcs7->data_type != OID_data) {
|
||||
pr_warn("Invalid module sig (not pkcs7-data)\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
if (pkcs7->have_authattrs) {
|
||||
pr_warn("Invalid module sig (has authattrs)\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
break;
|
||||
case VERIFYING_FIRMWARE_SIGNATURE:
|
||||
if (pkcs7->data_type != OID_data) {
|
||||
pr_warn("Invalid firmware sig (not pkcs7-data)\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
if (!pkcs7->have_authattrs) {
|
||||
pr_warn("Invalid firmware sig (missing authattrs)\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
break;
|
||||
case VERIFYING_KEXEC_PE_SIGNATURE:
|
||||
if (pkcs7->data_type != OID_msIndirectData) {
|
||||
pr_warn("Invalid kexec sig (not Authenticode)\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
/* Authattr presence checked in parser */
|
||||
break;
|
||||
case VERIFYING_UNSPECIFIED_SIGNATURE:
|
||||
if (pkcs7->data_type != OID_data) {
|
||||
pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pr_debug("X.509[%u] %*phN\n",
|
||||
n, x509->authority->len, x509->authority->data);
|
||||
}
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
|
@ -359,3 +443,28 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
|
|||
return enopkg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_verify);
|
||||
|
||||
/**
|
||||
* pkcs7_supply_detached_data - Supply the data needed to verify a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message
|
||||
* @data: The data to be verified
|
||||
* @datalen: The amount of data
|
||||
*
|
||||
* Supply the detached data needed to verify a PKCS#7 message. Note that no
|
||||
* attempt to retain/pin the data is made. That is left to the caller. The
|
||||
* data will not be modified by pkcs7_verify() and will not be freed when the
|
||||
* PKCS#7 message is freed.
|
||||
*
|
||||
* Returns -EINVAL if data is already supplied in the message, 0 otherwise.
|
||||
*/
|
||||
int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
|
||||
const void *data, size_t datalen)
|
||||
{
|
||||
if (pkcs7->data) {
|
||||
pr_debug("Data already supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
pkcs7->data = data;
|
||||
pkcs7->data_len = datalen;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ EXPORT_SYMBOL_GPL(pkey_algo);
|
|||
const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
|
||||
[PKEY_ID_PGP] = "PGP",
|
||||
[PKEY_ID_X509] = "X509",
|
||||
[PKEY_ID_PKCS7] = "PKCS#7",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pkey_id_type_name);
|
||||
|
||||
|
|
|
@ -393,6 +393,7 @@ error_no_desc:
|
|||
* @pebuf: Buffer containing the PE binary image
|
||||
* @pelen: Length of the binary image
|
||||
* @trust_keyring: Signing certificates to use as starting points
|
||||
* @usage: The use to which the key is being put.
|
||||
* @_trusted: Set to true if trustworth, false otherwise
|
||||
*
|
||||
* Validate that the certificate chain inside the PKCS#7 message inside the PE
|
||||
|
@ -417,7 +418,9 @@ error_no_desc:
|
|||
* May also return -ENOMEM.
|
||||
*/
|
||||
int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring, bool *_trusted)
|
||||
struct key *trusted_keyring,
|
||||
enum key_being_used_for usage,
|
||||
bool *_trusted)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
struct pefile_context ctx;
|
||||
|
@ -462,7 +465,7 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
|||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_verify(pkcs7);
|
||||
ret = pkcs7_verify(pkcs7, usage);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
-- X.509 AuthorityKeyIdentifier
|
||||
-- rfc5280 section 4.2.1.1
|
||||
|
||||
AuthorityKeyIdentifier ::= SEQUENCE {
|
||||
keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
|
||||
authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
|
||||
authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL
|
||||
}
|
||||
|
||||
KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid })
|
||||
|
||||
CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial })
|
||||
|
||||
GeneralNames ::= SEQUENCE OF GeneralName
|
||||
|
||||
GeneralName ::= CHOICE {
|
||||
otherName [0] ANY,
|
||||
rfc822Name [1] IA5String,
|
||||
dNSName [2] IA5String,
|
||||
x400Address [3] ANY,
|
||||
directoryName [4] Name ({ x509_akid_note_name }),
|
||||
ediPartyName [5] ANY,
|
||||
uniformResourceIdentifier [6] IA5String,
|
||||
iPAddress [7] OCTET STRING,
|
||||
registeredID [8] OBJECT IDENTIFIER
|
||||
}
|
||||
|
||||
Name ::= SEQUENCE OF RelativeDistinguishedName
|
||||
|
||||
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeType OBJECT IDENTIFIER ({ x509_note_OID }),
|
||||
attributeValue ANY ({ x509_extract_name_segment })
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
#include "public_key.h"
|
||||
#include "x509_parser.h"
|
||||
#include "x509-asn1.h"
|
||||
#include "x509_akid-asn1.h"
|
||||
#include "x509_rsakey-asn1.h"
|
||||
|
||||
struct x509_parse_context {
|
||||
|
@ -35,6 +36,10 @@ struct x509_parse_context {
|
|||
u16 o_offset; /* Offset of organizationName (O) */
|
||||
u16 cn_offset; /* Offset of commonName (CN) */
|
||||
u16 email_offset; /* Offset of emailAddress */
|
||||
unsigned raw_akid_size;
|
||||
const void *raw_akid; /* Raw authorityKeyId in ASN.1 */
|
||||
const void *akid_raw_issuer; /* Raw directoryName in authorityKeyId */
|
||||
unsigned akid_raw_issuer_size;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -48,7 +53,8 @@ void x509_free_certificate(struct x509_certificate *cert)
|
|||
kfree(cert->subject);
|
||||
kfree(cert->id);
|
||||
kfree(cert->skid);
|
||||
kfree(cert->authority);
|
||||
kfree(cert->akid_id);
|
||||
kfree(cert->akid_skid);
|
||||
kfree(cert->sig.digest);
|
||||
mpi_free(cert->sig.rsa.s);
|
||||
kfree(cert);
|
||||
|
@ -85,6 +91,18 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
|
|||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
/* Decode the AuthorityKeyIdentifier */
|
||||
if (ctx->raw_akid) {
|
||||
pr_devel("AKID: %u %*phN\n",
|
||||
ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
|
||||
ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
|
||||
ctx->raw_akid, ctx->raw_akid_size);
|
||||
if (ret < 0) {
|
||||
pr_warn("Couldn't decode AuthKeyIdentifier\n");
|
||||
goto error_decode;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode the public key */
|
||||
ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
|
||||
ctx->key, ctx->key_size);
|
||||
|
@ -422,7 +440,6 @@ int x509_process_extension(void *context, size_t hdrlen,
|
|||
struct x509_parse_context *ctx = context;
|
||||
struct asymmetric_key_id *kid;
|
||||
const unsigned char *v = value;
|
||||
int i;
|
||||
|
||||
pr_debug("Extension: %u\n", ctx->last_oid);
|
||||
|
||||
|
@ -437,9 +454,7 @@ int x509_process_extension(void *context, size_t hdrlen,
|
|||
|
||||
ctx->cert->raw_skid_size = vlen;
|
||||
ctx->cert->raw_skid = v;
|
||||
kid = asymmetric_key_generate_id(ctx->cert->raw_subject,
|
||||
ctx->cert->raw_subject_size,
|
||||
v, vlen);
|
||||
kid = asymmetric_key_generate_id(v, vlen, "", 0);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
ctx->cert->skid = kid;
|
||||
|
@ -449,117 +464,113 @@ int x509_process_extension(void *context, size_t hdrlen,
|
|||
|
||||
if (ctx->last_oid == OID_authorityKeyIdentifier) {
|
||||
/* Get hold of the CA key fingerprint */
|
||||
if (ctx->cert->authority || vlen < 5)
|
||||
return -EBADMSG;
|
||||
|
||||
/* Authority Key Identifier must be a Constructed SEQUENCE */
|
||||
if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)))
|
||||
return -EBADMSG;
|
||||
|
||||
/* Authority Key Identifier is not indefinite length */
|
||||
if (unlikely(vlen == ASN1_INDEFINITE_LENGTH))
|
||||
return -EBADMSG;
|
||||
|
||||
if (vlen < ASN1_INDEFINITE_LENGTH) {
|
||||
/* Short Form length */
|
||||
if (v[1] != vlen - 2 ||
|
||||
v[2] != SEQ_TAG_KEYID ||
|
||||
v[3] > vlen - 4)
|
||||
return -EBADMSG;
|
||||
|
||||
vlen = v[3];
|
||||
v += 4;
|
||||
} else {
|
||||
/* Long Form length */
|
||||
size_t seq_len = 0;
|
||||
size_t sub = v[1] - ASN1_INDEFINITE_LENGTH;
|
||||
|
||||
if (sub > 2)
|
||||
return -EBADMSG;
|
||||
|
||||
/* calculate the length from subsequent octets */
|
||||
v += 2;
|
||||
for (i = 0; i < sub; i++) {
|
||||
seq_len <<= 8;
|
||||
seq_len |= v[i];
|
||||
}
|
||||
|
||||
if (seq_len != vlen - 2 - sub ||
|
||||
v[sub] != SEQ_TAG_KEYID ||
|
||||
v[sub + 1] > vlen - 4 - sub)
|
||||
return -EBADMSG;
|
||||
|
||||
vlen = v[sub + 1];
|
||||
v += (sub + 2);
|
||||
}
|
||||
|
||||
kid = asymmetric_key_generate_id(ctx->cert->raw_issuer,
|
||||
ctx->cert->raw_issuer_size,
|
||||
v, vlen);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->authority = kid;
|
||||
ctx->raw_akid = v;
|
||||
ctx->raw_akid_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record a certificate time.
|
||||
/**
|
||||
* x509_decode_time - Decode an X.509 time ASN.1 object
|
||||
* @_t: The time to fill in
|
||||
* @hdrlen: The length of the object header
|
||||
* @tag: The object tag
|
||||
* @value: The object value
|
||||
* @vlen: The size of the object value
|
||||
*
|
||||
* Decode an ASN.1 universal time or generalised time field into a struct the
|
||||
* kernel can handle and check it for validity. The time is decoded thus:
|
||||
*
|
||||
* [RFC5280 §4.1.2.5]
|
||||
* CAs conforming to this profile MUST always encode certificate validity
|
||||
* dates through the year 2049 as UTCTime; certificate validity dates in
|
||||
* 2050 or later MUST be encoded as GeneralizedTime. Conforming
|
||||
* applications MUST be able to process validity dates that are encoded in
|
||||
* either UTCTime or GeneralizedTime.
|
||||
*/
|
||||
static int x509_note_time(struct tm *tm, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const unsigned char *value, size_t vlen)
|
||||
int x509_decode_time(time64_t *_t, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const unsigned char *value, size_t vlen)
|
||||
{
|
||||
static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31 };
|
||||
const unsigned char *p = value;
|
||||
unsigned year, mon, day, hour, min, sec, mon_len;
|
||||
|
||||
#define dec2bin(X) ((X) - '0')
|
||||
#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
|
||||
#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
|
||||
|
||||
if (tag == ASN1_UNITIM) {
|
||||
/* UTCTime: YYMMDDHHMMSSZ */
|
||||
if (vlen != 13)
|
||||
goto unsupported_time;
|
||||
tm->tm_year = DD2bin(p);
|
||||
if (tm->tm_year >= 50)
|
||||
tm->tm_year += 1900;
|
||||
year = DD2bin(p);
|
||||
if (year >= 50)
|
||||
year += 1900;
|
||||
else
|
||||
tm->tm_year += 2000;
|
||||
year += 2000;
|
||||
} else if (tag == ASN1_GENTIM) {
|
||||
/* GenTime: YYYYMMDDHHMMSSZ */
|
||||
if (vlen != 15)
|
||||
goto unsupported_time;
|
||||
tm->tm_year = DD2bin(p) * 100 + DD2bin(p);
|
||||
year = DD2bin(p) * 100 + DD2bin(p);
|
||||
if (year >= 1950 && year <= 2049)
|
||||
goto invalid_time;
|
||||
} else {
|
||||
goto unsupported_time;
|
||||
}
|
||||
|
||||
tm->tm_year -= 1900;
|
||||
tm->tm_mon = DD2bin(p) - 1;
|
||||
tm->tm_mday = DD2bin(p);
|
||||
tm->tm_hour = DD2bin(p);
|
||||
tm->tm_min = DD2bin(p);
|
||||
tm->tm_sec = DD2bin(p);
|
||||
mon = DD2bin(p);
|
||||
day = DD2bin(p);
|
||||
hour = DD2bin(p);
|
||||
min = DD2bin(p);
|
||||
sec = DD2bin(p);
|
||||
|
||||
if (*p != 'Z')
|
||||
goto unsupported_time;
|
||||
|
||||
mon_len = month_lengths[mon];
|
||||
if (mon == 2) {
|
||||
if (year % 4 == 0) {
|
||||
mon_len = 29;
|
||||
if (year % 100 == 0) {
|
||||
year /= 100;
|
||||
if (year % 4 != 0)
|
||||
mon_len = 28;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (year < 1970 ||
|
||||
mon < 1 || mon > 12 ||
|
||||
day < 1 || day > mon_len ||
|
||||
hour < 0 || hour > 23 ||
|
||||
min < 0 || min > 59 ||
|
||||
sec < 0 || sec > 59)
|
||||
goto invalid_time;
|
||||
|
||||
*_t = mktime64(year, mon, day, hour, min, sec);
|
||||
return 0;
|
||||
|
||||
unsupported_time:
|
||||
pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n",
|
||||
tag, (int)vlen, (int)vlen, value);
|
||||
pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
|
||||
tag, (int)vlen, value);
|
||||
return -EBADMSG;
|
||||
invalid_time:
|
||||
pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
|
||||
tag, (int)vlen, value);
|
||||
return -EBADMSG;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_decode_time);
|
||||
|
||||
int x509_note_not_before(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
|
||||
return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
|
||||
}
|
||||
|
||||
int x509_note_not_after(void *context, size_t hdrlen,
|
||||
|
@ -567,5 +578,71 @@ int x509_note_not_after(void *context, size_t hdrlen,
|
|||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
|
||||
return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a key identifier-based AuthorityKeyIdentifier
|
||||
*/
|
||||
int x509_akid_note_kid(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
|
||||
|
||||
if (ctx->cert->akid_skid)
|
||||
return 0;
|
||||
|
||||
kid = asymmetric_key_generate_id(value, vlen, "", 0);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->akid_skid = kid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a directoryName in an AuthorityKeyIdentifier
|
||||
*/
|
||||
int x509_akid_note_name(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
|
||||
pr_debug("AKID: name: %*phN\n", (int)vlen, value);
|
||||
|
||||
ctx->akid_raw_issuer = value;
|
||||
ctx->akid_raw_issuer_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a serial number in an AuthorityKeyIdentifier
|
||||
*/
|
||||
int x509_akid_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
struct asymmetric_key_id *kid;
|
||||
|
||||
pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
|
||||
|
||||
if (!ctx->akid_raw_issuer || ctx->cert->akid_id)
|
||||
return 0;
|
||||
|
||||
kid = asymmetric_key_generate_id(value,
|
||||
vlen,
|
||||
ctx->akid_raw_issuer,
|
||||
ctx->akid_raw_issuer_size);
|
||||
if (IS_ERR(kid))
|
||||
return PTR_ERR(kid);
|
||||
|
||||
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
|
||||
ctx->cert->akid_id = kid;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@ struct x509_certificate {
|
|||
struct public_key_signature sig; /* Signature parameters */
|
||||
char *issuer; /* Name of certificate issuer */
|
||||
char *subject; /* Name of certificate subject */
|
||||
struct asymmetric_key_id *id; /* Serial number + issuer */
|
||||
struct asymmetric_key_id *id; /* Issuer + Serial number */
|
||||
struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
|
||||
struct asymmetric_key_id *authority; /* Authority key identifier (optional) */
|
||||
struct tm valid_from;
|
||||
struct tm valid_to;
|
||||
struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */
|
||||
struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */
|
||||
time64_t valid_from;
|
||||
time64_t valid_to;
|
||||
const void *tbs; /* Signed data */
|
||||
unsigned tbs_size; /* Size of signed data */
|
||||
unsigned raw_sig_size; /* Size of sigature */
|
||||
|
@ -48,6 +49,9 @@ struct x509_certificate {
|
|||
*/
|
||||
extern void x509_free_certificate(struct x509_certificate *cert);
|
||||
extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
|
||||
extern int x509_decode_time(time64_t *_t, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const unsigned char *value, size_t vlen);
|
||||
|
||||
/*
|
||||
* x509_public_key.c
|
||||
|
|
|
@ -65,23 +65,37 @@ __setup("ca_keys=", ca_keys_setup);
|
|||
/**
|
||||
* x509_request_asymmetric_key - Request a key by X.509 certificate params.
|
||||
* @keyring: The keys to search.
|
||||
* @kid: The key ID.
|
||||
* @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 subject name and key ID. These might,
|
||||
* for instance, be the issuer name and the authority key ID of an X.509
|
||||
* certificate that needs to be verified.
|
||||
* 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 *kid,
|
||||
const struct asymmetric_key_id *id,
|
||||
const struct asymmetric_key_id *skid,
|
||||
bool partial)
|
||||
{
|
||||
key_ref_t key;
|
||||
char *id, *p;
|
||||
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 = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL);
|
||||
if (!id)
|
||||
p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
|
||||
if (!req)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (partial) {
|
||||
|
@ -92,32 +106,48 @@ struct key *x509_request_asymmetric_key(struct key *keyring,
|
|||
*p++ = 'x';
|
||||
}
|
||||
*p++ = ':';
|
||||
p = bin2hex(p, kid->data, kid->len);
|
||||
p = bin2hex(p, lookup, len);
|
||||
*p = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
pr_debug("Look up: \"%s\"\n", req);
|
||||
|
||||
key = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key));
|
||||
kfree(id);
|
||||
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(key)) {
|
||||
switch (PTR_ERR(key)) {
|
||||
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(key);
|
||||
return ERR_CAST(ref);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__,
|
||||
key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
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);
|
||||
|
||||
|
@ -227,10 +257,11 @@ static int x509_validate_trust(struct x509_certificate *cert,
|
|||
if (!trust_keyring)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ca_keyid && !asymmetric_key_id_partial(cert->authority, ca_keyid))
|
||||
if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid))
|
||||
return -EPERM;
|
||||
|
||||
key = x509_request_asymmetric_key(trust_keyring, cert->authority,
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
cert->akid_id, cert->akid_skid,
|
||||
false);
|
||||
if (!IS_ERR(key)) {
|
||||
if (!use_builtin_keys
|
||||
|
@ -271,14 +302,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
|||
}
|
||||
|
||||
pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]);
|
||||
pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n",
|
||||
cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
|
||||
cert->valid_from.tm_mday, cert->valid_from.tm_hour,
|
||||
cert->valid_from.tm_min, cert->valid_from.tm_sec);
|
||||
pr_devel("Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d\n",
|
||||
cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1,
|
||||
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
|
||||
cert->valid_to.tm_min, cert->valid_to.tm_sec);
|
||||
pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
|
||||
pr_devel("Cert Signature: %s + %s\n",
|
||||
pkey_algo_name[cert->sig.pkey_algo],
|
||||
hash_algo_name[cert->sig.pkey_hash_algo]);
|
||||
|
@ -287,8 +311,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
|||
cert->pub->id_type = PKEY_ID_X509;
|
||||
|
||||
/* Check the signature on the key if it appears to be self-signed */
|
||||
if (!cert->authority ||
|
||||
asymmetric_key_id_same(cert->skid, cert->authority)) {
|
||||
if ((!cert->akid_skid && !cert->akid_id) ||
|
||||
asymmetric_key_id_same(cert->skid, cert->akid_skid) ||
|
||||
asymmetric_key_id_same(cert->id, cert->akid_id)) {
|
||||
ret = x509_check_signature(cert->pub, cert); /* self-signed */
|
||||
if (ret < 0)
|
||||
goto error_free_cert;
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTO_PKCS7_H
|
||||
#define _CRYPTO_PKCS7_H
|
||||
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
struct key;
|
||||
struct pkcs7_message;
|
||||
|
||||
|
@ -33,4 +38,10 @@ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
|||
/*
|
||||
* pkcs7_verify.c
|
||||
*/
|
||||
extern int pkcs7_verify(struct pkcs7_message *pkcs7);
|
||||
extern int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
enum key_being_used_for usage);
|
||||
|
||||
extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
|
||||
const void *data, size_t datalen);
|
||||
|
||||
#endif /* _CRYPTO_PKCS7_H */
|
||||
|
|
|
@ -33,11 +33,26 @@ extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
|
|||
enum pkey_id_type {
|
||||
PKEY_ID_PGP, /* OpenPGP generated key ID */
|
||||
PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
|
||||
PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
|
||||
PKEY_ID_TYPE__LAST
|
||||
};
|
||||
|
||||
extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
|
||||
|
||||
/*
|
||||
* The use to which an asymmetric key is being put.
|
||||
*/
|
||||
enum key_being_used_for {
|
||||
VERIFYING_MODULE_SIGNATURE,
|
||||
VERIFYING_FIRMWARE_SIGNATURE,
|
||||
VERIFYING_KEXEC_PE_SIGNATURE,
|
||||
VERIFYING_KEY_SIGNATURE,
|
||||
VERIFYING_KEY_SELF_SIGNATURE,
|
||||
VERIFYING_UNSPECIFIED_SIGNATURE,
|
||||
NR__KEY_BEING_USED_FOR
|
||||
};
|
||||
extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
|
||||
|
||||
/*
|
||||
* Cryptographic data for the public-key subtype of the asymmetric key type.
|
||||
*
|
||||
|
@ -101,7 +116,8 @@ extern int verify_signature(const struct key *key,
|
|||
|
||||
struct asymmetric_key_id;
|
||||
extern struct key *x509_request_asymmetric_key(struct key *keyring,
|
||||
const struct asymmetric_key_id *kid,
|
||||
const struct asymmetric_key_id *id,
|
||||
const struct asymmetric_key_id *skid,
|
||||
bool partial);
|
||||
|
||||
#endif /* _LINUX_PUBLIC_KEY_H */
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
extern struct key *system_trusted_keyring;
|
||||
static inline struct key *get_system_trusted_keyring(void)
|
||||
|
@ -28,4 +29,10 @@ static inline struct key *get_system_trusted_keyring(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||
extern int system_verify_data(const void *data, unsigned long len,
|
||||
const void *raw_pkcs7, size_t pkcs7_len,
|
||||
enum key_being_used_for usage);
|
||||
#endif
|
||||
|
||||
#endif /* _KEYS_SYSTEM_KEYRING_H */
|
||||
|
|
|
@ -41,7 +41,7 @@ enum OID {
|
|||
OID_signed_data, /* 1.2.840.113549.1.7.2 */
|
||||
/* PKCS#9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)} */
|
||||
OID_email_address, /* 1.2.840.113549.1.9.1 */
|
||||
OID_content_type, /* 1.2.840.113549.1.9.3 */
|
||||
OID_contentType, /* 1.2.840.113549.1.9.3 */
|
||||
OID_messageDigest, /* 1.2.840.113549.1.9.4 */
|
||||
OID_signingTime, /* 1.2.840.113549.1.9.5 */
|
||||
OID_smimeCapabilites, /* 1.2.840.113549.1.9.15 */
|
||||
|
@ -54,6 +54,8 @@ enum OID {
|
|||
|
||||
/* Microsoft Authenticode & Software Publishing */
|
||||
OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */
|
||||
OID_msStatementType, /* 1.3.6.1.4.1.311.2.1.11 */
|
||||
OID_msSpOpusInfo, /* 1.3.6.1.4.1.311.2.1.12 */
|
||||
OID_msPeImageDataObjId, /* 1.3.6.1.4.1.311.2.1.15 */
|
||||
OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */
|
||||
OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
|
||||
|
|
|
@ -12,7 +12,11 @@
|
|||
#ifndef _LINUX_VERIFY_PEFILE_H
|
||||
#define _LINUX_VERIFY_PEFILE_H
|
||||
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring, bool *_trusted);
|
||||
struct key *trusted_keyring,
|
||||
enum key_being_used_for usage,
|
||||
bool *_trusted);
|
||||
|
||||
#endif /* _LINUX_VERIFY_PEFILE_H */
|
||||
|
|
59
init/Kconfig
59
init/Kconfig
|
@ -1752,6 +1752,37 @@ config SYSTEM_TRUSTED_KEYRING
|
|||
|
||||
Keys in this keyring are used by module signature checking.
|
||||
|
||||
config SYSTEM_TRUSTED_KEYS
|
||||
string "Additional X.509 keys for default system keyring"
|
||||
depends on SYSTEM_TRUSTED_KEYRING
|
||||
help
|
||||
If set, this option should be the filename of a PEM-formatted file
|
||||
containing trusted X.509 certificates to be included in the default
|
||||
system keyring. Any certificate used for module signing is implicitly
|
||||
also trusted.
|
||||
|
||||
NOTE: If you previously provided keys for the system keyring in the
|
||||
form of DER-encoded *.x509 files in the top-level build directory,
|
||||
those are no longer used. You will need to set this option instead.
|
||||
|
||||
config SYSTEM_DATA_VERIFICATION
|
||||
def_bool n
|
||||
select SYSTEM_TRUSTED_KEYRING
|
||||
select KEYS
|
||||
select CRYPTO
|
||||
select ASYMMETRIC_KEY_TYPE
|
||||
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select PUBLIC_KEY_ALGO_RSA
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
select X509_CERTIFICATE_PARSER
|
||||
select PKCS7_MESSAGE_PARSER
|
||||
help
|
||||
Provide PKCS#7 message verification using the contents of the system
|
||||
trusted keyring to provide public keys. This then can be used for
|
||||
module verification, kexec image verification and firmware blob
|
||||
verification.
|
||||
|
||||
config PROFILING
|
||||
bool "Profiling support"
|
||||
help
|
||||
|
@ -1860,20 +1891,16 @@ config MODULE_SRCVERSION_ALL
|
|||
config MODULE_SIG
|
||||
bool "Module signature verification"
|
||||
depends on MODULES
|
||||
select SYSTEM_TRUSTED_KEYRING
|
||||
select KEYS
|
||||
select CRYPTO
|
||||
select ASYMMETRIC_KEY_TYPE
|
||||
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select PUBLIC_KEY_ALGO_RSA
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
select X509_CERTIFICATE_PARSER
|
||||
select SYSTEM_DATA_VERIFICATION
|
||||
help
|
||||
Check modules for valid signatures upon load: the signature
|
||||
is simply appended to the module. For more information see
|
||||
Documentation/module-signing.txt.
|
||||
|
||||
Note that this option adds the OpenSSL development packages as a
|
||||
kernel build dependency so that the signing tool can use its crypto
|
||||
library.
|
||||
|
||||
!!!WARNING!!! If you enable this option, you MUST make sure that the
|
||||
module DOES NOT get stripped after being signed. This includes the
|
||||
debuginfo strip done by some packagers (such as rpmbuild) and
|
||||
|
@ -1938,6 +1965,20 @@ config MODULE_SIG_HASH
|
|||
default "sha384" if MODULE_SIG_SHA384
|
||||
default "sha512" if MODULE_SIG_SHA512
|
||||
|
||||
config MODULE_SIG_KEY
|
||||
string "File name or PKCS#11 URI of module signing key"
|
||||
default "signing_key.pem"
|
||||
depends on MODULE_SIG
|
||||
help
|
||||
Provide the file name of a private key/certificate in PEM format,
|
||||
or a PKCS#11 URI according to RFC7512. The file should contain, or
|
||||
the URI should identify, both the certificate and its corresponding
|
||||
private key.
|
||||
|
||||
If this option is unchanged from its default "signing_key.pem",
|
||||
then the kernel will automatically generate the private key and
|
||||
certificate as described in Documentation/module-signing.txt
|
||||
|
||||
config MODULE_COMPRESS
|
||||
bool "Compress modules on installation"
|
||||
depends on MODULES
|
||||
|
|
114
kernel/Makefile
114
kernel/Makefile
|
@ -114,46 +114,74 @@ $(obj)/config_data.h: $(obj)/config_data.gz FORCE
|
|||
|
||||
###############################################################################
|
||||
#
|
||||
# Roll all the X.509 certificates that we can find together and pull them into
|
||||
# the kernel so that they get loaded into the system trusted keyring during
|
||||
# boot.
|
||||
# When a Kconfig string contains a filename, it is suitable for
|
||||
# passing to shell commands. It is surrounded by double-quotes, and
|
||||
# any double-quotes or backslashes within it are escaped by
|
||||
# backslashes.
|
||||
#
|
||||
# We look in the source root and the build root for all files whose name ends
|
||||
# in ".x509". Unfortunately, this will generate duplicate filenames, so we
|
||||
# have make canonicalise the pathnames and then sort them to discard the
|
||||
# duplicates.
|
||||
# This is no use for dependencies or $(wildcard). We need to strip the
|
||||
# surrounding quotes and the escaping from quotes and backslashes, and
|
||||
# we *do* need to escape any spaces in the string. So, for example:
|
||||
#
|
||||
# Usage: $(eval $(call config_filename,FOO))
|
||||
#
|
||||
# Defines FOO_FILENAME based on the contents of the CONFIG_FOO option,
|
||||
# transformed as described above to be suitable for use within the
|
||||
# makefile.
|
||||
#
|
||||
# Also, if the filename is a relative filename and exists in the source
|
||||
# tree but not the build tree, define FOO_SRCPREFIX as $(srctree)/ to
|
||||
# be prefixed to *both* command invocation and dependencies.
|
||||
#
|
||||
# Note: We also print the filenames in the quiet_cmd_foo text, and
|
||||
# perhaps ought to have a version specially escaped for that purpose.
|
||||
# But it's only cosmetic, and $(patsubst "%",%,$(CONFIG_FOO)) is good
|
||||
# enough. It'll strip the quotes in the common case where there's no
|
||||
# space and it's a simple filename, and it'll retain the quotes when
|
||||
# there's a space. There are some esoteric cases in which it'll print
|
||||
# the wrong thing, but we don't really care. The actual dependencies
|
||||
# and commands *do* get it right, with various combinations of single
|
||||
# and double quotes, backslashes and spaces in the filenames.
|
||||
#
|
||||
###############################################################################
|
||||
#
|
||||
quote := $(firstword " ")
|
||||
space :=
|
||||
space +=
|
||||
space_escape := %%%SPACE%%%
|
||||
#
|
||||
define config_filename
|
||||
ifneq ($$(CONFIG_$(1)),"")
|
||||
$(1)_FILENAME := $$(subst \\,\,$$(subst \$$(quote),$$(quote),$$(subst $$(space_escape),\$$(space),$$(patsubst "%",%,$$(subst $$(space),$$(space_escape),$$(CONFIG_$(1)))))))
|
||||
ifneq ($$(patsubst /%,%,$$(firstword $$($(1)_FILENAME))),$$(firstword $$($(1)_FILENAME)))
|
||||
else
|
||||
ifeq ($$(wildcard $$($(1)_FILENAME)),)
|
||||
ifneq ($$(wildcard $$(srctree)/$$($(1)_FILENAME)),)
|
||||
$(1)_SRCPREFIX := $(srctree)/
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endef
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
|
||||
X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509)
|
||||
X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += $(objtree)/signing_key.x509
|
||||
X509_CERTIFICATES-raw := $(sort $(foreach CERT,$(X509_CERTIFICATES-y), \
|
||||
$(or $(realpath $(CERT)),$(CERT))))
|
||||
X509_CERTIFICATES := $(subst $(realpath $(objtree))/,,$(X509_CERTIFICATES-raw))
|
||||
|
||||
ifeq ($(X509_CERTIFICATES),)
|
||||
$(warning *** No X.509 certificates found ***)
|
||||
endif
|
||||
$(eval $(call config_filename,SYSTEM_TRUSTED_KEYS))
|
||||
|
||||
ifneq ($(wildcard $(obj)/.x509.list),)
|
||||
ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES))
|
||||
$(warning X.509 certificate list changed to "$(X509_CERTIFICATES)" from "$(shell cat $(obj)/.x509.list)")
|
||||
$(shell rm $(obj)/.x509.list)
|
||||
endif
|
||||
endif
|
||||
# GCC doesn't include .incbin files in -MD generated dependencies (PR#66871)
|
||||
$(obj)/system_certificates.o: $(obj)/x509_certificate_list
|
||||
|
||||
kernel/system_certificates.o: $(obj)/x509_certificate_list
|
||||
# Cope with signing_key.x509 existing in $(srctree) not $(objtree)
|
||||
AFLAGS_system_certificates.o := -I$(srctree)
|
||||
|
||||
quiet_cmd_x509certs = CERTS $@
|
||||
cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; $(kecho) " - Including cert $(X509)")
|
||||
quiet_cmd_extract_certs = EXTRACT_CERTS $(patsubst "%",%,$(2))
|
||||
cmd_extract_certs = scripts/extract-cert $(2) $@ || ( rm $@; exit 1)
|
||||
|
||||
targets += $(obj)/x509_certificate_list
|
||||
$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list
|
||||
$(call if_changed,x509certs)
|
||||
|
||||
targets += $(obj)/.x509.list
|
||||
$(obj)/.x509.list:
|
||||
@echo $(X509_CERTIFICATES) >$@
|
||||
targets += x509_certificate_list
|
||||
$(obj)/x509_certificate_list: scripts/extract-cert $(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(SYSTEM_TRUSTED_KEYS_FILENAME) FORCE
|
||||
$(call if_changed,extract_certs,$(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(CONFIG_SYSTEM_TRUSTED_KEYS))
|
||||
endif
|
||||
|
||||
clean-files := x509_certificate_list .x509.list
|
||||
|
@ -170,7 +198,11 @@ ifndef CONFIG_MODULE_SIG_HASH
|
|||
$(error Could not determine digest type to use from kernel config)
|
||||
endif
|
||||
|
||||
signing_key.priv signing_key.x509: x509.genkey
|
||||
# We do it this way rather than having a boolean option for enabling an
|
||||
# external private key, because 'make randconfig' might enable such a
|
||||
# boolean option and we unfortunately can't make it depend on !RANDCONFIG.
|
||||
ifeq ($(CONFIG_MODULE_SIG_KEY),"signing_key.pem")
|
||||
signing_key.pem: x509.genkey
|
||||
@echo "###"
|
||||
@echo "### Now generating an X.509 key pair to be used for signing modules."
|
||||
@echo "###"
|
||||
|
@ -181,8 +213,8 @@ signing_key.priv signing_key.x509: x509.genkey
|
|||
@echo "###"
|
||||
openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
|
||||
-batch -x509 -config x509.genkey \
|
||||
-outform DER -out signing_key.x509 \
|
||||
-keyout signing_key.priv 2>&1
|
||||
-outform PEM -out signing_key.pem \
|
||||
-keyout signing_key.pem 2>&1
|
||||
@echo "###"
|
||||
@echo "### Key pair generated."
|
||||
@echo "###"
|
||||
|
@ -207,3 +239,17 @@ x509.genkey:
|
|||
@echo >>x509.genkey "subjectKeyIdentifier=hash"
|
||||
@echo >>x509.genkey "authorityKeyIdentifier=keyid"
|
||||
endif
|
||||
|
||||
$(eval $(call config_filename,MODULE_SIG_KEY))
|
||||
|
||||
# If CONFIG_MODULE_SIG_KEY isn't a PKCS#11 URI, depend on it
|
||||
ifeq ($(patsubst pkcs11:%,%,$(firstword $(MODULE_SIG_KEY_FILENAME))),$(firstword $(MODULE_SIG_KEY_FILENAME)))
|
||||
X509_DEP := $(MODULE_SIG_KEY_SRCPREFIX)$(MODULE_SIG_KEY_FILENAME)
|
||||
endif
|
||||
|
||||
# GCC PR#66871 again.
|
||||
$(obj)/system_certificates.o: signing_key.x509
|
||||
|
||||
signing_key.x509: scripts/extract-cert include/config/module/sig/key.h $(X509_DEP)
|
||||
$(call cmd,extract_certs,$(MODULE_SIG_KEY_SRCPREFIX)$(CONFIG_MODULE_SIG_KEY))
|
||||
endif
|
||||
|
|
|
@ -10,11 +10,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include "module-internal.h"
|
||||
|
||||
/*
|
||||
|
@ -28,170 +25,22 @@
|
|||
* - Information block
|
||||
*/
|
||||
struct module_signature {
|
||||
u8 algo; /* Public-key crypto algorithm [enum pkey_algo] */
|
||||
u8 hash; /* Digest algorithm [enum hash_algo] */
|
||||
u8 id_type; /* Key identifier type [enum pkey_id_type] */
|
||||
u8 signer_len; /* Length of signer's name */
|
||||
u8 key_id_len; /* Length of key identifier */
|
||||
u8 algo; /* Public-key crypto algorithm [0] */
|
||||
u8 hash; /* Digest algorithm [0] */
|
||||
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
||||
u8 signer_len; /* Length of signer's name [0] */
|
||||
u8 key_id_len; /* Length of key identifier [0] */
|
||||
u8 __pad[3];
|
||||
__be32 sig_len; /* Length of signature data */
|
||||
};
|
||||
|
||||
/*
|
||||
* Digest the module contents.
|
||||
*/
|
||||
static struct public_key_signature *mod_make_digest(enum hash_algo hash,
|
||||
const void *mod,
|
||||
unsigned long modlen)
|
||||
{
|
||||
struct public_key_signature *pks;
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
/* We allocate the hash operational data storage on the end of our
|
||||
* context data and the digest output buffer on the end of that.
|
||||
*/
|
||||
ret = -ENOMEM;
|
||||
pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
|
||||
if (!pks)
|
||||
goto error_no_pks;
|
||||
|
||||
pks->pkey_hash_algo = hash;
|
||||
pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
|
||||
pks->digest_size = digest_size;
|
||||
|
||||
desc = (void *)pks + sizeof(*pks);
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = crypto_shash_finup(desc, mod, modlen, pks->digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
crypto_free_shash(tfm);
|
||||
pr_devel("<==%s() = ok\n", __func__);
|
||||
return pks;
|
||||
|
||||
error:
|
||||
kfree(pks);
|
||||
error_no_pks:
|
||||
crypto_free_shash(tfm);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract an MPI array from the signature data. This represents the actual
|
||||
* signature. Each raw MPI is prefaced by a BE 2-byte value indicating the
|
||||
* size of the MPI in bytes.
|
||||
*
|
||||
* RSA signatures only have one MPI, so currently we only read one.
|
||||
*/
|
||||
static int mod_extract_mpi_array(struct public_key_signature *pks,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
size_t nbytes;
|
||||
MPI mpi;
|
||||
|
||||
if (len < 3)
|
||||
return -EBADMSG;
|
||||
nbytes = ((const u8 *)data)[0] << 8 | ((const u8 *)data)[1];
|
||||
data += 2;
|
||||
len -= 2;
|
||||
if (len != nbytes)
|
||||
return -EBADMSG;
|
||||
|
||||
mpi = mpi_read_raw_data(data, nbytes);
|
||||
if (!mpi)
|
||||
return -ENOMEM;
|
||||
pks->mpi[0] = mpi;
|
||||
pks->nr_mpi = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Request an asymmetric key.
|
||||
*/
|
||||
static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
|
||||
const u8 *key_id, size_t key_id_len)
|
||||
{
|
||||
key_ref_t key;
|
||||
size_t i;
|
||||
char *id, *q;
|
||||
|
||||
pr_devel("==>%s(,%zu,,%zu)\n", __func__, signer_len, key_id_len);
|
||||
|
||||
/* Construct an identifier. */
|
||||
id = kmalloc(signer_len + 2 + key_id_len * 2 + 1, GFP_KERNEL);
|
||||
if (!id)
|
||||
return ERR_PTR(-ENOKEY);
|
||||
|
||||
memcpy(id, signer, signer_len);
|
||||
|
||||
q = id + signer_len;
|
||||
*q++ = ':';
|
||||
*q++ = ' ';
|
||||
for (i = 0; i < key_id_len; i++) {
|
||||
*q++ = hex_asc[*key_id >> 4];
|
||||
*q++ = hex_asc[*key_id++ & 0x0f];
|
||||
}
|
||||
|
||||
*q = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
|
||||
key = keyring_search(make_key_ref(system_trusted_keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_warn("Request for unknown module key '%s' err %ld\n",
|
||||
id, PTR_ERR(key));
|
||||
kfree(id);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
switch (PTR_ERR(key)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(key);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the signature on a module.
|
||||
*/
|
||||
int mod_verify_sig(const void *mod, unsigned long *_modlen)
|
||||
{
|
||||
struct public_key_signature *pks;
|
||||
struct module_signature ms;
|
||||
struct key *key;
|
||||
const void *sig;
|
||||
size_t modlen = *_modlen, sig_len;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s(,%zu)\n", __func__, modlen);
|
||||
|
||||
|
@ -205,46 +54,24 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
|
|||
if (sig_len >= modlen)
|
||||
return -EBADMSG;
|
||||
modlen -= sig_len;
|
||||
if ((size_t)ms.signer_len + ms.key_id_len >= modlen)
|
||||
return -EBADMSG;
|
||||
modlen -= (size_t)ms.signer_len + ms.key_id_len;
|
||||
|
||||
*_modlen = modlen;
|
||||
sig = mod + modlen;
|
||||
|
||||
/* For the moment, only support RSA and X.509 identifiers */
|
||||
if (ms.algo != PKEY_ALGO_RSA ||
|
||||
ms.id_type != PKEY_ID_X509)
|
||||
if (ms.id_type != PKEY_ID_PKCS7) {
|
||||
pr_err("Module is not signed with expected PKCS#7 message\n");
|
||||
return -ENOPKG;
|
||||
|
||||
if (ms.hash >= PKEY_HASH__LAST ||
|
||||
!hash_algo_name[ms.hash])
|
||||
return -ENOPKG;
|
||||
|
||||
key = request_asymmetric_key(sig, ms.signer_len,
|
||||
sig + ms.signer_len, ms.key_id_len);
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key);
|
||||
|
||||
pks = mod_make_digest(ms.hash, mod, modlen);
|
||||
if (IS_ERR(pks)) {
|
||||
ret = PTR_ERR(pks);
|
||||
goto error_put_key;
|
||||
}
|
||||
|
||||
ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len,
|
||||
sig_len);
|
||||
if (ret < 0)
|
||||
goto error_free_pks;
|
||||
if (ms.algo != 0 ||
|
||||
ms.hash != 0 ||
|
||||
ms.signer_len != 0 ||
|
||||
ms.key_id_len != 0 ||
|
||||
ms.__pad[0] != 0 ||
|
||||
ms.__pad[1] != 0 ||
|
||||
ms.__pad[2] != 0) {
|
||||
pr_err("PKCS#7 signature info has unexpected non-zero params\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
ret = verify_signature(key, pks);
|
||||
pr_devel("verify_signature() = %d\n", ret);
|
||||
|
||||
error_free_pks:
|
||||
mpi_free(pks->rsa.s);
|
||||
kfree(pks);
|
||||
error_put_key:
|
||||
key_put(key);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
return system_verify_data(mod, modlen, mod + modlen, sig_len,
|
||||
VERIFYING_MODULE_SIGNATURE);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
.globl VMLINUX_SYMBOL(system_certificate_list)
|
||||
VMLINUX_SYMBOL(system_certificate_list):
|
||||
__cert_list_start:
|
||||
#ifdef CONFIG_MODULE_SIG
|
||||
.incbin "signing_key.x509"
|
||||
#endif
|
||||
.incbin "kernel/x509_certificate_list"
|
||||
__cert_list_end:
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "module-internal.h"
|
||||
#include <crypto/pkcs7.h>
|
||||
|
||||
struct key *system_trusted_keyring;
|
||||
EXPORT_SYMBOL_GPL(system_trusted_keyring);
|
||||
|
@ -104,3 +104,54 @@ dodgy_cert:
|
|||
return 0;
|
||||
}
|
||||
late_initcall(load_system_certificate_list);
|
||||
|
||||
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
|
||||
|
||||
/**
|
||||
* Verify a PKCS#7-based signature on system data.
|
||||
* @data: The data to be verified.
|
||||
* @len: Size of @data.
|
||||
* @raw_pkcs7: The PKCS#7 message that is the signature.
|
||||
* @pkcs7_len: The size of @raw_pkcs7.
|
||||
* @usage: The use to which the key is being put.
|
||||
*/
|
||||
int system_verify_data(const void *data, unsigned long len,
|
||||
const void *raw_pkcs7, size_t pkcs7_len,
|
||||
enum key_being_used_for usage)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
|
||||
if (IS_ERR(pkcs7))
|
||||
return PTR_ERR(pkcs7);
|
||||
|
||||
/* The data should be detached - so we need to supply it. */
|
||||
if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
|
||||
pr_err("PKCS#7 signature with non-detached data\n");
|
||||
ret = -EBADMSG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = pkcs7_verify(pkcs7, usage);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (!trusted) {
|
||||
pr_err("PKCS#7 signature not signed with a trusted key\n");
|
||||
ret = -ENOKEY;
|
||||
}
|
||||
|
||||
error:
|
||||
pkcs7_free_message(pkcs7);
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(system_verify_data);
|
||||
|
||||
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
|
||||
|
|
|
@ -16,9 +16,13 @@ hostprogs-$(CONFIG_VT) += conmakehash
|
|||
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
|
||||
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
|
||||
hostprogs-$(CONFIG_ASN1) += asn1_compiler
|
||||
hostprogs-$(CONFIG_MODULE_SIG) += sign-file
|
||||
hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
|
||||
|
||||
HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
|
||||
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
|
||||
HOSTLOADLIBES_sign-file = -lcrypto
|
||||
HOSTLOADLIBES_extract-cert = -lcrypto
|
||||
|
||||
always := $(hostprogs-y) $(hostprogs-m)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ quiet_cmd_modules_install = INSTALL $@
|
|||
mkdir -p $(2) ; \
|
||||
cp $@ $(2) ; \
|
||||
$(mod_strip_cmd) $(2)/$(notdir $@) ; \
|
||||
$(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) ; \
|
||||
$(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) && \
|
||||
$(mod_compress_cmd) $(2)/$(notdir $@)
|
||||
|
||||
# Modules built outside the kernel source tree go into extra by default
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
@ -293,8 +294,8 @@ static const char *const directives[NR__DIRECTIVES] = {
|
|||
|
||||
struct action {
|
||||
struct action *next;
|
||||
char *name;
|
||||
unsigned char index;
|
||||
char name[];
|
||||
};
|
||||
|
||||
static struct action *action_list;
|
||||
|
@ -305,15 +306,17 @@ struct token {
|
|||
enum token_type token_type : 8;
|
||||
unsigned char size;
|
||||
struct action *action;
|
||||
const char *value;
|
||||
char *content;
|
||||
struct type *type;
|
||||
};
|
||||
|
||||
static struct token *token_list;
|
||||
static unsigned nr_tokens;
|
||||
static _Bool verbose;
|
||||
static bool verbose_opt;
|
||||
static bool debug_opt;
|
||||
|
||||
#define debug(fmt, ...) do { if (verbose) printf(fmt, ## __VA_ARGS__); } while (0)
|
||||
#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0)
|
||||
#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
|
||||
|
||||
static int directive_compare(const void *_key, const void *_pdir)
|
||||
{
|
||||
|
@ -325,11 +328,9 @@ static int directive_compare(const void *_key, const void *_pdir)
|
|||
dlen = strlen(dir);
|
||||
clen = (dlen < token->size) ? dlen : token->size;
|
||||
|
||||
//debug("cmp(%*.*s,%s) = ",
|
||||
// (int)token->size, (int)token->size, token->value,
|
||||
// dir);
|
||||
//debug("cmp(%s,%s) = ", token->content, dir);
|
||||
|
||||
val = memcmp(token->value, dir, clen);
|
||||
val = memcmp(token->content, dir, clen);
|
||||
if (val != 0) {
|
||||
//debug("%d [cmp]\n", val);
|
||||
return val;
|
||||
|
@ -349,7 +350,7 @@ static int directive_compare(const void *_key, const void *_pdir)
|
|||
static void tokenise(char *buffer, char *end)
|
||||
{
|
||||
struct token *tokens;
|
||||
char *line, *nl, *p, *q;
|
||||
char *line, *nl, *start, *p, *q;
|
||||
unsigned tix, lineno;
|
||||
|
||||
/* Assume we're going to have half as many tokens as we have
|
||||
|
@ -408,11 +409,11 @@ static void tokenise(char *buffer, char *end)
|
|||
break;
|
||||
|
||||
tokens[tix].line = lineno;
|
||||
tokens[tix].value = p;
|
||||
start = p;
|
||||
|
||||
/* Handle string tokens */
|
||||
if (isalpha(*p)) {
|
||||
const char **dir;
|
||||
const char **dir, *start = p;
|
||||
|
||||
/* Can be a directive, type name or element
|
||||
* name. Find the end of the name.
|
||||
|
@ -423,10 +424,18 @@ static void tokenise(char *buffer, char *end)
|
|||
tokens[tix].size = q - p;
|
||||
p = q;
|
||||
|
||||
tokens[tix].content = malloc(tokens[tix].size + 1);
|
||||
if (!tokens[tix].content) {
|
||||
perror(NULL);
|
||||
exit(1);
|
||||
}
|
||||
memcpy(tokens[tix].content, start, tokens[tix].size);
|
||||
tokens[tix].content[tokens[tix].size] = 0;
|
||||
|
||||
/* If it begins with a lowercase letter then
|
||||
* it's an element name
|
||||
*/
|
||||
if (islower(tokens[tix].value[0])) {
|
||||
if (islower(tokens[tix].content[0])) {
|
||||
tokens[tix++].token_type = TOKEN_ELEMENT_NAME;
|
||||
continue;
|
||||
}
|
||||
|
@ -455,6 +464,13 @@ static void tokenise(char *buffer, char *end)
|
|||
q++;
|
||||
tokens[tix].size = q - p;
|
||||
p = q;
|
||||
tokens[tix].content = malloc(tokens[tix].size + 1);
|
||||
if (!tokens[tix].content) {
|
||||
perror(NULL);
|
||||
exit(1);
|
||||
}
|
||||
memcpy(tokens[tix].content, start, tokens[tix].size);
|
||||
tokens[tix].content[tokens[tix].size] = 0;
|
||||
tokens[tix++].token_type = TOKEN_NUMBER;
|
||||
continue;
|
||||
}
|
||||
|
@ -463,6 +479,7 @@ static void tokenise(char *buffer, char *end)
|
|||
if (memcmp(p, "::=", 3) == 0) {
|
||||
p += 3;
|
||||
tokens[tix].size = 3;
|
||||
tokens[tix].content = "::=";
|
||||
tokens[tix++].token_type = TOKEN_ASSIGNMENT;
|
||||
continue;
|
||||
}
|
||||
|
@ -472,12 +489,14 @@ static void tokenise(char *buffer, char *end)
|
|||
if (memcmp(p, "({", 2) == 0) {
|
||||
p += 2;
|
||||
tokens[tix].size = 2;
|
||||
tokens[tix].content = "({";
|
||||
tokens[tix++].token_type = TOKEN_OPEN_ACTION;
|
||||
continue;
|
||||
}
|
||||
if (memcmp(p, "})", 2) == 0) {
|
||||
p += 2;
|
||||
tokens[tix].size = 2;
|
||||
tokens[tix].content = "})";
|
||||
tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
|
||||
continue;
|
||||
}
|
||||
|
@ -488,22 +507,27 @@ static void tokenise(char *buffer, char *end)
|
|||
switch (*p) {
|
||||
case '{':
|
||||
p += 1;
|
||||
tokens[tix].content = "{";
|
||||
tokens[tix++].token_type = TOKEN_OPEN_CURLY;
|
||||
continue;
|
||||
case '}':
|
||||
p += 1;
|
||||
tokens[tix].content = "}";
|
||||
tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
|
||||
continue;
|
||||
case '[':
|
||||
p += 1;
|
||||
tokens[tix].content = "[";
|
||||
tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
|
||||
continue;
|
||||
case ']':
|
||||
p += 1;
|
||||
tokens[tix].content = "]";
|
||||
tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
|
||||
continue;
|
||||
case ',':
|
||||
p += 1;
|
||||
tokens[tix].content = ",";
|
||||
tokens[tix++].token_type = TOKEN_COMMA;
|
||||
continue;
|
||||
default:
|
||||
|
@ -518,22 +542,20 @@ static void tokenise(char *buffer, char *end)
|
|||
}
|
||||
|
||||
nr_tokens = tix;
|
||||
debug("Extracted %u tokens\n", nr_tokens);
|
||||
verbose("Extracted %u tokens\n", nr_tokens);
|
||||
|
||||
#if 0
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < nr_tokens; n++)
|
||||
debug("Token %3u: '%*.*s'\n",
|
||||
n,
|
||||
(int)token_list[n].size, (int)token_list[n].size,
|
||||
token_list[n].value);
|
||||
debug("Token %3u: '%s'\n", n, token_list[n].content);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void build_type_list(void);
|
||||
static void parse(void);
|
||||
static void dump_elements(void);
|
||||
static void render(FILE *out, FILE *hdr);
|
||||
|
||||
/*
|
||||
|
@ -548,16 +570,27 @@ int main(int argc, char **argv)
|
|||
char *kbuild_verbose;
|
||||
int fd;
|
||||
|
||||
kbuild_verbose = getenv("KBUILD_VERBOSE");
|
||||
if (kbuild_verbose)
|
||||
verbose_opt = atoi(kbuild_verbose);
|
||||
|
||||
while (argc > 4) {
|
||||
if (strcmp(argv[1], "-v") == 0)
|
||||
verbose_opt = true;
|
||||
else if (strcmp(argv[1], "-d") == 0)
|
||||
debug_opt = true;
|
||||
else
|
||||
break;
|
||||
memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *));
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Format: %s <grammar-file> <c-file> <hdr-file>\n",
|
||||
fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n",
|
||||
argv[0]);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
kbuild_verbose = getenv("KBUILD_VERBOSE");
|
||||
if (kbuild_verbose)
|
||||
verbose = atoi(kbuild_verbose);
|
||||
|
||||
filename = argv[1];
|
||||
outputname = argv[2];
|
||||
headername = argv[3];
|
||||
|
@ -608,6 +641,7 @@ int main(int argc, char **argv)
|
|||
tokenise(buffer, buffer + readlen);
|
||||
build_type_list();
|
||||
parse();
|
||||
dump_elements();
|
||||
|
||||
out = fopen(outputname, "w");
|
||||
if (!out) {
|
||||
|
@ -693,7 +727,7 @@ static int type_index_compare(const void *_a, const void *_b)
|
|||
if ((*a)->name->size != (*b)->name->size)
|
||||
return (*a)->name->size - (*b)->name->size;
|
||||
else
|
||||
return memcmp((*a)->name->value, (*b)->name->value,
|
||||
return memcmp((*a)->name->content, (*b)->name->content,
|
||||
(*a)->name->size);
|
||||
}
|
||||
|
||||
|
@ -706,7 +740,7 @@ static int type_finder(const void *_key, const void *_ti)
|
|||
if (token->size != type->name->size)
|
||||
return token->size - type->name->size;
|
||||
else
|
||||
return memcmp(token->value, type->name->value,
|
||||
return memcmp(token->content, type->name->content,
|
||||
token->size);
|
||||
}
|
||||
|
||||
|
@ -756,14 +790,11 @@ static void build_type_list(void)
|
|||
|
||||
qsort(type_index, nr, sizeof(type_index[0]), type_index_compare);
|
||||
|
||||
debug("Extracted %u types\n", nr_types);
|
||||
verbose("Extracted %u types\n", nr_types);
|
||||
#if 0
|
||||
for (n = 0; n < nr_types; n++) {
|
||||
struct type *type = type_index[n];
|
||||
debug("- %*.*s\n",
|
||||
(int)type->name->size,
|
||||
(int)type->name->size,
|
||||
type->name->value);
|
||||
debug("- %*.*s\n", type->name->content);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -793,15 +824,14 @@ static void parse(void)
|
|||
type->element->type_def = type;
|
||||
|
||||
if (cursor != type[1].name) {
|
||||
fprintf(stderr, "%s:%d: Parse error at token '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Parse error at token '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} while (type++, !(type->flags & TYPE_STOP_MARKER));
|
||||
|
||||
debug("Extracted %u actions\n", nr_actions);
|
||||
verbose("Extracted %u actions\n", nr_actions);
|
||||
}
|
||||
|
||||
static struct element *element_list;
|
||||
|
@ -862,34 +892,31 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
|
|||
cursor++;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s:%d: Unrecognised tag class token '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (cursor >= end)
|
||||
goto overrun_error;
|
||||
if (cursor->token_type != TOKEN_NUMBER) {
|
||||
fprintf(stderr, "%s:%d: Missing tag number '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Missing tag number '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
element->tag &= ~0x1f;
|
||||
element->tag |= strtoul(cursor->value, &p, 10);
|
||||
element->tag |= strtoul(cursor->content, &p, 10);
|
||||
element->flags |= ELEMENT_TAG_SPECIFIED;
|
||||
if (p - cursor->value != cursor->size)
|
||||
if (p - cursor->content != cursor->size)
|
||||
abort();
|
||||
cursor++;
|
||||
|
||||
if (cursor >= end)
|
||||
goto overrun_error;
|
||||
if (cursor->token_type != TOKEN_CLOSE_SQUARE) {
|
||||
fprintf(stderr, "%s:%d: Missing closing square bracket '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
cursor++;
|
||||
|
@ -989,9 +1016,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
|
|||
ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]),
|
||||
type_finder);
|
||||
if (!ref) {
|
||||
fprintf(stderr, "%s:%d: Type '%*.*s' undefined\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Type '%s' undefined\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
cursor->type = *ref;
|
||||
|
@ -1040,9 +1066,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
|
|||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s:%d: Token '%*.*s' does not introduce a type\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -1059,20 +1084,18 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
|
|||
if (cursor >= end)
|
||||
goto overrun_error;
|
||||
if (cursor->token_type != TOKEN_ELEMENT_NAME) {
|
||||
fprintf(stderr, "%s:%d: Token '%*.*s' is not an action function name\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
action = malloc(sizeof(struct action) + cursor->size + 1);
|
||||
action = malloc(sizeof(struct action));
|
||||
if (!action) {
|
||||
perror(NULL);
|
||||
exit(1);
|
||||
}
|
||||
action->index = 0;
|
||||
memcpy(action->name, cursor->value, cursor->size);
|
||||
action->name[cursor->size] = 0;
|
||||
action->name = cursor->content;
|
||||
|
||||
for (ppaction = &action_list;
|
||||
*ppaction;
|
||||
|
@ -1102,9 +1125,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
|
|||
if (cursor >= end)
|
||||
goto overrun_error;
|
||||
if (cursor->token_type != TOKEN_CLOSE_ACTION) {
|
||||
fprintf(stderr, "%s:%d: Missing close action, got '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Missing close action, got '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
cursor++;
|
||||
|
@ -1114,9 +1136,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
|
|||
return top;
|
||||
|
||||
parse_error:
|
||||
fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Unexpected token '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
|
||||
overrun_error:
|
||||
|
@ -1134,9 +1155,8 @@ static struct element *parse_compound(struct token **_cursor, struct token *end,
|
|||
struct token *cursor = *_cursor, *name;
|
||||
|
||||
if (cursor->token_type != TOKEN_OPEN_CURLY) {
|
||||
fprintf(stderr, "%s:%d: Expected compound to start with brace not '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
cursor++;
|
||||
|
@ -1177,9 +1197,8 @@ static struct element *parse_compound(struct token **_cursor, struct token *end,
|
|||
children->flags &= ~ELEMENT_CONDITIONAL;
|
||||
|
||||
if (cursor->token_type != TOKEN_CLOSE_CURLY) {
|
||||
fprintf(stderr, "%s:%d: Expected compound closure, got '%*.*s'\n",
|
||||
filename, cursor->line,
|
||||
(int)cursor->size, (int)cursor->size, cursor->value);
|
||||
fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n",
|
||||
filename, cursor->line, cursor->content);
|
||||
exit(1);
|
||||
}
|
||||
cursor++;
|
||||
|
@ -1192,6 +1211,52 @@ overrun_error:
|
|||
exit(1);
|
||||
}
|
||||
|
||||
static void dump_element(const struct element *e, int level)
|
||||
{
|
||||
const struct element *c;
|
||||
const struct type *t = e->type_def;
|
||||
const char *name = e->name ? e->name->content : ".";
|
||||
const char *tname = t && t->name ? t->name->content : ".";
|
||||
char tag[32];
|
||||
|
||||
if (e->class == 0 && e->method == 0 && e->tag == 0)
|
||||
strcpy(tag, "<...>");
|
||||
else if (e->class == ASN1_UNIV)
|
||||
sprintf(tag, "%s %s %s",
|
||||
asn1_classes[e->class],
|
||||
asn1_methods[e->method],
|
||||
asn1_universal_tags[e->tag]);
|
||||
else
|
||||
sprintf(tag, "%s %s %u",
|
||||
asn1_classes[e->class],
|
||||
asn1_methods[e->method],
|
||||
e->tag);
|
||||
|
||||
printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n",
|
||||
e->flags & ELEMENT_IMPLICIT ? 'I' : '-',
|
||||
e->flags & ELEMENT_EXPLICIT ? 'E' : '-',
|
||||
e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-',
|
||||
e->flags & ELEMENT_SKIPPABLE ? 'S' : '-',
|
||||
e->flags & ELEMENT_CONDITIONAL ? 'C' : '-',
|
||||
"-tTqQcaro"[e->compound],
|
||||
level, "",
|
||||
tag,
|
||||
tname,
|
||||
name,
|
||||
e->action ? e->action->name : "");
|
||||
if (e->compound == TYPE_REF)
|
||||
dump_element(e->type->type->element, level + 3);
|
||||
else
|
||||
for (c = e->children; c; c = c->next)
|
||||
dump_element(c, level + 3);
|
||||
}
|
||||
|
||||
static void dump_elements(void)
|
||||
{
|
||||
if (debug_opt)
|
||||
dump_element(type_list[0].element, 0);
|
||||
}
|
||||
|
||||
static void render_element(FILE *out, struct element *e, struct element *tag);
|
||||
static void render_out_of_line_list(FILE *out);
|
||||
|
||||
|
@ -1293,7 +1358,7 @@ static void render(FILE *out, FILE *hdr)
|
|||
}
|
||||
|
||||
/* We do two passes - the first one calculates all the offsets */
|
||||
debug("Pass 1\n");
|
||||
verbose("Pass 1\n");
|
||||
nr_entries = 0;
|
||||
root = &type_list[0];
|
||||
render_element(NULL, root->element, NULL);
|
||||
|
@ -1304,7 +1369,7 @@ static void render(FILE *out, FILE *hdr)
|
|||
e->flags &= ~ELEMENT_RENDERED;
|
||||
|
||||
/* And then we actually render */
|
||||
debug("Pass 2\n");
|
||||
verbose("Pass 2\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "static const unsigned char %s_machine[] = {\n",
|
||||
grammar_name);
|
||||
|
@ -1390,9 +1455,7 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
|
|||
outofline = 1;
|
||||
|
||||
if (e->type_def && out) {
|
||||
render_more(out, "\t// %*.*s\n",
|
||||
(int)e->type_def->name->size, (int)e->type_def->name->size,
|
||||
e->type_def->name->value);
|
||||
render_more(out, "\t// %s\n", e->type_def->name->content);
|
||||
}
|
||||
|
||||
/* Render the operation */
|
||||
|
@ -1404,9 +1467,7 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
|
|||
render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,",
|
||||
cond, act, skippable ? "_OR_SKIP" : "");
|
||||
if (e->name)
|
||||
render_more(out, "\t\t// %*.*s",
|
||||
(int)e->name->size, (int)e->name->size,
|
||||
e->name->value);
|
||||
render_more(out, "\t\t// %s", e->name->content);
|
||||
render_more(out, "\n");
|
||||
goto dont_render_tag;
|
||||
|
||||
|
@ -1439,9 +1500,7 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
|
|||
|
||||
x = tag ?: e;
|
||||
if (x->name)
|
||||
render_more(out, "\t\t// %*.*s",
|
||||
(int)x->name->size, (int)x->name->size,
|
||||
x->name->value);
|
||||
render_more(out, "\t\t// %s", x->name->content);
|
||||
render_more(out, "\n");
|
||||
|
||||
/* Render the tag */
|
||||
|
@ -1479,10 +1538,8 @@ dont_render_tag:
|
|||
* skipability */
|
||||
render_opcode(out, "_jump_target(%u),", e->entry_index);
|
||||
if (e->type_def && e->type_def->name)
|
||||
render_more(out, "\t\t// --> %*.*s",
|
||||
(int)e->type_def->name->size,
|
||||
(int)e->type_def->name->size,
|
||||
e->type_def->name->value);
|
||||
render_more(out, "\t\t// --> %s",
|
||||
e->type_def->name->content);
|
||||
render_more(out, "\n");
|
||||
if (!(e->flags & ELEMENT_RENDERED)) {
|
||||
e->flags |= ELEMENT_RENDERED;
|
||||
|
@ -1507,10 +1564,8 @@ dont_render_tag:
|
|||
* skipability */
|
||||
render_opcode(out, "_jump_target(%u),", e->entry_index);
|
||||
if (e->type_def && e->type_def->name)
|
||||
render_more(out, "\t\t// --> %*.*s",
|
||||
(int)e->type_def->name->size,
|
||||
(int)e->type_def->name->size,
|
||||
e->type_def->name->value);
|
||||
render_more(out, "\t\t// --> %s",
|
||||
e->type_def->name->content);
|
||||
render_more(out, "\n");
|
||||
if (!(e->flags & ELEMENT_RENDERED)) {
|
||||
e->flags |= ELEMENT_RENDERED;
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/* Extract X.509 certificate in DER form from PKCS#11 or PEM.
|
||||
*
|
||||
* Copyright © 2014 Red Hat, Inc. All Rights Reserved.
|
||||
* Copyright © 2015 Intel Corporation.
|
||||
*
|
||||
* Authors: David Howells <dhowells@redhat.com>
|
||||
* David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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 _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <err.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/pkcs7.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/engine.h>
|
||||
|
||||
#define PKEY_ID_PKCS7 2
|
||||
|
||||
static __attribute__((noreturn))
|
||||
void format(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: scripts/extract-cert <source> <dest>\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void display_openssl_errors(int l)
|
||||
{
|
||||
const char *file;
|
||||
char buf[120];
|
||||
int e, line;
|
||||
|
||||
if (ERR_peek_error() == 0)
|
||||
return;
|
||||
fprintf(stderr, "At main.c:%d:\n", l);
|
||||
|
||||
while ((e = ERR_get_error_line(&file, &line))) {
|
||||
ERR_error_string(e, buf);
|
||||
fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
static void drain_openssl_errors(void)
|
||||
{
|
||||
const char *file;
|
||||
int line;
|
||||
|
||||
if (ERR_peek_error() == 0)
|
||||
return;
|
||||
while (ERR_get_error_line(&file, &line)) {}
|
||||
}
|
||||
|
||||
#define ERR(cond, fmt, ...) \
|
||||
do { \
|
||||
bool __cond = (cond); \
|
||||
display_openssl_errors(__LINE__); \
|
||||
if (__cond) { \
|
||||
err(1, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static const char *key_pass;
|
||||
static BIO *wb;
|
||||
static char *cert_dst;
|
||||
int kbuild_verbose;
|
||||
|
||||
static void write_cert(X509 *x509)
|
||||
{
|
||||
char buf[200];
|
||||
|
||||
if (!wb) {
|
||||
wb = BIO_new_file(cert_dst, "wb");
|
||||
ERR(!wb, "%s", cert_dst);
|
||||
}
|
||||
X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf));
|
||||
ERR(!i2d_X509_bio(wb, x509), cert_dst);
|
||||
if (kbuild_verbose)
|
||||
fprintf(stderr, "Extracted cert: %s\n", buf);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *cert_src;
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
ERR_clear_error();
|
||||
|
||||
kbuild_verbose = atoi(getenv("KBUILD_VERBOSE")?:"0");
|
||||
|
||||
key_pass = getenv("KBUILD_SIGN_PIN");
|
||||
|
||||
if (argc != 3)
|
||||
format();
|
||||
|
||||
cert_src = argv[1];
|
||||
cert_dst = argv[2];
|
||||
|
||||
if (!cert_src[0]) {
|
||||
/* Invoked with no input; create empty file */
|
||||
FILE *f = fopen(cert_dst, "wb");
|
||||
ERR(!f, "%s", cert_dst);
|
||||
fclose(f);
|
||||
exit(0);
|
||||
} else if (!strncmp(cert_src, "pkcs11:", 7)) {
|
||||
ENGINE *e;
|
||||
struct {
|
||||
const char *cert_id;
|
||||
X509 *cert;
|
||||
} parms;
|
||||
|
||||
parms.cert_id = cert_src;
|
||||
parms.cert = NULL;
|
||||
|
||||
ENGINE_load_builtin_engines();
|
||||
drain_openssl_errors();
|
||||
e = ENGINE_by_id("pkcs11");
|
||||
ERR(!e, "Load PKCS#11 ENGINE");
|
||||
if (ENGINE_init(e))
|
||||
drain_openssl_errors();
|
||||
else
|
||||
ERR(1, "ENGINE_init");
|
||||
if (key_pass)
|
||||
ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
|
||||
ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1);
|
||||
ERR(!parms.cert, "Get X.509 from PKCS#11");
|
||||
write_cert(parms.cert);
|
||||
} else {
|
||||
BIO *b;
|
||||
X509 *x509;
|
||||
|
||||
b = BIO_new_file(cert_src, "rb");
|
||||
ERR(!b, "%s", cert_src);
|
||||
|
||||
while (1) {
|
||||
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
|
||||
if (wb && !x509) {
|
||||
unsigned long err = ERR_peek_last_error();
|
||||
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
|
||||
ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
|
||||
ERR_clear_error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ERR(!x509, "%s", cert_src);
|
||||
write_cert(x509);
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free(wb);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,421 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
#
|
||||
# Sign a module file using the given key.
|
||||
#
|
||||
|
||||
my $USAGE =
|
||||
"Usage: scripts/sign-file [-v] <hash algo> <key> <x509> <module> [<dest>]\n" .
|
||||
" scripts/sign-file [-v] -s <raw sig> <hash algo> <x509> <module> [<dest>]\n";
|
||||
|
||||
use strict;
|
||||
use FileHandle;
|
||||
use IPC::Open2;
|
||||
use Getopt::Std;
|
||||
|
||||
my %opts;
|
||||
getopts('vs:', \%opts) or die $USAGE;
|
||||
my $verbose = $opts{'v'};
|
||||
my $signature_file = $opts{'s'};
|
||||
|
||||
die $USAGE if ($#ARGV > 4);
|
||||
die $USAGE if (!$signature_file && $#ARGV < 3 || $signature_file && $#ARGV < 2);
|
||||
|
||||
my $dgst = shift @ARGV;
|
||||
my $private_key;
|
||||
if (!$signature_file) {
|
||||
$private_key = shift @ARGV;
|
||||
}
|
||||
my $x509 = shift @ARGV;
|
||||
my $module = shift @ARGV;
|
||||
my ($dest, $keep_orig);
|
||||
if (@ARGV) {
|
||||
$dest = $ARGV[0];
|
||||
$keep_orig = 1;
|
||||
} else {
|
||||
$dest = $module . "~";
|
||||
}
|
||||
|
||||
die "Can't read private key\n" if (!$signature_file && !-r $private_key);
|
||||
die "Can't read signature file\n" if ($signature_file && !-r $signature_file);
|
||||
die "Can't read X.509 certificate\n" unless (-r $x509);
|
||||
die "Can't read module\n" unless (-r $module);
|
||||
|
||||
#
|
||||
# Function to read the contents of a file into a variable.
|
||||
#
|
||||
sub read_file($)
|
||||
{
|
||||
my ($file) = @_;
|
||||
my $contents;
|
||||
my $len;
|
||||
|
||||
open(FD, "<$file") || die $file;
|
||||
binmode FD;
|
||||
my @st = stat(FD);
|
||||
die $file if (!@st);
|
||||
$len = read(FD, $contents, $st[7]) || die $file;
|
||||
close(FD) || die $file;
|
||||
die "$file: Wanted length ", $st[7], ", got ", $len, "\n"
|
||||
if ($len != $st[7]);
|
||||
return $contents;
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# First of all, we have to parse the X.509 certificate to find certain details
|
||||
# about it.
|
||||
#
|
||||
# We read the DER-encoded X509 certificate and parse it to extract the Subject
|
||||
# name and Subject Key Identifier. Theis provides the data we need to build
|
||||
# the certificate identifier.
|
||||
#
|
||||
# The signer's name part of the identifier is fabricated from the commonName,
|
||||
# the organizationName or the emailAddress components of the X.509 subject
|
||||
# name.
|
||||
#
|
||||
# The subject key ID is used to select which of that signer's certificates
|
||||
# we're intending to use to sign the module.
|
||||
#
|
||||
###############################################################################
|
||||
my $x509_certificate = read_file($x509);
|
||||
|
||||
my $UNIV = 0 << 6;
|
||||
my $APPL = 1 << 6;
|
||||
my $CONT = 2 << 6;
|
||||
my $PRIV = 3 << 6;
|
||||
|
||||
my $CONS = 0x20;
|
||||
|
||||
my $BOOLEAN = 0x01;
|
||||
my $INTEGER = 0x02;
|
||||
my $BIT_STRING = 0x03;
|
||||
my $OCTET_STRING = 0x04;
|
||||
my $NULL = 0x05;
|
||||
my $OBJ_ID = 0x06;
|
||||
my $UTF8String = 0x0c;
|
||||
my $SEQUENCE = 0x10;
|
||||
my $SET = 0x11;
|
||||
my $UTCTime = 0x17;
|
||||
my $GeneralizedTime = 0x18;
|
||||
|
||||
my %OIDs = (
|
||||
pack("CCC", 85, 4, 3) => "commonName",
|
||||
pack("CCC", 85, 4, 6) => "countryName",
|
||||
pack("CCC", 85, 4, 10) => "organizationName",
|
||||
pack("CCC", 85, 4, 11) => "organizationUnitName",
|
||||
pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption",
|
||||
pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption",
|
||||
pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress",
|
||||
pack("CCC", 85, 29, 35) => "authorityKeyIdentifier",
|
||||
pack("CCC", 85, 29, 14) => "subjectKeyIdentifier",
|
||||
pack("CCC", 85, 29, 19) => "basicConstraints"
|
||||
);
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Extract an ASN.1 element from a string and return information about it.
|
||||
#
|
||||
###############################################################################
|
||||
sub asn1_extract($$@)
|
||||
{
|
||||
my ($cursor, $expected_tag, $optional) = @_;
|
||||
|
||||
return [ -1 ]
|
||||
if ($cursor->[1] == 0 && $optional);
|
||||
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n"
|
||||
if ($cursor->[1] < 2);
|
||||
|
||||
my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2));
|
||||
|
||||
if ($expected_tag != -1 && $tag != $expected_tag) {
|
||||
return [ -1 ]
|
||||
if ($optional);
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag,
|
||||
" not ", $expected_tag, ")\n";
|
||||
}
|
||||
|
||||
$cursor->[0] += 2;
|
||||
$cursor->[1] -= 2;
|
||||
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 long tag\n"
|
||||
if (($tag & 0x1f) == 0x1f);
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 indefinite length\n"
|
||||
if ($len == 0x80);
|
||||
|
||||
if ($len > 0x80) {
|
||||
my $l = $len - 0x80;
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n"
|
||||
if ($cursor->[1] < $l);
|
||||
|
||||
if ($l == 0x1) {
|
||||
$len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1));
|
||||
} elsif ($l == 0x2) {
|
||||
$len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2));
|
||||
} elsif ($l == 0x3) {
|
||||
$len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16;
|
||||
$len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2));
|
||||
} elsif ($l == 0x4) {
|
||||
$len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4));
|
||||
} else {
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n";
|
||||
}
|
||||
|
||||
$cursor->[0] += $l;
|
||||
$cursor->[1] -= $l;
|
||||
}
|
||||
|
||||
die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n"
|
||||
if ($cursor->[1] < $len);
|
||||
|
||||
my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ];
|
||||
$cursor->[0] += $len;
|
||||
$cursor->[1] -= $len;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Retrieve the data referred to by a cursor
|
||||
#
|
||||
###############################################################################
|
||||
sub asn1_retrieve($)
|
||||
{
|
||||
my ($cursor) = @_;
|
||||
my ($offset, $len, $data) = @$cursor;
|
||||
return substr($$data, $offset, $len);
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Roughly parse the X.509 certificate
|
||||
#
|
||||
###############################################################################
|
||||
my $cursor = [ 0, length($x509_certificate), \$x509_certificate ];
|
||||
|
||||
my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE);
|
||||
my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1);
|
||||
my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER);
|
||||
my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1);
|
||||
my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1);
|
||||
my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1);
|
||||
|
||||
my $subject_key_id = ();
|
||||
my $authority_key_id = ();
|
||||
|
||||
#
|
||||
# Parse the extension list
|
||||
#
|
||||
if ($extension_list->[0] != -1) {
|
||||
my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
|
||||
while ($extensions->[1]->[1] > 0) {
|
||||
my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID);
|
||||
my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1);
|
||||
my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING);
|
||||
|
||||
my $raw_oid = asn1_retrieve($x_oid->[1]);
|
||||
next if (!exists($OIDs{$raw_oid}));
|
||||
my $x_type = $OIDs{$raw_oid};
|
||||
|
||||
my $raw_value = asn1_retrieve($x_val->[1]);
|
||||
|
||||
if ($x_type eq "subjectKeyIdentifier") {
|
||||
my $vcursor = [ 0, length($raw_value), \$raw_value ];
|
||||
|
||||
$subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Determine what we're going to use as the signer's name. In order of
|
||||
# preference, take one of: commonName, organizationName or emailAddress.
|
||||
#
|
||||
###############################################################################
|
||||
my $org = "";
|
||||
my $cn = "";
|
||||
my $email = "";
|
||||
|
||||
while ($subject->[1]->[1] > 0) {
|
||||
my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET);
|
||||
my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE);
|
||||
my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID);
|
||||
my $n_val = asn1_extract($attr->[1], -1);
|
||||
|
||||
my $raw_oid = asn1_retrieve($n_oid->[1]);
|
||||
next if (!exists($OIDs{$raw_oid}));
|
||||
my $n_type = $OIDs{$raw_oid};
|
||||
|
||||
my $raw_value = asn1_retrieve($n_val->[1]);
|
||||
|
||||
if ($n_type eq "organizationName") {
|
||||
$org = $raw_value;
|
||||
} elsif ($n_type eq "commonName") {
|
||||
$cn = $raw_value;
|
||||
} elsif ($n_type eq "emailAddress") {
|
||||
$email = $raw_value;
|
||||
}
|
||||
}
|
||||
|
||||
my $signers_name = $email;
|
||||
|
||||
if ($org && $cn) {
|
||||
# Don't use the organizationName if the commonName repeats it
|
||||
if (length($org) <= length($cn) &&
|
||||
substr($cn, 0, length($org)) eq $org) {
|
||||
$signers_name = $cn;
|
||||
goto got_id_name;
|
||||
}
|
||||
|
||||
# Or a signifcant chunk of it
|
||||
if (length($org) >= 7 &&
|
||||
length($cn) >= 7 &&
|
||||
substr($cn, 0, 7) eq substr($org, 0, 7)) {
|
||||
$signers_name = $cn;
|
||||
goto got_id_name;
|
||||
}
|
||||
|
||||
$signers_name = $org . ": " . $cn;
|
||||
} elsif ($org) {
|
||||
$signers_name = $org;
|
||||
} elsif ($cn) {
|
||||
$signers_name = $cn;
|
||||
}
|
||||
|
||||
got_id_name:
|
||||
|
||||
die $x509, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n"
|
||||
if (!$subject_key_id);
|
||||
|
||||
my $key_identifier = asn1_retrieve($subject_key_id->[1]);
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Create and attach the module signature
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
# Signature parameters
|
||||
#
|
||||
my $algo = 1; # Public-key crypto algorithm: RSA
|
||||
my $hash = 0; # Digest algorithm
|
||||
my $id_type = 1; # Identifier type: X.509
|
||||
|
||||
#
|
||||
# Digest the data
|
||||
#
|
||||
my $prologue;
|
||||
if ($dgst eq "sha1") {
|
||||
$prologue = pack("C*",
|
||||
0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
|
||||
0x2B, 0x0E, 0x03, 0x02, 0x1A,
|
||||
0x05, 0x00, 0x04, 0x14);
|
||||
$hash = 2;
|
||||
} elsif ($dgst eq "sha224") {
|
||||
$prologue = pack("C*",
|
||||
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
|
||||
0x05, 0x00, 0x04, 0x1C);
|
||||
$hash = 7;
|
||||
} elsif ($dgst eq "sha256") {
|
||||
$prologue = pack("C*",
|
||||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
||||
0x05, 0x00, 0x04, 0x20);
|
||||
$hash = 4;
|
||||
} elsif ($dgst eq "sha384") {
|
||||
$prologue = pack("C*",
|
||||
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
|
||||
0x05, 0x00, 0x04, 0x30);
|
||||
$hash = 5;
|
||||
} elsif ($dgst eq "sha512") {
|
||||
$prologue = pack("C*",
|
||||
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
|
||||
0x05, 0x00, 0x04, 0x40);
|
||||
$hash = 6;
|
||||
} else {
|
||||
die "Unknown hash algorithm: $dgst\n";
|
||||
}
|
||||
|
||||
my $signature;
|
||||
if ($signature_file) {
|
||||
$signature = read_file($signature_file);
|
||||
} else {
|
||||
#
|
||||
# Generate the digest and read from openssl's stdout
|
||||
#
|
||||
my $digest;
|
||||
$digest = readpipe("openssl dgst -$dgst -binary $module") || die "openssl dgst";
|
||||
|
||||
#
|
||||
# Generate the binary signature, which will be just the integer that
|
||||
# comprises the signature with no metadata attached.
|
||||
#
|
||||
my $pid;
|
||||
$pid = open2(*read_from, *write_to,
|
||||
"openssl rsautl -sign -inkey $private_key -keyform PEM") ||
|
||||
die "openssl rsautl";
|
||||
binmode write_to;
|
||||
print write_to $prologue . $digest || die "pipe to openssl rsautl";
|
||||
close(write_to) || die "pipe to openssl rsautl";
|
||||
|
||||
binmode read_from;
|
||||
read(read_from, $signature, 4096) || die "pipe from openssl rsautl";
|
||||
close(read_from) || die "pipe from openssl rsautl";
|
||||
waitpid($pid, 0) || die;
|
||||
die "openssl rsautl died: $?" if ($? >> 8);
|
||||
}
|
||||
$signature = pack("n", length($signature)) . $signature,
|
||||
|
||||
#
|
||||
# Build the signed binary
|
||||
#
|
||||
my $unsigned_module = read_file($module);
|
||||
|
||||
my $magic_number = "~Module signature appended~\n";
|
||||
|
||||
my $info = pack("CCCCCxxxN",
|
||||
$algo, $hash, $id_type,
|
||||
length($signers_name),
|
||||
length($key_identifier),
|
||||
length($signature));
|
||||
|
||||
if ($verbose) {
|
||||
print "Size of unsigned module: ", length($unsigned_module), "\n";
|
||||
print "Size of signer's name : ", length($signers_name), "\n";
|
||||
print "Size of key identifier : ", length($key_identifier), "\n";
|
||||
print "Size of signature : ", length($signature), "\n";
|
||||
print "Size of information : ", length($info), "\n";
|
||||
print "Size of magic number : ", length($magic_number), "\n";
|
||||
print "Signer's name : '", $signers_name, "'\n";
|
||||
print "Digest : $dgst\n";
|
||||
}
|
||||
|
||||
open(FD, ">$dest") || die $dest;
|
||||
binmode FD;
|
||||
print FD
|
||||
$unsigned_module,
|
||||
$signers_name,
|
||||
$key_identifier,
|
||||
$signature,
|
||||
$info,
|
||||
$magic_number
|
||||
;
|
||||
close FD || die $dest;
|
||||
|
||||
if (!$keep_orig) {
|
||||
rename($dest, $module) || die $module;
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
/* Sign a module file using the given key.
|
||||
*
|
||||
* Copyright (C) 2014 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 _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <err.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/cms.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/engine.h>
|
||||
|
||||
struct module_signature {
|
||||
uint8_t algo; /* Public-key crypto algorithm [0] */
|
||||
uint8_t hash; /* Digest algorithm [0] */
|
||||
uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
||||
uint8_t signer_len; /* Length of signer's name [0] */
|
||||
uint8_t key_id_len; /* Length of key identifier [0] */
|
||||
uint8_t __pad[3];
|
||||
uint32_t sig_len; /* Length of signature data */
|
||||
};
|
||||
|
||||
#define PKEY_ID_PKCS7 2
|
||||
|
||||
static char magic_number[] = "~Module signature appended~\n";
|
||||
|
||||
static __attribute__((noreturn))
|
||||
void format(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void display_openssl_errors(int l)
|
||||
{
|
||||
const char *file;
|
||||
char buf[120];
|
||||
int e, line;
|
||||
|
||||
if (ERR_peek_error() == 0)
|
||||
return;
|
||||
fprintf(stderr, "At main.c:%d:\n", l);
|
||||
|
||||
while ((e = ERR_get_error_line(&file, &line))) {
|
||||
ERR_error_string(e, buf);
|
||||
fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
static void drain_openssl_errors(void)
|
||||
{
|
||||
const char *file;
|
||||
int line;
|
||||
|
||||
if (ERR_peek_error() == 0)
|
||||
return;
|
||||
while (ERR_get_error_line(&file, &line)) {}
|
||||
}
|
||||
|
||||
#define ERR(cond, fmt, ...) \
|
||||
do { \
|
||||
bool __cond = (cond); \
|
||||
display_openssl_errors(__LINE__); \
|
||||
if (__cond) { \
|
||||
err(1, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static const char *key_pass;
|
||||
|
||||
static int pem_pw_cb(char *buf, int len, int w, void *v)
|
||||
{
|
||||
int pwlen;
|
||||
|
||||
if (!key_pass)
|
||||
return -1;
|
||||
|
||||
pwlen = strlen(key_pass);
|
||||
if (pwlen >= len)
|
||||
return -1;
|
||||
|
||||
strcpy(buf, key_pass);
|
||||
|
||||
/* If it's wrong, don't keep trying it. */
|
||||
key_pass = NULL;
|
||||
|
||||
return pwlen;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
|
||||
char *hash_algo = NULL;
|
||||
char *private_key_name, *x509_name, *module_name, *dest_name;
|
||||
bool save_cms = false, replace_orig;
|
||||
bool sign_only = false;
|
||||
unsigned char buf[4096];
|
||||
unsigned long module_size, cms_size;
|
||||
unsigned int use_keyid = 0, use_signed_attrs = CMS_NOATTR;
|
||||
const EVP_MD *digest_algo;
|
||||
EVP_PKEY *private_key;
|
||||
CMS_ContentInfo *cms;
|
||||
X509 *x509;
|
||||
BIO *b, *bd = NULL, *bm;
|
||||
int opt, n;
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
ERR_clear_error();
|
||||
|
||||
key_pass = getenv("KBUILD_SIGN_PIN");
|
||||
|
||||
do {
|
||||
opt = getopt(argc, argv, "dpk");
|
||||
switch (opt) {
|
||||
case 'p': save_cms = true; break;
|
||||
case 'd': sign_only = true; save_cms = true; break;
|
||||
case 'k': use_keyid = CMS_USE_KEYID; break;
|
||||
case -1: break;
|
||||
default: format();
|
||||
}
|
||||
} while (opt != -1);
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc < 4 || argc > 5)
|
||||
format();
|
||||
|
||||
hash_algo = argv[0];
|
||||
private_key_name = argv[1];
|
||||
x509_name = argv[2];
|
||||
module_name = argv[3];
|
||||
if (argc == 5) {
|
||||
dest_name = argv[4];
|
||||
replace_orig = false;
|
||||
} else {
|
||||
ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0,
|
||||
"asprintf");
|
||||
replace_orig = true;
|
||||
}
|
||||
|
||||
/* Read the private key and the X.509 cert the PKCS#7 message
|
||||
* will point to.
|
||||
*/
|
||||
if (!strncmp(private_key_name, "pkcs11:", 7)) {
|
||||
ENGINE *e;
|
||||
|
||||
ENGINE_load_builtin_engines();
|
||||
drain_openssl_errors();
|
||||
e = ENGINE_by_id("pkcs11");
|
||||
ERR(!e, "Load PKCS#11 ENGINE");
|
||||
if (ENGINE_init(e))
|
||||
drain_openssl_errors();
|
||||
else
|
||||
ERR(1, "ENGINE_init");
|
||||
if (key_pass)
|
||||
ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
|
||||
private_key = ENGINE_load_private_key(e, private_key_name, NULL,
|
||||
NULL);
|
||||
ERR(!private_key, "%s", private_key_name);
|
||||
} else {
|
||||
b = BIO_new_file(private_key_name, "rb");
|
||||
ERR(!b, "%s", private_key_name);
|
||||
private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, NULL);
|
||||
ERR(!private_key, "%s", private_key_name);
|
||||
BIO_free(b);
|
||||
}
|
||||
|
||||
b = BIO_new_file(x509_name, "rb");
|
||||
ERR(!b, "%s", x509_name);
|
||||
x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
|
||||
if (!x509) {
|
||||
ERR(BIO_reset(b) != 1, "%s", x509_name);
|
||||
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); /* PEM encoded X.509 */
|
||||
if (x509)
|
||||
drain_openssl_errors();
|
||||
}
|
||||
BIO_free(b);
|
||||
ERR(!x509, "%s", x509_name);
|
||||
|
||||
/* Open the destination file now so that we can shovel the module data
|
||||
* across as we read it.
|
||||
*/
|
||||
if (!sign_only) {
|
||||
bd = BIO_new_file(dest_name, "wb");
|
||||
ERR(!bd, "%s", dest_name);
|
||||
}
|
||||
|
||||
/* Digest the module data. */
|
||||
OpenSSL_add_all_digests();
|
||||
display_openssl_errors(__LINE__);
|
||||
digest_algo = EVP_get_digestbyname(hash_algo);
|
||||
ERR(!digest_algo, "EVP_get_digestbyname");
|
||||
|
||||
bm = BIO_new_file(module_name, "rb");
|
||||
ERR(!bm, "%s", module_name);
|
||||
|
||||
/* Load the CMS message from the digest buffer. */
|
||||
cms = CMS_sign(NULL, NULL, NULL, NULL,
|
||||
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM);
|
||||
ERR(!cms, "CMS_sign");
|
||||
|
||||
ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
|
||||
CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
|
||||
use_keyid | use_signed_attrs),
|
||||
"CMS_sign_add_signer");
|
||||
ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0,
|
||||
"CMS_final");
|
||||
|
||||
if (save_cms) {
|
||||
char *cms_name;
|
||||
|
||||
ERR(asprintf(&cms_name, "%s.p7s", module_name) < 0, "asprintf");
|
||||
b = BIO_new_file(cms_name, "wb");
|
||||
ERR(!b, "%s", cms_name);
|
||||
ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, "%s", cms_name);
|
||||
BIO_free(b);
|
||||
}
|
||||
|
||||
if (sign_only)
|
||||
return 0;
|
||||
|
||||
/* Append the marker and the PKCS#7 message to the destination file */
|
||||
ERR(BIO_reset(bm) < 0, "%s", module_name);
|
||||
while ((n = BIO_read(bm, buf, sizeof(buf))),
|
||||
n > 0) {
|
||||
ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
|
||||
}
|
||||
ERR(n < 0, "%s", module_name);
|
||||
module_size = BIO_number_written(bd);
|
||||
|
||||
ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name);
|
||||
cms_size = BIO_number_written(bd) - module_size;
|
||||
sig_info.sig_len = htonl(cms_size);
|
||||
ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
|
||||
ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
|
||||
|
||||
ERR(BIO_free(bd) < 0, "%s", dest_name);
|
||||
|
||||
/* Finally, if we're signing in place, replace the original. */
|
||||
if (replace_orig)
|
||||
ERR(rename(dest_name, module_name) < 0, "%s", dest_name);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue