gpg: Limit the nesting level of I/O filters.
authorWerner Koch <wk@gnupg.org>
Fri, 4 Oct 2013 06:20:49 +0000 (08:20 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 4 Oct 2013 12:05:59 +0000 (14:05 +0200)
* 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>
(cherry picked from commit 35e40e2d514223c950c2f6d1214e02e92d87e997)

Resolved conflicts:
common/iobuf.c
g10/mainproc.c

common/iobuf.c
g10/mainproc.c

index a305830..7193020 100644 (file)
    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
index 4dec748..bd5cac5 100644 (file)
@@ -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))