nixpkgs/pkgs/tools/security/gnupg/v3-0001-Disallow-compressed-signatures-and-certificates.patch

217 lines
7.9 KiB
Diff

From 459b61fa21db755d6c879c3ef9ab85b3d1786c9f Mon Sep 17 00:00:00 2001
From: Demi Marie Obenour <demi () invisiblethingslab com>
Date: Fri, 27 May 2022 19:51:19 -0400
Subject: [PATCH GnuPG v3] Disallow compressed signatures and certificates
Compressed packets have significant attack surface, due to the potential
for both denial of service (zip bombs and the like) and for code
execution via memory corruption vulnerabilities in the decompressor.
Furthermore, I am not aware of any implementation that uses them in keys
or detached signatures. Therefore, disallow their use in such contexts
entirely. This includes signatures that are part of a cleartext-signed
message.
When parsing detached signatures, forbid any packet that is not a
signature or marker packet. When parsing keys, return an error when
encountering a compressed packet, instead of decompressing the packet.
Furthermore, certificates, keys, and signatures are not allowed to
contain partial-length or indeterminate-length packets. Reject those in
parse_packet, rather than activating the partial-length filter code.
This is not (yet) implemented for cleartext-signed messages, as these
messages are internally represented as inline-signed messages.
GnuPG-bug-id: T5993
Signed-off-by: Demi Marie Obenour <demiobenour () gmail com>
---
g10/import.c | 18 ++----------------
g10/mainproc.c | 24 +++++++++++++++++++++---
g10/packet.h | 2 ++
g10/parse-packet.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 68 insertions(+), 20 deletions(-)
diff --git a/g10/import.c b/g10/import.c
index bb0bf67934a8316130cde182cd43d56353e0171d..a8136351f6f7dae8c65634ed8e1c242d323e2009 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1042,22 +1042,8 @@ read_block( IOBUF a, unsigned int options,
switch (pkt->pkttype)
{
case PKT_COMPRESSED:
- if (check_compress_algo (pkt->pkt.compressed->algorithm))
- {
- rc = GPG_ERR_COMPR_ALGO;
- goto ready;
- }
- else
- {
- compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx );
- pkt->pkt.compressed->buf = NULL;
- if (push_compress_filter2 (a, cfx,
- pkt->pkt.compressed->algorithm, 1))
- xfree (cfx); /* e.g. in case of compression_algo NONE. */
- }
- free_packet (pkt, &parsectx);
- init_packet(pkt);
- break;
+ rc = GPG_ERR_UNEXPECTED;
+ goto ready;
case PKT_RING_TRUST:
/* Skip those packets unless we are in restore mode. */
diff --git a/g10/mainproc.c b/g10/mainproc.c
index af11877aa257e46662c42b6ff573ee01c3ad1547..3629fc921b742afd131e8d8e2664b201095990f0 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -152,6 +152,7 @@ add_onepass_sig (CTX c, PACKET *pkt)
{
kbnode_t node;
+ log_assert(!(c->sigs_only && c->signed_data.used));
if (c->list) /* Add another packet. */
add_kbnode (c->list, new_kbnode (pkt));
else /* Insert the first one. */
@@ -1076,8 +1077,16 @@ proc_compressed (CTX c, PACKET *pkt)
int rc;
/*printf("zip: compressed data packet\n");*/
- if (c->sigs_only)
- rc = handle_compressed (c->ctrl, c, zd, proc_compressed_cb, c);
+ if ( literals_seen )
+ {
+ log_error ("Compressed packet follows literal data packet\n");
+ rc = GPG_ERR_UNEXPECTED;
+ }
+ else if ( c->sigs_only )
+ {
+ log_assert(!c->signed_data.used);
+ rc = handle_compressed (c->ctrl, c, zd, proc_compressed_cb, c);
+ }
else if( c->encrypt_only )
rc = handle_compressed (c->ctrl, c, zd, proc_encrypt_cb, c);
else
@@ -1596,6 +1605,7 @@ do_proc_packets (CTX c, iobuf_t a)
c->iobuf = a;
init_packet(pkt);
init_parse_packet (&parsectx, a);
+ parsectx.sigs_only = c->sigs_only && c->signed_data.used;
while ((rc=parse_packet (&parsectx, pkt)) != -1)
{
any_data = 1;
@@ -1607,6 +1617,12 @@ do_proc_packets (CTX c, iobuf_t a)
if (gpg_err_code (rc) == GPG_ERR_INV_PACKET
&& opt.list_packets == 0)
break;
+
+ if (gpg_err_code (rc) == GPG_ERR_UNEXPECTED)
+ {
+ write_status_text( STATUS_UNEXPECTED, "0" );
+ goto leave;
+ }
continue;
}
newpkt = -1;
@@ -1644,7 +1660,9 @@ do_proc_packets (CTX c, iobuf_t a)
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break;
- default: newpkt = 0; break;
+ default:
+ log_assert(!c->signed_data.used);
+ newpkt = 0; break;
}
}
else if (c->encrypt_only)
diff --git a/g10/packet.h b/g10/packet.h
index 5a14015a16c872fe7b0b15468598daf7a05ffc02..82dfe786b46051491e7015e64441678140defa9e 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -657,6 +657,7 @@ struct parse_packet_ctx_s
int free_last_pkt; /* Indicates that LAST_PKT must be freed. */
int skip_meta; /* Skip ring trust packets. */
unsigned int n_parsed_packets; /* Number of parsed packets. */
+ int sigs_only; /* Only accept detached signature packets */
};
typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
@@ -667,6 +668,7 @@ typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
(a)->free_last_pkt = 0; \
(a)->skip_meta = 0; \
(a)->n_parsed_packets = 0; \
+ (a)->sigs_only = 0; \
} while (0)
#define deinit_parse_packet(a) do { \
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index cea1f7ebc5daec3863ae963c1ab25500f86796fe..dca66ff427ea6778e536782ec6bda83584877342 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -738,6 +738,20 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD:
case PKT_COMPRESSED:
+ if (ctx->sigs_only)
+ {
+ log_error (_("partial length packet of type %d in detached"
+ " signature\n"), pkttype);
+ rc = gpg_error (GPG_ERR_UNEXPECTED);
+ goto leave;
+ }
+ if (onlykeypkts)
+ {
+ log_error (_("partial length packet of type %d in keyring\n"),
+ pkttype);
+ rc = gpg_error (GPG_ERR_UNEXPECTED);
+ goto leave;
+ }
iobuf_set_partial_body_length_mode (inp, c & 0xff);
pktlen = 0; /* To indicate partial length. */
partial = 1;
@@ -775,6 +789,20 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
+ else if (ctx->sigs_only)
+ {
+ log_error (_("indeterminate length packet of type %d in detached"
+ " signature\n"), pkttype);
+ rc = gpg_error (GPG_ERR_UNEXPECTED);
+ goto leave;
+ }
+ else if (onlykeypkts)
+ {
+ log_error (_("indeterminate length packet of type %d in"
+ " keyring\n"), pkttype);
+ rc = gpg_error (GPG_ERR_UNEXPECTED);
+ goto leave;
+ }
}
else
{
@@ -828,7 +856,21 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
goto leave;
}
- if (with_uid && pkttype == PKT_USER_ID)
+ if (ctx->sigs_only)
+ switch (pkttype)
+ {
+ case PKT_SIGNATURE:
+ case PKT_MARKER:
+ break;
+ default:
+ log_error(_("Packet type %d not allowed in detached signature\n"),
+ pkttype);
+ iobuf_skip_rest (inp, pktlen, partial);
+ *skip = 1;
+ rc = GPG_ERR_UNEXPECTED;
+ goto leave;
+ }
+ else if (with_uid && pkttype == PKT_USER_ID)
/* If ONLYKEYPKTS is set to 2, then we never skip user id packets,
even if DO_SKIP is set. */
;
--
2.36.1