1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-05 23:07:49 +02:00

gpg: Limit the nesting level of I/O filters.

* common/iobuf.c (MAX_NESTING_FILTER): New.
(iobuf_push_filter2): Limit the nesting level.

* g10/mainproc.c (mainproc_context): New field ANY.  Change HAVE_DATA
and ANY_SIG_SIGN to bit fields of ANY.  Add bit field
UNCOMPRESS_FAILED.
(proc_compressed): Avoid printing multiple Bad Data messages.
(check_nesting): Return GPG_ERR_BAD_DATA instead of UNEXPECTED_DATA.
--

This is a more general fix for the nested compression packet bug.  In
particular this helps g10/import.c:read_block to stop pushing
compression filters onto an iobuf stream.  This patch also reduces the
number of error messages for the non-import case.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2013-10-04 08:20:49 +02:00
parent cd1b696b28
commit 35e40e2d51
2 changed files with 59 additions and 30 deletions

View File

@ -55,6 +55,10 @@
be aware that there is no fsync support for the stdio backend. */ be aware that there is no fsync support for the stdio backend. */
#undef FILE_FILTER_USES_STDIO #undef FILE_FILTER_USES_STDIO
/* To avoid a potential DoS with compression packets we better limit
the number of filters in a chain. */
#define MAX_NESTING_FILTER 64
/*-- End configurable part. --*/ /*-- End configurable part. --*/
@ -1615,6 +1619,13 @@ iobuf_push_filter2 (iobuf_t a,
if (a->use == 2 && (rc = iobuf_flush (a))) if (a->use == 2 && (rc = iobuf_flush (a)))
return rc; return rc;
if (a->subno >= MAX_NESTING_FILTER)
{
log_error ("i/o filter too deeply nested - corrupted data?\n");
return GPG_ERR_BAD_DATA;
}
/* make a copy of the current stream, so that /* make a copy of the current stream, so that
* A is the new stream and B the original one. * A is the new stream and B the original one.
* The contents of the buffers are transferred to the * The contents of the buffers are transferred to the

View File

@ -92,12 +92,16 @@ struct mainproc_context
DEK *dek; DEK *dek;
int last_was_session_key; int last_was_session_key;
KBNODE list; /* The current list of packets. */ KBNODE list; /* The current list of packets. */
int have_data;
IOBUF iobuf; /* Used to get the filename etc. */ IOBUF iobuf; /* Used to get the filename etc. */
int trustletter; /* Temporary usage in list_node. */ int trustletter; /* Temporary usage in list_node. */
ulong symkeys; ulong symkeys;
struct kidlist_item *pkenc_list; /* List of encryption packets. */ struct kidlist_item *pkenc_list; /* List of encryption packets. */
int any_sig_seen; /* Set to true if a signature packet has been seen. */ struct {
unsigned int sig_seen:1; /* Set to true if a signature packet
has been seen. */
unsigned int data:1; /* Any data packet seen */
unsigned int uncompress_failed:1;
} any;
}; };
@ -126,7 +130,8 @@ release_list( CTX c )
} }
c->pkenc_list = NULL; c->pkenc_list = NULL;
c->list = NULL; c->list = NULL;
c->have_data = 0; c->any.data = 0;
c->any.uncompress_failed = 0;
c->last_was_session_key = 0; c->last_was_session_key = 0;
xfree(c->dek); c->dek = NULL; xfree(c->dek); c->dek = NULL;
} }
@ -204,7 +209,7 @@ add_signature( CTX c, PACKET *pkt )
{ {
KBNODE node; KBNODE node;
c->any_sig_seen = 1; c->any.sig_seen = 1;
if( pkt->pkttype == PKT_SIGNATURE && !c->list ) { if( pkt->pkttype == PKT_SIGNATURE && !c->list ) {
/* This is the first signature for the following datafile. /* This is the first signature for the following datafile.
* GPG does not write such packets; instead it always uses * GPG does not write such packets; instead it always uses
@ -773,21 +778,34 @@ proc_encrypt_cb( IOBUF a, void *info )
static int static int
proc_compressed( CTX c, PACKET *pkt ) proc_compressed( CTX c, PACKET *pkt )
{ {
PKT_compressed *zd = pkt->pkt.compressed; PKT_compressed *zd = pkt->pkt.compressed;
int rc; int rc;
/*printf("zip: compressed data packet\n");*/ /*printf("zip: compressed data packet\n");*/
if (c->sigs_only) if (c->sigs_only)
rc = handle_compressed( c, zd, proc_compressed_cb, c ); rc = handle_compressed (c, zd, proc_compressed_cb, c);
else if( c->encrypt_only ) else if (c->encrypt_only)
rc = handle_compressed( c, zd, proc_encrypt_cb, c ); rc = handle_compressed (c, zd, proc_encrypt_cb, c);
else else
rc = handle_compressed( c, zd, NULL, NULL ); rc = handle_compressed (c, zd, NULL, NULL);
if( rc )
log_error("uncompressing failed: %s\n", g10_errstr(rc)); if (gpg_err_code (rc) == GPG_ERR_BAD_DATA)
free_packet(pkt); {
c->last_was_session_key = 0; if (!c->any.uncompress_failed)
return rc; {
CTX cc;
for (cc=c; cc; cc = cc->anchor)
cc->any.uncompress_failed = 1;
log_error ("uncompressing failed: %s\n", g10_errstr(rc));
}
}
else if (rc)
log_error("uncompressing failed: %s\n", g10_errstr(rc));
free_packet (pkt);
c->last_was_session_key = 0;
return rc;
} }
/**************** /****************
@ -1204,7 +1222,7 @@ proc_signature_packets( void *anchor, IOBUF a,
Using log_error is required because verify_files does not check Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process error codes for each file but we want to terminate the process
with an error. */ with an error. */
if (!rc && !c->any_sig_seen) if (!rc && !c->any.sig_seen)
{ {
write_status_text (STATUS_NODATA, "4"); write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n")); log_error (_("no signature found\n"));
@ -1214,8 +1232,8 @@ proc_signature_packets( void *anchor, IOBUF a,
/* Propagate the signature seen flag upward. Do this only on /* Propagate the signature seen flag upward. Do this only on
success so that we won't issue the nodata status several success so that we won't issue the nodata status several
times. */ times. */
if (!rc && c->anchor && c->any_sig_seen) if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any_sig_seen = 1; c->anchor->any.sig_seen = 1;
xfree( c ); xfree( c );
return rc; return rc;
@ -1241,7 +1259,7 @@ proc_signature_packets_by_fd (void *anchor, IOBUF a, int signed_data_fd )
Using log_error is required because verify_files does not check Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process error codes for each file but we want to terminate the process
with an error. */ with an error. */
if (!rc && !c->any_sig_seen) if (!rc && !c->any.sig_seen)
{ {
write_status_text (STATUS_NODATA, "4"); write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n")); log_error (_("no signature found\n"));
@ -1250,8 +1268,8 @@ proc_signature_packets_by_fd (void *anchor, IOBUF a, int signed_data_fd )
/* Propagate the signature seen flag upward. Do this only on success /* Propagate the signature seen flag upward. Do this only on success
so that we won't issue the nodata status several times. */ so that we won't issue the nodata status several times. */
if (!rc && c->anchor && c->any_sig_seen) if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any_sig_seen = 1; c->anchor->any.sig_seen = 1;
xfree ( c ); xfree ( c );
return rc; return rc;
@ -1277,14 +1295,14 @@ check_nesting (CTX c)
{ {
int level; int level;
for (level = 0; c; c = c->anchor) for (level=0; c; c = c->anchor)
level++; level++;
if (level > MAX_NESTING_DEPTH) if (level > MAX_NESTING_DEPTH)
{ {
log_error ("input data with too deeply nested packets\n"); log_error ("input data with too deeply nested packets\n");
write_status_text (STATUS_UNEXPECTED, "1"); write_status_text (STATUS_UNEXPECTED, "1");
return G10ERR_UNEXPECTED; return GPG_ERR_BAD_DATA;
} }
return 0; return 0;
} }
@ -1406,7 +1424,7 @@ do_proc_packets( CTX c, IOBUF a )
* Hmmm: Rewrite this whole module here?? * Hmmm: Rewrite this whole module here??
*/ */
if( pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC ) if( pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC )
c->have_data = pkt->pkttype == PKT_PLAINTEXT; c->any.data = (pkt->pkttype == PKT_PLAINTEXT);
if( newpkt == -1 ) if( newpkt == -1 )
; ;
@ -2044,7 +2062,7 @@ proc_tree( CTX c, KBNODE node )
} }
else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) { else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) {
/* check all signatures */ /* check all signatures */
if( !c->have_data ) { if( !c->any.data ) {
int use_textmode = 0; int use_textmode = 0;
free_md_filter_context( &c->mfx ); free_md_filter_context( &c->mfx );
@ -2097,7 +2115,7 @@ proc_tree( CTX c, KBNODE node )
&& node->pkt->pkt.gpg_control->control && node->pkt->pkt.gpg_control->control
== CTRLPKT_CLEARSIGN_START ) { == CTRLPKT_CLEARSIGN_START ) {
/* clear text signed message */ /* clear text signed message */
if( !c->have_data ) { if( !c->any.data ) {
log_error("cleartext signature without data\n" ); log_error("cleartext signature without data\n" );
return; return;
} }
@ -2139,7 +2157,7 @@ proc_tree( CTX c, KBNODE node )
if( sig->sig_class != 0x00 && sig->sig_class != 0x01 ) if( sig->sig_class != 0x00 && sig->sig_class != 0x01 )
log_info(_("standalone signature of class 0x%02x\n"), log_info(_("standalone signature of class 0x%02x\n"),
sig->sig_class); sig->sig_class);
else if( !c->have_data ) { else if( !c->any.data ) {
/* detached signature */ /* detached signature */
free_md_filter_context( &c->mfx ); free_md_filter_context( &c->mfx );
if (gcry_md_open (&c->mfx.md, sig->digest_algo, 0)) if (gcry_md_open (&c->mfx.md, sig->digest_algo, 0))