Commit 4e59d757 authored by Roberto Sassu's avatar Roberto Sassu Committed by zgzxx
Browse files

KEYS: PGP data parser

euleros inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I91FSN


CVE: NA

-------------------------------------------------

Implement a PGP data parser for the crypto key type to use when
instantiating a key.

This parser attempts to parse the instantiation data as a PGP packet
sequence (RFC 4880) and if it parses okay, attempts to extract a public-key
algorithm key or subkey from it.

If it finds such a key, it will set up a public_key subtype payload with
appropriate handler routines (DSA or RSA) and attach it to the key.

Thanks to Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> for pointing
out some errors.

Changelog

v0:
- remove declaration of pgp_to_public_key_algo (Roberto Sassu)
- replace prep->type_data with prep->payload.data (Roberto Sassu)
- replace public_key_destroy() with public_key_free() (Roberto Sassu)
- use asymmetric_key_generate_id() to generate the key identifiers
  (Roberto Sassu)
- add raw_fingerprint to pgp_key_data_parse_context structure
  (Roberto Sassu)
- replace algorithm identifiers with strings (Roberto Sassu)
- don't parse MPIs (Roberto Sassu)
- don't process Public-Subkey packets (only one key will be created)
  (Roberto Sassu)
v5:
 - fix checkpatch warning MISSING_EOF_NEWLINE in pgp_public_key.c
   (zhangguangzhi)

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Co-developed-by: default avatarRoberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: default avatarRoberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: default avatarTianxing Zhang <zhangtianxing3@huawei.com>
Reviewed-by: default avatarJason Yan <yanaijie@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
Signed-off-by: default avatarzhoushuiqing <zhoushuiqing2@huawei.com>
Signed-off-by: default avatarzhangguangzhi <zhangguangzhi3@huawei.com>
parent b78af657
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -92,4 +92,15 @@ config PGP_LIBRARY
	  This option enables a library that provides a number of simple
	  utility functions for parsing PGP (RFC 4880) packet-based messages.

config PGP_KEY_PARSER
	tristate "PGP key parser"
	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
	select PGP_LIBRARY
	select MD5 # V3 fingerprint generation
	select SHA1 # V4 fingerprint generation
	help
	  This option provides support for parsing PGP (RFC 4880) format blobs
	  for key data and provides the ability to instantiate a crypto key
	  from a public key packet found inside the blob.

endif # ASYMMETRIC_KEY_TYPE
+4 −0
Original line number Diff line number Diff line
@@ -82,3 +82,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h
# PGP handling
#
obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o

obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o
pgp_key_parser-y := \
	pgp_public_key.o
+349 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Instantiate a public key crypto key from PGP format data [RFC 4880]
 *
 * Copyright (C) 2011 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.
 */

#ifdef CONFIG_PGP_KEY_PARSER

#define pr_fmt(fmt) "PGP: "fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mpi.h>
#include <linux/pgplib.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <crypto/hash.h>
#include <crypto/public_key.h>
#include "pgp_parser.h"

#define MAX_MPI 5

MODULE_LICENSE("GPL");

static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
{
	crypto_shash_update(digest, &ch, 1);
}

struct pgp_key_data_parse_context {
	struct pgp_parse_context pgp;
	struct public_key *pub;
	unsigned char *raw_fingerprint;
	char *fingerprint;
};

/*
 * Calculate the public key ID (RFC4880 12.2)
 */
static int pgp_calc_pkey_keyid(struct shash_desc *digest,
			       struct pgp_parse_pubkey *pgp,
			       struct public_key *pub)
{
	unsigned int nb[MAX_MPI];
	unsigned int nn[MAX_MPI];
	unsigned int n;
	size_t keylen = pub->keylen;
	u8 *key_ptr = pub->key;
	u8 *pp[MAX_MPI];
	u32 a32;
	int npkey;
	int i, ret;

	kenter("");

