Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6e17c1b authored by David Howells's avatar David Howells
Browse files

Merge branch 'keys-sig' into keys-next



These commits do the following:

 (1) Retain a signature in an asymmetric-type key and associate with it the
     identifiers that will match a key that can be used to verify it.

 (2) Differentiate an X.509 cert that cannot be used versus one that cannot
     be verified due to unavailable crypto.  This is noted in the
     structures involved.

 (3) Determination of the self-signedness of an X.509 cert is improved to
     include checks on the subject/issuer names and the key
     algorithm/signature algorithm types.

 (4) Self-signed X.509 certificates are consistency checked early on if the
     appropriate crypto is available.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parents 6e007f31 ad3043fd
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -331,7 +331,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
	pr_devel("==>%s()\n", __func__);

	if (subtype) {
		subtype->destroy(prep->payload.data[asym_crypto]);
		subtype->destroy(prep->payload.data[asym_crypto],
				 prep->payload.data[asym_auth]);
		module_put(subtype->owner);
	}
	asymmetric_key_free_kids(kids);
@@ -346,13 +347,15 @@ static void asymmetric_key_destroy(struct key *key)
	struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
	struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
	void *data = key->payload.data[asym_crypto];
	void *auth = key->payload.data[asym_auth];

	key->payload.data[asym_crypto] = NULL;
	key->payload.data[asym_subtype] = NULL;
	key->payload.data[asym_key_ids] = NULL;
	key->payload.data[asym_auth] = NULL;

	if (subtype) {
		subtype->destroy(data);
		subtype->destroy(data, auth);
		module_put(subtype->owner);
	}

+23 −15
Original line number Diff line number Diff line
@@ -44,9 +44,7 @@ struct pkcs7_parse_context {
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
{
	if (sinfo) {
		kfree(sinfo->sig.s);
		kfree(sinfo->sig.digest);
		kfree(sinfo->signing_cert_id);
		public_key_signature_free(sinfo->sig);
		kfree(sinfo);
	}
}
@@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
	if (!ctx->sinfo)
		goto out_no_sinfo;
	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
				  GFP_KERNEL);
	if (!ctx->sinfo->sig)
		goto out_no_sig;

	ctx->data = (unsigned long)data;
	ctx->ppcerts = &ctx->certs;
@@ -150,6 +152,7 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
		ctx->certs = cert->next;
		x509_free_certificate(cert);
	}
out_no_sig:
	pkcs7_free_signed_info(ctx->sinfo);
out_no_sinfo:
	pkcs7_free_message(ctx->msg);
@@ -218,25 +221,26 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,

	switch (ctx->last_oid) {
	case OID_md4:
		ctx->sinfo->sig.hash_algo = "md4";
		ctx->sinfo->sig->hash_algo = "md4";
		break;
	case OID_md5:
		ctx->sinfo->sig.hash_algo = "md5";
		ctx->sinfo->sig->hash_algo = "md5";
		break;
	case OID_sha1:
		ctx->sinfo->sig.hash_algo = "sha1";
		ctx->sinfo->sig->hash_algo = "sha1";
		break;
	case OID_sha256:
		ctx->sinfo->sig.hash_algo = "sha256";
		ctx->sinfo->sig->hash_algo = "sha256";
		break;
	case OID_sha384:
		ctx->sinfo->sig.hash_algo = "sha384";
		ctx->sinfo->sig->hash_algo = "sha384";
		break;
	case OID_sha512:
		ctx->sinfo->sig.hash_algo = "sha512";
		ctx->sinfo->sig->hash_algo = "sha512";
		break;
	case OID_sha224:
		ctx->sinfo->sig.hash_algo = "sha224";
		ctx->sinfo->sig->hash_algo = "sha224";
		break;
	default:
		printk("Unsupported digest algo: %u\n", ctx->last_oid);
		return -ENOPKG;
@@ -255,7 +259,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,

	switch (ctx->last_oid) {
	case OID_rsaEncryption:
		ctx->sinfo->sig.pkey_algo = "rsa";
		ctx->sinfo->sig->pkey_algo = "rsa";
		break;
	default:
		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
@@ -615,11 +619,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen,
{
	struct pkcs7_parse_context *ctx = context;

	ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL);
	if (!ctx->sinfo->sig.s)
	ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
	if (!ctx->sinfo->sig->s)
		return -ENOMEM;

	ctx->sinfo->sig.s_size = vlen;
	ctx->sinfo->sig->s_size = vlen;
	return 0;
}

@@ -655,12 +659,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,

	pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);

	sinfo->signing_cert_id = kid;
	sinfo->sig->auth_ids[0] = kid;
	sinfo->index = ++ctx->sinfo_index;
	*ctx->ppsinfo = sinfo;
	ctx->ppsinfo = &sinfo->next;
	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
	if (!ctx->sinfo)
		return -ENOMEM;
	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
				  GFP_KERNEL);
	if (!ctx->sinfo->sig)
		return -ENOMEM;
	return 0;
}
+4 −6
Original line number Diff line number Diff line
@@ -41,19 +41,17 @@ struct pkcs7_signed_info {
#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 asymmetric_key_id *signing_cert_id;

	/* Message signature.
	 *
	 * This contains the generated digest of _either_ the Content Data or
	 * the Authenticated Attributes [RFC2315 9.3].  If the latter, one of
	 * the attributes contains the digest of the the Content Data within
	 * it.
	 *
	 * THis also contains the issuing cert serial number and issuer's name
	 * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
	 */
	struct public_key_signature sig;
	struct public_key_signature *sig;
};

