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 Changelog v0: - use verify_pgp_signature() to verify signatures with builtin_trusted_keys or secondary_trusted_keys (Roberto Sassu) - use pgp_verify_sig() to verify signatures with keys in the user keyring (Roberto Sassu) - replace user_instantiate with generic_key_instantiate (Roberto Sassu) - remove .def_lookup_type and .match methods (Roberto Sassu) - fix style issues (Roberto Sassu) Signed-off-by: David Howells <dhowells@xxxxxxxxxx> Co-developed-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> --- crypto/asymmetric_keys/Kconfig | 13 +++ crypto/asymmetric_keys/Makefile | 4 + crypto/asymmetric_keys/pgp_library.c | 64 +++++++++++++ crypto/asymmetric_keys/pgp_test_key.c | 132 ++++++++++++++++++++++++++ include/linux/pgplib.h | 15 +++ 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 356b85fc34bd..8226d06fe1e0 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -110,4 +110,17 @@ 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 PGP_KEY_PARSER + depends on SYSTEM_DATA_VERIFICATION + help + This option provides a type of key that can be loaded up from a + PGP message - provided the message is signed by a trusted 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. + + 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 e2aeb2a4b6a6..48bbe2b1d446 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -96,3 +96,7 @@ 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.o +pgp_test-y := \ + pgp_test_key.o \ + pgp_signature.o diff --git a/crypto/asymmetric_keys/pgp_library.c b/crypto/asymmetric_keys/pgp_library.c index 13bfc9db1ae4..3a2bd73f1915 100644 --- a/crypto/asymmetric_keys/pgp_library.c +++ b/crypto/asymmetric_keys/pgp_library.c @@ -559,3 +559,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 4880: 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..55bc6cad8559 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_test_key.c @@ -0,0 +1,132 @@ +/* Testing module to load key from trusted PGP message + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@xxxxxxxxxx) + * + * 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 pr_fmt(fmt) "PGPtest: "fmt +#include <linux/key.h> +#include <linux/key-type.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/pgp.h> +#include <linux/pgplib.h> +#include <linux/pgp_sig.h> +#include <linux/module.h> +#include <linux/verification.h> +#include <keys/user-type.h> +#include <keys/system_keyring.h> +#include "pgp_parser.h" + +MODULE_LICENSE("GPL"); + +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; + int ret; + + kenter(""); + + if (!current_cred()->user->uid_keyring) + return -ENOKEY; + + 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, + NULL, 0, prep->data, prep->datalen, NULL); + if (ret < 0) { + ret = pgp_verify_sig(NULL, p.content, p.params.content_len, + NULL, 0, prep->data, prep->datalen); + 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/include/linux/pgplib.h b/include/linux/pgplib.h index 44c8a07b32e3..21999bfe8e2a 100644 --- a/include/linux/pgplib.h +++ b/include/linux/pgplib.h @@ -66,6 +66,21 @@ 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 */ #endif /* CONFIG_PGP_LIBRARY */ -- 2.17.1