	n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
	for (i = 0; i < MAX_MPI && keylen > 0; i++) {
		ret = mpi_key_length(key_ptr, keylen, nb + i, nn + i);
		if (ret < 0)
			return ret;

		pp[i] = key_ptr + 2;
		key_ptr += 2 + nn[i];
		keylen -= 2 + nn[i];
		n += 2 + nn[i];
	}

	if (keylen != 0) {
		pr_debug("excess %zu\n", keylen);
		return -EBADMSG;
	}

	npkey = i;

	digest_putc(digest, 0x99);     /* ctb */
	digest_putc(digest, n >> 8);   /* 16-bit header length */
	digest_putc(digest, n);
	digest_putc(digest, pgp->version);

	a32 = pgp->creation_time;
	digest_putc(digest, a32 >> 24);
	digest_putc(digest, a32 >> 16);
	digest_putc(digest, a32 >>  8);
	digest_putc(digest, a32 >>  0);

	if (pgp->version < PGP_KEY_VERSION_4) {
		u16 a16;

		if (pgp->expires_at)
			a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
		else
			a16 = 0;
		digest_putc(digest, a16 >> 8);
		digest_putc(digest, a16 >> 0);
	}

	digest_putc(digest, pgp->pubkey_algo);

	for (i = 0; i < npkey; i++) {
		digest_putc(digest, nb[i] >> 8);
		digest_putc(digest, nb[i]);
		crypto_shash_update(digest, pp[i], nn[i]);
	}
	ret = 0;

	kleave(" = %d", ret);
	return ret;
}

/*
 * Calculate the public key ID fingerprint
 */
static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
				    struct pgp_parse_pubkey *pgp,
				    struct public_key *pub)
{
	struct crypto_shash *tfm;
	struct shash_desc *digest;
	char *fingerprint;
	u8 *raw_fingerprint;
	int digest_size, offset;
	int ret, i;

	ret = -ENOMEM;
	tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
				 "md5" : "sha1", 0, 0);
	if (IS_ERR(tfm))
		goto cleanup;

	digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
			 GFP_KERNEL);
	if (!digest)
		goto cleanup_tfm;

	digest->tfm = tfm;
	crypto_shash_set_flags(digest->tfm, CRYPTO_TFM_REQ_MAY_SLEEP);
	ret = crypto_shash_init(digest);
	if (ret < 0)
		goto cleanup_hash;

	ret = pgp_calc_pkey_keyid(digest, pgp, pub);
	if (ret < 0)
		goto cleanup_hash;

	digest_size = crypto_shash_digestsize(tfm);

	raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
	if (!raw_fingerprint) {
		ret = -ENOMEM;
		goto cleanup_hash;
	}

	ret = crypto_shash_final(digest, raw_fingerprint);
	if (ret < 0)
		goto cleanup_raw_fingerprint;

	fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
	if (!fingerprint) {
		ret = -ENOMEM;
		goto cleanup_raw_fingerprint;
	}

	offset = digest_size - 8;
	pr_debug("offset %u/%u\n", offset, digest_size);

	for (i = 0; i < digest_size; i++)
		sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
	pr_debug("fingerprint %s\n", fingerprint);

	ctx->raw_fingerprint = raw_fingerprint;
	ctx->fingerprint = fingerprint;
	ret = 0;
cleanup_raw_fingerprint:
	if (ret < 0)
		kfree(raw_fingerprint);
cleanup_hash:
	kfree(digest);
cleanup_tfm:
	crypto_free_shash(tfm);
cleanup:
	kleave(" = %d", ret);
	return ret;
}

/*
 * Extract a public key or public subkey from the PGP stream.
 */