struct pkcs7_message {
+6 −6
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
				    struct pkcs7_signed_info *sinfo,
				    struct key *trust_keyring)
{
	struct public_key_signature *sig = &sinfo->sig;
	struct public_key_signature *sig = sinfo->sig;
	struct x509_certificate *x509, *last = NULL, *p;
	struct key *key;
	bool trusted;
@@ -80,16 +80,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,

		might_sleep();
		last = x509;
		sig = &last->sig;
		sig = last->sig;
	}

	/* No match - see if the root certificate has a signer amongst the
	 * trusted keys.
	 */
	if (last && (last->akid_id || last->akid_skid)) {
	if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
		key = x509_request_asymmetric_key(trust_keyring,
						  last->akid_id,
						  last->akid_skid,
						  last->sig->auth_ids[0],
						  last->sig->auth_ids[1],
						  false);
		if (!IS_ERR(key)) {
			x509 = last;
@@ -105,7 +105,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
	 * the signed info directly.
	 */
	key = x509_request_asymmetric_key(trust_keyring,
					  sinfo->signing_cert_id,
					  sinfo->sig->auth_ids[0],
					  NULL,
					  false);
	if (!IS_ERR(key)) {
+46 −61
Original line number Diff line number Diff line
@@ -25,34 +25,36 @@
static int pkcs7_digest(struct pkcs7_message *pkcs7,
			struct pkcs7_signed_info *sinfo)
{
	struct public_key_signature *sig = sinfo->sig;
	struct crypto_shash *tfm;
	struct shash_desc *desc;
	size_t digest_size, desc_size;
	void *digest;
	size_t desc_size;
	int ret;

	kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo);
	kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);

	if (!sinfo->sig.hash_algo)
	if (!sinfo->sig->hash_algo)
		return -ENOPKG;

	/* Allocate the hashing algorithm we're going to need and find out how
	 * big the hash operational data will be.
	 */
	tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0);
	tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0);
	if (IS_ERR(tfm))
		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);

	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
	sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
	sig->digest_size = crypto_shash_digestsize(tfm);

	ret = -ENOMEM;
	digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
			 GFP_KERNEL);
	if (!digest)
	sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
	if (!sig->digest)
		goto error_no_desc;

	desc = kzalloc(desc_size, GFP_KERNEL);
	if (!desc)
		goto error_no_desc;

	desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
	desc->tfm   = tfm;
	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;

@@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
	ret = crypto_shash_init(desc);
	if (ret < 0)
		goto error;
	ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
	ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len,
				 sig->digest);
	if (ret < 0)
		goto error;
	pr_devel("MsgDigest = [%*ph]\n", 8, digest);
	pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest);

	/* However, if there are authenticated attributes, there must be a
	 * message digest attribute amongst them which corresponds to the
@@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
			goto error;
		}

		if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
		if (sinfo->msgdigest_len != sig->digest_size) {
			pr_debug("Sig %u: Invalid digest size (%u)\n",
				 sinfo->index, sinfo->msgdigest_len);
			ret = -EBADMSG;
			goto error;
		}

		if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) {
		if (memcmp(sig->digest, sinfo->msgdigest,
			   sinfo->msgdigest_len) != 0) {
			pr_debug("Sig %u: Message digest doesn't match\n",
				 sinfo->index);
			ret = -EKEYREJECTED;
@@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
		 * convert the attributes from a CONT.0 into a SET before we
		 * hash it.
		 */
		memset(digest, 0, sinfo->sig.digest_size);
		memset(sig->digest, 0, sig->digest_size);

		ret = crypto_shash_init(desc);
		if (ret < 0)
@@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
		if (ret < 0)
			goto error;
		ret = crypto_shash_finup(desc, sinfo->authattrs,
					 sinfo->authattrs_len, digest);
					 sinfo->authattrs_len, sig->digest);
		if (ret < 0)
			goto error;
		pr_devel("AADigest = [%*ph]\n", 8, digest);
		pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
	}

	sinfo->sig.digest = digest;
	digest = NULL;

