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)
*/
#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>
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);
SignedData ::= SEQUENCE {
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
}
ContentInfo ::= SEQUENCE {
- contentType ContentType,
+ contentType ContentType ({ pkcs7_note_OID }),
content [0] EXPLICIT Data OPTIONAL
}
}
UnauthenticatedAttribute ::= SEQUENCE {
- type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ type OBJECT IDENTIFIER,
values SET OF ANY
}
#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"
+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;
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);
goto error;
}
- ret = pkcs7_verify(pkcs7);
+ ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error_free;
}
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
goto out;
}
+ ret = pkcs7_check_authattrs(ctx->msg);
+ if (ret < 0)
+ goto out;
+
msg = ctx->msg;
ctx->msg = NULL;
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.
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;
}
/*
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;
}
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 */
if (!ctx->expect_skid) {
kid = asymmetric_key_generate_id(ctx->raw_serial,
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;
/* 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 [PKCS#7 or CMS ver 1]
* or issuing cert's SKID [CMS ver 3].
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 */
* 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);
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)
/**
* 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
*
* 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:
*
* (*) 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;
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)
* @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
* 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;
if (ret < 0)
goto error;
- ret = pkcs7_verify(pkcs7);
+ ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;
* 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;
/*
* 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 */
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.
*
#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)
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
extern int system_verify_data(const void *data, unsigned long len,
- const void *raw_pkcs7, size_t pkcs7_len);
+ const void *raw_pkcs7, size_t pkcs7_len,
+ enum key_being_used_for usage);
#endif
#endif /* _KEYS_SYSTEM_KEYRING_H */
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 */
/* 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 */
#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 */
return -EBADMSG;
}
- return system_verify_data(mod, modlen, mod + modlen, sig_len);
+ return system_verify_data(mod, modlen, mod + modlen, sig_len,
+ VERIFYING_MODULE_SIGNATURE);
}
* @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)
+ const void *raw_pkcs7, size_t pkcs7_len,
+ enum key_being_used_for usage)
{
struct pkcs7_message *pkcs7;
bool trusted;
goto error;
}
- ret = pkcs7_verify(pkcs7);
+ ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;
bool sign_only = false;
unsigned char buf[4096];
unsigned long module_size, cms_size;
- unsigned int use_keyid = 0;
+ unsigned int use_keyid = 0, use_signed_attrs = CMS_NOATTR;
const EVP_MD *digest_algo;
EVP_PKEY *private_key;
CMS_ContentInfo *cms;
ERR(!cms, "CMS_sign");
ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
- CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP | use_keyid),
+ 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");