static int pgp_process_public_key(struct pgp_parse_context *context,
				  enum pgp_packet_tag type,
				  u8 headerlen,
				  const u8 *data,
				  size_t datalen)
{
	const char *algo;
	struct pgp_key_data_parse_context *ctx =
		container_of(context, struct pgp_key_data_parse_context, pgp);
	struct pgp_parse_pubkey pgp;
	struct public_key *pub;
	int ret;

	kenter(",%u,%u,,%zu", type, headerlen, datalen);

	if (ctx->fingerprint) {
		kleave(" = -ENOKEY [already]");
		return -EBADMSG;
	}

	pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
	if (!pub)
		return -ENOMEM;
	pub->id_type = "PGP";

	ret = pgp_parse_public_key(&data, &datalen, &pgp);
	if (ret < 0)
		goto cleanup;

	if (pgp.pubkey_algo >= PGP_PUBKEY__LAST)
		goto cleanup_unsupported_pkey_algo;
	algo = pgp_to_public_key_algo[pgp.pubkey_algo];
	if (!algo)
		goto cleanup_unsupported_pkey_algo;
	pub->pkey_algo = algo;

	pub->key = kmemdup(data, datalen, GFP_KERNEL);
	if (!pub->key)
		goto cleanup_nomem;

	pub->keylen = datalen;

	ret = pgp_generate_fingerprint(ctx, &pgp, pub);
	if (ret < 0)
		goto cleanup;

	ctx->pub = pub;
	kleave(" = 0 [use]");
	return 0;

cleanup_unsupported_pkey_algo:
	pr_debug("Unsupported public key algorithm %u\n",
		 pgp.pubkey_algo);
	ret = -ENOPKG;
	goto cleanup;
cleanup_nomem:
	ret = -ENOMEM;
	goto cleanup;
cleanup:
	pr_devel("cleanup");
	kfree(pub->key);
	kfree(pub);
	kleave(" = %d", ret);
	return ret;
}

static struct asymmetric_key_ids *pgp_key_generate_id(
					struct pgp_key_data_parse_context *ctx)
{
	struct asymmetric_key_ids *kids;
	struct asymmetric_key_id *kid;
	int fingerprint_len = strlen(ctx->fingerprint) / 2;

	kids = kzalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
	if (!kids)
		return kids;

	kid = asymmetric_key_generate_id(ctx->raw_fingerprint, fingerprint_len,
					 NULL, 0);
	if (IS_ERR(kid))
		goto error;

	kids->id[0] = kid;
	kids->id[1] = kmemdup(kid, struct_size(kid, data, fingerprint_len),
		      GFP_KERNEL);
	if (!kids->id[1])
		goto error;

	return kids;
error:
	kfree(kids->id[0]);
	kfree(kids);

	return NULL;
}

/*
 * Attempt to parse the instantiation data blob for a key as a PGP packet
 * message holding a key.
 */
static int pgp_key_parse(struct key_preparsed_payload *prep)
{
	struct pgp_key_data_parse_context ctx;
	int ret;

	kenter("");

	memset(&ctx, 0, sizeof(ctx));
	ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY);
	ctx.pgp.process_packet = pgp_process_public_key;

	ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp);
	if (ret < 0)
		goto error;

	/* We're pinning the module by being linked against it */
	__module_get(public_key_subtype.owner);
	prep->payload.data[asym_subtype] = &public_key_subtype;
	prep->payload.data[asym_key_ids] = pgp_key_generate_id(&ctx);
	prep->payload.data[asym_crypto] = ctx.pub;
	prep->quotalen = 100;
	kfree(ctx.fingerprint);
	kfree(ctx.raw_fingerprint);
	return 0;

error:
	public_key_free(ctx.pub);
	kfree(ctx.fingerprint);
	kfree(ctx.raw_fingerprint);
	return ret;
}

static struct asymmetric_key_parser pgp_key_parser = {
	.owner		= THIS_MODULE,
	.name		= "pgp",
	.parse		= pgp_key_parse,
};

/*
 * Module stuff
 */
static int __init pgp_key_init(void)
{
	return register_asymmetric_key_parser(&pgp_key_parser);
}

static void __exit pgp_key_exit(void)
{
	unregister_asymmetric_key_parser(&pgp_key_parser);
}

module_init(pgp_key_init);
module_exit(pgp_key_exit);

#endif /* CONFIG_PGP_KEY_PARSER */