error:
	kfree(digest);
	kfree(desc);
error_no_desc:
	crypto_free_shash(tfm);
	kleave(" = %d", ret);
@@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
		 * PKCS#7 message - but I can't be 100% sure of that.  It's
		 * possible this will need element-by-element comparison.
		 */
		if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
		if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0]))
			continue;
		pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
			 sinfo->index, certix);

		if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
		if (x509->pub->pkey_algo != sinfo->sig->pkey_algo) {
			pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
				sinfo->index);
			continue;
@@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
	 */
	pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
		 sinfo->index,
		 sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
		 sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
	return 0;
}

@@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
				  struct pkcs7_signed_info *sinfo)
{
	struct public_key_signature *sig;
	struct x509_certificate *x509 = sinfo->signer, *p;
	struct asymmetric_key_id *auth;
	int ret;
@@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
			 x509->subject,
			 x509->raw_serial_size, x509->raw_serial);
		x509->seen = true;
		ret = x509_get_sig_params(x509);
		if (ret < 0)
			goto maybe_missing_crypto_in_x509;
		if (x509->unsupported_key)
			goto unsupported_crypto_in_x509;

		pr_debug("- issuer %s\n", x509->issuer);
		if (x509->akid_id)
		sig = x509->sig;
		if (sig->auth_ids[0])
			pr_debug("- authkeyid.id %*phN\n",
				 x509->akid_id->len, x509->akid_id->data);
		if (x509->akid_skid)
				 sig->auth_ids[0]->len, sig->auth_ids[0]->data);
		if (sig->auth_ids[1])
			pr_debug("- authkeyid.skid %*phN\n",
				 x509->akid_skid->len, x509->akid_skid->data);
				 sig->auth_ids[1]->len, sig->auth_ids[1]->data);

		if ((!x509->akid_id && !x509->akid_skid) ||
		    strcmp(x509->subject, x509->issuer) == 0) {
		if (x509->self_signed) {
			/* If there's no authority certificate specified, then
			 * the certificate must be self-signed and is the root
			 * of the chain.  Likewise if the cert is its own
			 * authority.
			 */
			pr_debug("- no auth?\n");
			if (x509->raw_subject_size != x509->raw_issuer_size ||
			    memcmp(x509->raw_subject, x509->raw_issuer,
				   x509->raw_issuer_size) != 0)
				return 0;

			ret = x509_check_signature(x509->pub, x509);
			if (ret < 0)
				goto maybe_missing_crypto_in_x509;
			if (x509->unsupported_sig)
				goto unsupported_crypto_in_x509;
			x509->signer = x509;
			pr_debug("- self-signed\n");
			return 0;
@@ -224,7 +218,7 @@ 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.
		 */
		auth = x509->akid_id;
		auth = sig->auth_ids[0];
		if (auth) {
			pr_debug("- want %*phN\n", auth->len, auth->data);
			for (p = pkcs7->certs; p; p = p->next) {
@@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
					goto found_issuer_check_skid;
			}
		} else {
			auth = x509->akid_skid;
			auth = sig->auth_ids[1];
			pr_debug("- want %*phN\n", auth->len, auth->data);
			for (p = pkcs7->certs; p; p = p->next) {
				if (!p->skid)
@@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
		/* 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)) {
		if (sig->auth_ids[1] &&
		    !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) {
			pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
				sinfo->index, x509->index, p->index);
			return -EKEYREJECTED;
@@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
				sinfo->index);
			return 0;
		}
		ret = x509_check_signature(p->pub, x509);
		ret = public_key_verify_signature(p->pub, p->sig);
		if (ret < 0)
			return ret;
		x509->signer = p;
@@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
		might_sleep();
	}

maybe_missing_crypto_in_x509:
unsupported_crypto_in_x509:
	/* Just prune the certificate chain at this point if we lack some
	 * crypto module to go further.  Note, however, we don't want to set
	 * sinfo->missing_crypto as the signed info block may still be
	 * sinfo->unsupported_crypto as the signed info block may still be
	 * validatable against an X.509 cert lower in the chain that we have a
	 * trusted copy of.
	 */
	if (ret == -ENOPKG)
	return 0;
	return ret;
}

/*
@@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
	}

	/* Verify the PKCS#7 binary against the key */
	ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
	ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig);
	if (ret < 0)
		return ret;

@@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
		 enum key_being_used_for usage)
{
	struct pkcs7_signed_info *sinfo;
	struct x509_certificate *x509;
	int enopkg = -ENOPKG;
	int ret, n;
	int ret;

	kenter("");

@@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
		return -EINVAL;
	}

	for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
		ret = x509_get_sig_params(x509);
		if (ret < 0)
			return ret;
	}

	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
		ret = pkcs7_verify_one(pkcs7, sinfo);
		if (ret < 0) {
Loading