Commit b78af657 authored by David Howells's avatar David Howells Committed by zgzxx
Browse files

PGPLIB: Basic packet parser

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


CVE: NA

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

Provide a simple parser that extracts the packets from a PGP packet blob
and passes the desirous ones to the given processor function:

	struct pgp_parse_context {
		u64 types_of_interest;
		int (*process_packet)(struct pgp_parse_context *context,
				      enum pgp_packet_tag type,
				      u8 headerlen,
				      const u8 *data,
				      size_t datalen);
	};

	int pgp_parse_packets(const u8 *data, size_t datalen,
			      struct pgp_parse_context *ctx);

This is configured on with CONFIG_PGP_LIBRARY.

Changelog

v0:
- fix style issues (Roberto Sassu)
- declare pgp_to_public_key_algo (Roberto Sassu)
v5:
 - context adapt crypto/asymmetric_keys/Kconfig for 6.6 kernel
   (zhangguangzhi)
 - fix checkpatch error MISSING_EOF_NEWLINE in
   crypto/asymmetric_keys/pgp_library.c and
   crypto/asymmetric_keys/pgp_parser.h (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 e6335c49
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -86,4 +86,10 @@ config FIPS_SIGNATURE_SELFTEST
	depends on PKCS7_MESSAGE_PARSER=X509_CERTIFICATE_PARSER
	depends on X509_CERTIFICATE_PARSER

config PGP_LIBRARY
	tristate "PGP parsing library"
	help
	  This option enables a library that provides a number of simple
	  utility functions for parsing PGP (RFC 4880) packet-based messages.

endif # ASYMMETRIC_KEY_TYPE
+5 −0
Original line number Diff line number Diff line
@@ -77,3 +77,8 @@ verify_signed_pefile-y := \

$(obj)/mscode_parser.o: $(obj)/mscode.asn1.h $(obj)/mscode.asn1.h
$(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h

#
# PGP handling
#
obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+285 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* PGP packet parser (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_LIBRARY

#define pr_fmt(fmt) "PGPL: "fmt
#include <linux/pgplib.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

const char *const pgp_hash_algorithms[PGP_HASH__LAST] = {
	[PGP_HASH_MD5]			= "md5",
	[PGP_HASH_SHA1]			= "sha1",
	[PGP_HASH_RIPE_MD_160]		= "rmd160",
	[PGP_HASH_SHA256]		= "sha256",
	[PGP_HASH_SHA384]		= "sha384",
	[PGP_HASH_SHA512]		= "sha512",
	[PGP_HASH_SHA224]		= "sha224",
};
EXPORT_SYMBOL_GPL(pgp_hash_algorithms);

const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST] = {
	[PGP_PUBKEY_RSA_ENC_OR_SIG]	= "rsa",
	[PGP_PUBKEY_RSA_ENC_ONLY]	= "rsa",
	[PGP_PUBKEY_RSA_SIG_ONLY]	= "rsa",
	[PGP_PUBKEY_ELGAMAL]		= NULL,
	[PGP_PUBKEY_DSA]		= NULL,
};
EXPORT_SYMBOL_GPL(pgp_to_public_key_algo);

/**
 * pgp_parse_packet_header - Parse a PGP packet header
 * @_data: Start of the PGP packet (updated to PGP packet data)
 * @_datalen: Amount of data remaining in buffer (decreased)
 * @_type: Where the packet type will be returned
 * @_headerlen: Where the header length will be returned
 *
 * Parse a set of PGP packet header [RFC 4880: 4.2].
 *
 * Returns packet data size on success; non-zero on error.  If successful,
 * *_data and *_datalen will have been updated and *_headerlen will be set to
 * hold the length of the packet header.
 */
static ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen,
				       enum pgp_packet_tag *_type,
				       u8 *_headerlen)
{
	enum pgp_packet_tag type;
	const u8 *data = *_data;
	size_t size, datalen = *_datalen;

	pr_devel("-->%s(,%zu,,)\n", __func__, datalen);

	if (datalen < 2)
		goto short_packet;

	pr_devel("pkthdr %02x, %02x\n", data[0], data[1]);

	type = *data++;
	datalen--;
	if (!(type & 0x80)) {
		pr_debug("Packet type does not have MSB set\n");
		return -EBADMSG;
	}
	type &= ~0x80;

	if (type & 0x40) {
		/* New packet length format */
		type &= ~0x40;
		pr_devel("new format: t=%u\n", type);
		switch (data[0]) {
		case 0x00 ... 0xbf:
			/* One-byte length */
			size = data[0];
			data++;
			datalen--;
			*_headerlen = 2;
			break;
		case 0xc0 ... 0xdf:
			/* Two-byte length */
			if (datalen < 2)
				goto short_packet;
			size = (data[0] - 192) * 256;
			size += data[1] + 192;
			data += 2;
			datalen -= 2;
			*_headerlen = 3;
			break;
		case 0xff:
			/* Five-byte length */
			if (datalen < 5)
				goto short_packet;
			size =  data[1] << 24;
			size |= data[2] << 16;
			size |= data[3] << 8;
			size |= data[4];
			data += 5;
			datalen -= 5;
			*_headerlen = 6;
			break;
		default:
			pr_debug("Partial body length packet not supported\n");
			return -EBADMSG;
		}
	} else {
		/* Old packet length format */
		u8 length_type = type & 0x03;

		type >>= 2;
		pr_devel("old format: t=%u lt=%u\n", type, length_type);

		switch (length_type) {
		case 0:
			/* One-byte length */
			size = data[0];
			data++;
			datalen--;
			*_headerlen = 2;
			break;
		case 1:
			/* Two-byte length */
			if (datalen < 2)
				goto short_packet;
			size  = data[0] << 8;
			size |= data[1];
			data += 2;
			datalen -= 2;
			*_headerlen = 3;
			break;
		case 2:
			/* Four-byte length */
			if (datalen < 4)
				goto short_packet;
			size  = data[0] << 24;
			size |= data[1] << 16;
			size |= data[2] << 8;
			size |= data[3];
			data += 4;
			datalen -= 4;
			*_headerlen = 5;
			break;
		default:
			pr_debug("Indefinite length packet not supported\n");
			return -EBADMSG;
		}
	}

	pr_devel("datalen=%zu size=%zu", datalen, size);
	if (datalen < size)
		goto short_packet;
	if ((int)size < 0)
		goto too_big;

	*_data = data;
	*_datalen = datalen;
	*_type = type;
	pr_devel("Found packet type=%u size=%zd\n", type, size);
	return size;

short_packet:
	pr_debug("Attempt to parse short packet\n");
	return -EBADMSG;
too_big:
	pr_debug("Signature subpacket size >2G\n");
	return -EMSGSIZE;
}

/**
 * pgp_parse_packets - Parse a set of PGP packets
 * @_data: Data to be parsed (updated)
 * @_datalen: Amount of data (updated)
 * @ctx: Parsing context
 *
 * Parse a set of PGP packets [RFC 4880: 4].
 */
int pgp_parse_packets(const u8 *data, size_t datalen,
		      struct pgp_parse_context *ctx)
{
	enum pgp_packet_tag type;
	ssize_t pktlen;
	u8 headerlen;
	int ret;

	while (datalen > 2) {
		pktlen = pgp_parse_packet_header(&data, &datalen, &type,
						 &headerlen);
		if (pktlen < 0)
			return pktlen;

		if ((ctx->types_of_interest >> type) & 1) {
			ret = ctx->process_packet(ctx, type, headerlen,
						  data, pktlen);
			if (ret < 0)
				return ret;
		}
		data += pktlen;
		datalen -= pktlen;
	}

	if (datalen != 0) {
		pr_debug("Excess octets in packet stream\n");
		return -EBADMSG;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(pgp_parse_packets);

/**
 * pgp_parse_public_key - Parse the common part of a PGP pubkey packet
 * @_data: Content of packet (updated)
 * @_datalen: Length of packet remaining (updated)
 * @pk: Public key data
 *
 * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2].
 */
int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
			 struct pgp_parse_pubkey *pk)
{
	const u8 *data = *_data;
	size_t datalen = *_datalen;
	unsigned int tmp;

	if (datalen < 12) {
		pr_debug("Public key packet too short\n");
		return -EBADMSG;
	}

	pk->version = *data++;
	switch (pk->version) {
	case PGP_KEY_VERSION_2:
	case PGP_KEY_VERSION_3:
	case PGP_KEY_VERSION_4:
		break;
	default:
		pr_debug("Public key packet with unhandled version %d\n",
			   pk->version);
		return -EBADMSG;
	}

	tmp  = *data++ << 24;
	tmp |= *data++ << 16;
	tmp |= *data++ << 8;
	tmp |= *data++;
	pk->creation_time = tmp;
	if (pk->version == PGP_KEY_VERSION_4) {
		pk->expires_at = 0; /* Have to get it from the selfsignature */
	} else {
		unsigned short ndays;

		ndays  = *data++ << 8;
		ndays |= *data++;
		if (ndays)
			pk->expires_at = pk->creation_time + ndays * 86400UL;
		else
			pk->expires_at = 0;
		datalen -= 2;
	}

	pk->pubkey_algo = *data++;
	datalen -= 6;

	pr_devel("%x,%x,%lx,%lx\n",
		 pk->version, pk->pubkey_algo, pk->creation_time,
		 pk->expires_at);

	*_data = data;
	*_datalen = datalen;
	return 0;
}
EXPORT_SYMBOL_GPL(pgp_parse_public_key);

#endif /* #CONFIG_PGP_LIBRARY */
+28 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* PGP crypto data parser internal definitions
 *
 * 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_LIBRARY

#include <linux/pgp.h>

#define kenter(FMT, ...) \
	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
#define kleave(FMT, ...) \
	pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)

/*
 * pgp_library.c
 */
extern const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST];

