From: David Howells <dhowells@xxxxxxxxxx> Provide a key type for testing the PGP signature parser. It is given a non-detached PGP message as payload: keyctl padd pgp_test a @s <content.txt.gpg A suitable message can be generated like this: echo "This is a test attached-signed content" >content.txt gpg --compress-algo=none -s content.txt Signed-off-by: David Howells <dhowells@xxxxxxxxxx> Co-developed-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> --- crypto/asymmetric_keys/Kconfig | 15 +++ crypto/asymmetric_keys/Makefile | 2 + crypto/asymmetric_keys/pgp_library.c | 64 +++++++++++++ crypto/asymmetric_keys/pgp_test_key.c | 131 ++++++++++++++++++++++++++ crypto/asymmetric_keys/pgplib.h | 16 ++++ 5 files changed, 228 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_test_key.c diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index c69bfdc022c0..d28b9593a70d 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -119,4 +119,19 @@ config PGP_KEY_PARSER for key data and provides the ability to instantiate a crypto key from a public key packet found inside the blob. +config PGP_TEST_KEY + tristate "PGP testing key type" + depends on SYSTEM_DATA_VERIFICATION + depends on PGP_KEY_PARSER=y + help + This option provides a type of key that can be loaded up from a + PGP message - provided the message is signed by a loaded key. If + it is, the PGP wrapper is discarded and reading the key returns + just the payload. If it isn't, adding the key will fail with an + error. If the key used for signature verification isn't in the + built-in or secondary keyring, adding a key will succeed but this + fact will be signaled through a debug message in the kernel log. + + This is intended for testing the PGP parser. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index e7ff01997eb2..507a78f9a0a1 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -89,3 +89,5 @@ obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o pgp_key_parser-y := \ pgp_public_key.o \ pgp_signature.o + +obj-$(CONFIG_PGP_TEST_KEY) += pgp_test_key.o diff --git a/crypto/asymmetric_keys/pgp_library.c b/crypto/asymmetric_keys/pgp_library.c index a9708ccbcb81..ff4bb52790dd 100644 --- a/crypto/asymmetric_keys/pgp_library.c +++ b/crypto/asymmetric_keys/pgp_library.c @@ -544,3 +544,67 @@ int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, return 0; } EXPORT_SYMBOL_GPL(pgp_parse_sig_params); + +#if IS_ENABLED(CONFIG_PGP_TEST_KEY) + +/** + * pgp_parse_literal_data - Parse basic params from a PGP literal data packet + * @data: Content of packet + * @datalen: Length of packet remaining + * @p: The basic parameters + * + * Parse the basic parameters from a PGP literal data packet [RFC 9580: 5.9] + * that are needed to work out what form the data is in and where it is. + * + * Returns 0 if successful or a negative error code. + */ +int pgp_parse_literal_data(const u8 *data, size_t datalen, + struct pgp_literal_data_parameters *p) +{ + unsigned int tmp; + + pr_devel("-->%s(,%zu,,)\n", __func__, datalen); + + if (datalen < 6) + goto too_short; + datalen -= 6; + + p->format = *data++; + switch (p->format) { + case PGP_LIT_FORMAT_BINARY: + case PGP_LIT_FORMAT_TEXT: + case PGP_LIT_FORMAT_TEXT_UTF8: + break; + default: + pr_debug("Literal data packet with unhandled format %02x\n", + p->format); + return -EBADMSG; + } + + p->filename_len = *data++; + p->filename_offset = 2; + if (datalen < p->filename_len) + goto too_short; + data += p->filename_len; + datalen -= p->filename_len; + + tmp = *data++ << 24; + tmp |= *data++ << 16; + tmp |= *data++ << 8; + tmp |= *data++; + p->time = tmp; + + p->content_offset = 6 + p->filename_len; + p->content_len = datalen; + + pr_devel("%x,%u,%x,%u\n", + p->format, p->filename_len, p->time, p->content_len); + return 0; + +too_short: + pr_debug("Literal data packet too short\n"); + return -EBADMSG; +} +EXPORT_SYMBOL_GPL(pgp_parse_literal_data); + +#endif /* CONFIG_PGP_TEST_KEY */ diff --git a/crypto/asymmetric_keys/pgp_test_key.c b/crypto/asymmetric_keys/pgp_test_key.c new file mode 100644 index 000000000000..80ab72ed09b9 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_test_key.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Testing module to load key from trusted PGP message + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@xxxxxxxxxx) + */ + +#define pr_fmt(fmt) "PGPtest: "fmt +#include <linux/key.h> +#include <linux/key-type.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/verification.h> +#include <keys/user-type.h> +#include <keys/system_keyring.h> +#include <crypto/pgp.h> + +#include "pgp_parser.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PGP testing key type"); + +struct pgp_test_parse_context { + struct pgp_parse_context pgp; + struct pgp_literal_data_parameters params; + const void *content; +}; + +static int pgp_test_parse_data(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct pgp_test_parse_context *ctx = + container_of(context, struct pgp_test_parse_context, pgp); + int ret; + + kenter(""); + + ret = pgp_parse_literal_data(data, datalen, &ctx->params); + if (ret == 0) + ctx->content = data + ctx->params.content_offset; + return ret; +} + +/* + * Instantiate a PGP wrapped and validated key. + */ +static int pgp_test_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + struct pgp_test_parse_context p; + const void *saved_prep_data; + size_t saved_prep_datalen; + const struct cred *cred = current_cred(); + int ret; + + kenter(""); + + memset(&p, 0, sizeof(p)); + p.pgp.types_of_interest = (1 << PGP_PKT_LITERAL_DATA); + p.pgp.process_packet = pgp_test_parse_data; + ret = pgp_parse_packets(prep->data, prep->datalen, &p.pgp); + if (ret < 0) { + kleave(" = %d [parse]", ret); + return ret; + } + + if (!p.params.content_len) { + kleave(" = -ENODATA [no literal data"); + return -ENODATA; + } + + ret = verify_pgp_signature(p.content, p.params.content_len, + prep->data, prep->datalen, + VERIFY_USE_SECONDARY_KEYRING, + VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL); + if (ret < 0 && cred->session_keyring) { + ret = verify_pgp_signature(p.content, p.params.content_len, + prep->data, prep->datalen, + cred->session_keyring, + VERIFYING_UNSPECIFIED_SIGNATURE, + NULL, NULL); + if (ret < 0) + goto error; + + pr_warn("PGP message doesn't chain back to a trusted key\n"); + } + + saved_prep_data = prep->data; + saved_prep_datalen = prep->datalen; + prep->data = p.content; + prep->datalen = p.params.content_len; + ret = generic_key_instantiate(key, prep); + prep->data = saved_prep_data; + prep->datalen = saved_prep_datalen; +error: + kleave(" = %d", ret); + return ret; +} + +/* + * user defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +static struct key_type key_type_pgp_test = { + .name = "pgp_test", + .instantiate = pgp_test_instantiate, + .revoke = user_revoke, + .destroy = user_destroy, + .describe = user_describe, + .read = user_read, +}; + +/* + * Module stuff + */ +static int __init pgp_key_init(void) +{ + return register_key_type(&key_type_pgp_test); +} + +static void __exit pgp_key_cleanup(void) +{ + unregister_key_type(&key_type_pgp_test); +} + +module_init(pgp_key_init); +module_exit(pgp_key_cleanup); diff --git a/crypto/asymmetric_keys/pgplib.h b/crypto/asymmetric_keys/pgplib.h index 25191cea33a4..9c852256d1c9 100644 --- a/crypto/asymmetric_keys/pgplib.h +++ b/crypto/asymmetric_keys/pgplib.h @@ -56,3 +56,19 @@ struct pgp_sig_parameters { extern int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, struct pgp_sig_parameters *p); + +#if IS_ENABLED(CONFIG_PGP_TEST_KEY) + +struct pgp_literal_data_parameters { + enum pgp_literal_data_format format : 8; + u8 filename_len; + u8 filename_offset; + u8 content_offset; + u32 content_len; + u32 time; +}; + +extern int pgp_parse_literal_data(const u8 *data, size_t datalen, + struct pgp_literal_data_parameters *p); + +#endif /* CONFIG_PGP_TEST_KEY */ -- 2.34.1