diff --git a/common/iobuf.c b/common/iobuf.c index a3058303d..71930208c 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -60,6 +60,10 @@ test "armored_key_8192" in armor.test! */ #define IOBUF_BUFFER_SIZE 8192 +/* 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. --*/ @@ -1599,6 +1603,13 @@ iobuf_push_filter2 (iobuf_t a, if (a->use == 2 && (rc = iobuf_flush (a))) 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 * A is the new stream and B the original one. * The contents of the buffers are transferred to the diff --git a/g10/mainproc.c b/g10/mainproc.c index 4dec7487b..bd5cac5bc 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -92,12 +92,16 @@ struct mainproc_context DEK *dek; int last_was_session_key; KBNODE list; /* The current list of packets. */ - int have_data; IOBUF iobuf; /* Used to get the filename etc. */ int trustletter; /* Temporary usage in list_node. */ ulong symkeys; 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->list = NULL; - c->have_data = 0; + c->any.data = 0; + c->any.uncompress_failed = 0; c->last_was_session_key = 0; xfree(c->dek); c->dek = NULL; } @@ -204,7 +209,7 @@ add_signature( CTX c, PACKET *pkt ) { KBNODE node; - c->any_sig_seen = 1; + c->any.sig_seen = 1; if( pkt->pkttype == PKT_SIGNATURE && !c->list ) { /* This is the first signature for the following datafile. * GPG does not write such packets; instead it always uses @@ -777,21 +782,34 @@ proc_encrypt_cb (IOBUF a, void *info ) static int proc_compressed( CTX c, PACKET *pkt ) { - PKT_compressed *zd = pkt->pkt.compressed; - int rc; + PKT_compressed *zd = pkt->pkt.compressed; + int rc; - /*printf("zip: compressed data packet\n");*/ - if (c->sigs_only) - 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 - rc = handle_compressed (c->ctrl, c, zd, NULL, NULL ); - if( rc ) - log_error("uncompressing failed: %s\n", g10_errstr(rc)); - free_packet(pkt); - c->last_was_session_key = 0; - return rc; + /*printf("zip: compressed data packet\n");*/ + if (c->sigs_only) + 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 + rc = handle_compressed (c->ctrl, c, zd, NULL, NULL); + + if (gpg_err_code (rc) == GPG_ERR_BAD_DATA) + { + if (!c->any.uncompress_failed) + { + CTX cc; + + for (cc=c; cc; cc = cc->anchor) + cc->any.uncompress_failed = 1; + log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); + } + } + else if (rc) + log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); + + free_packet(pkt); + c->last_was_session_key = 0; + return rc; } /**************** @@ -1213,7 +1231,7 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, IOBUF a, Using log_error is required because verify_files does not check error codes for each file but we want to terminate the process with an error. */ - if (!rc && !c->any_sig_seen) + if (!rc && !c->any.sig_seen) { write_status_text (STATUS_NODATA, "4"); log_error (_("no signature found\n")); @@ -1223,8 +1241,8 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, IOBUF a, /* Propagate the signature seen flag upward. Do this only on success so that we won't issue the nodata status several times. */ - if (!rc && c->anchor && c->any_sig_seen) - c->anchor->any_sig_seen = 1; + if (!rc && c->anchor && c->any.sig_seen) + c->anchor->any.sig_seen = 1; xfree( c ); return rc; @@ -1257,7 +1275,7 @@ proc_signature_packets_by_fd (ctrl_t ctrl, Using log_error is required because verify_files does not check error codes for each file but we want to terminate the process with an error. */ - if (!rc && !c->any_sig_seen) + if (!rc && !c->any.sig_seen) { write_status_text (STATUS_NODATA, "4"); log_error (_("no signature found\n")); @@ -1266,8 +1284,8 @@ proc_signature_packets_by_fd (ctrl_t ctrl, /* Propagate the signature seen flag upward. Do this only on success so that we won't issue the nodata status several times. */ - if (!rc && c->anchor && c->any_sig_seen) - c->anchor->any_sig_seen = 1; + if (!rc && c->anchor && c->any.sig_seen) + c->anchor->any.sig_seen = 1; xfree ( c ); return rc; @@ -1294,14 +1312,14 @@ check_nesting (CTX c) { int level; - for (level = 0; c; c = c->anchor) + for (level=0; c; c = c->anchor) level++; if (level > MAX_NESTING_DEPTH) { log_error ("input data with too deeply nested packets\n"); write_status_text (STATUS_UNEXPECTED, "1"); - return G10ERR_UNEXPECTED; + return GPG_ERR_BAD_DATA; } return 0; } @@ -1423,7 +1441,7 @@ do_proc_packets( CTX c, IOBUF a ) * Hmmm: Rewrite this whole module here?? */ 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 ) ; @@ -2061,7 +2079,7 @@ proc_tree( CTX c, KBNODE node ) } else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) { /* check all signatures */ - if( !c->have_data ) { + if( !c->any.data ) { int use_textmode = 0; free_md_filter_context( &c->mfx ); @@ -2114,7 +2132,7 @@ proc_tree( CTX c, KBNODE node ) && node->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) { /* clear text signed message */ - if( !c->have_data ) { + if( !c->any.data ) { log_error("cleartext signature without data\n" ); return; } @@ -2156,7 +2174,7 @@ proc_tree( CTX c, KBNODE node ) if( sig->sig_class != 0x00 && sig->sig_class != 0x01 ) log_info(_("standalone signature of class 0x%02x\n"), sig->sig_class); - else if( !c->have_data ) { + else if( !c->any.data ) { /* detached signature */ free_md_filter_context( &c->mfx ); if (gcry_md_open (&c->mfx.md, sig->digest_algo, 0))