#endif /* CONFIG_PGP_LIBRARY */

include/linux/pgplib.h

0 → 100644
+48 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* PGP library definitions (RFC 4880)
 *
 * Copyright (C) 2012 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.
 */

#ifndef _LINUX_PGPLIB_H
#define _LINUX_PGPLIB_H

#if IS_ENABLED(CONFIG_PGP_LIBRARY) || IS_ENABLED(CONFIG_PGP_LIBRARY_MODULE)

#include <linux/pgp.h>

/*
 * PGP library packet parser
 */
struct pgp_parse_context {
	u64 types_of_interest;
	int (*process_packet)(struct pgp_parse_context *context,
			      enum pgp_packet_tag type,
			      u8 headerlen,
			      const u8 *data,
			      size_t datalen);
};

extern int pgp_parse_packets(const u8 *data, size_t datalen,
			     struct pgp_parse_context *ctx);

struct pgp_parse_pubkey {
	enum pgp_key_version version : 8;
	enum pgp_pubkey_algo pubkey_algo : 8;
	__kernel_old_time_t creation_time;
	__kernel_old_time_t expires_at;
};

extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
				struct pgp_parse_pubkey *pk);


#endif /* CONFIG_PGP_LIBRARY */

#endif /* _LINUX_PGPLIB_H */