Commit message: gpg: Avoid infinite loop in uncompressing garbled packets.

* g10/compress.c (do_uncompress): Limit the number of extra FF bytes.
--

A packet like (a3 01 5b ff) leads to an infinite loop.  Using
--max-output won't help if it is a partial packet.  This patch
actually fixes a regression introduced on 1999-05-31 (c34c6769).
Actually it would be sufficient to stuff just one extra 0xff byte.
Given that this problem popped up only after 15 years, I feel safer to
allow for a very few FF bytes.

Thanks to Olivier Levillain and Florian Maury for their detailed
report.
CVE: CVE-2014-4617
CWE: CWE-20
Vulnerability description: The do_uncompress function in g10/compress.c in GnuPG 1.x before 1.4.17 and 2.x before 2.0.24 allows context-dependent attackers to cause a denial of service (infinite loop) via malformed compressed packets, as demonstrated by an a3 01 5b ff byte sequence.

Project: gnupg
File: compress.c

do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a )
{
    int rc;
    int zrc;
    unsigned n;

    do {
	zs->next_out = BYTEF_CAST (zfx->outbuf);
	zs->avail_out = zfx->outbufsize;
	if( DBG_FILTER )
	    log_debug("enter deflate: avail_in=%u, avail_out=%u, flush=%d\n",
		    (unsigned)zs->avail_in, (unsigned)zs->avail_out, flush );
	zrc = deflate( zs, flush );
	if( zrc == Z_STREAM_END && flush == Z_FINISH )
	    ;
	else if( zrc != Z_OK ) {
	    if( zs->msg )
		log_fatal("zlib deflate problem: %s\n", zs->msg );
	    else
		log_fatal("zlib deflate problem: rc=%d\n", zrc );
	}
	n = zfx->outbufsize - zs->avail_out;
	if( DBG_FILTER )
	    log_debug("leave deflate: "
		      "avail_in=%u, avail_out=%u, n=%u, zrc=%d\n",
		(unsigned)zs->avail_in, (unsigned)zs->avail_out,
					       (unsigned)n, zrc );

	if( (rc=iobuf_write( a, zfx->outbuf, n )) ) {
	    log_debug("deflate: iobuf_write failed\n");
	    return rc;
	}
    } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) );
    return 0;
}

Project: gnupg
File: compress.c

handle_compressed( void *procctx, PKT_compressed *cd,
		   int (*callback)(IOBUF, void *), void *passthru )
{
    compress_filter_context_t *cfx;
    int rc;

    if(check_compress_algo(cd->algorithm))
      return G10ERR_COMPR_ALGO;
    cfx = xmalloc_clear (sizeof *cfx);
    cfx->release = release_context;
    cfx->algo = cd->algorithm;
    push_compress_filter(cd->buf,cfx,cd->algorithm);
    if( callback )
	rc = callback(cd->buf, passthru );
    else
	rc = proc_packets(procctx, cd->buf);
    cd->buf = NULL;
    return rc;
}

Project: gnupg
File: compress.c

init_compress( compress_filter_context_t *zfx, z_stream *zs )
{
    int rc;
    int level;

#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
    static int zlib_initialized = 0;

    if (!zlib_initialized)
        zlib_initialized = riscos_load_module("ZLib", zlib_path, 1);
#endif

    if( opt.compress_level >= 1 && opt.compress_level <= 9 )
	level = opt.compress_level;
    else if( opt.compress_level == -1 )
	level = Z_DEFAULT_COMPRESSION;
    else {
	log_error("invalid compression level; using default level\n");
	level = Z_DEFAULT_COMPRESSION;
    }

    if( (rc = zfx->algo == 1? deflateInit2( zs, level, Z_DEFLATED,
					    -13, 8, Z_DEFAULT_STRATEGY)
			    : deflateInit( zs, level )
			    ) != Z_OK ) {
	log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
			       rc == Z_MEM_ERROR ? "out of core" :
			       rc == Z_VERSION_ERROR ? "invalid lib version" :
						       "unknown error" );
    }

    zfx->outbufsize = 8192;
    zfx->outbuf = xmalloc( zfx->outbufsize );
}

Project: gnupg
File: compress.c

init_uncompress( compress_filter_context_t *zfx, z_stream *zs )
{
    int rc;

    /****************
     * PGP uses a windowsize of 13 bits. Using a negative value for
     * it forces zlib not to expect a zlib header.  This is a
     * undocumented feature Peter Gutmann told me about.
     *    
     * We must use 15 bits for the inflator because CryptoEx uses 15
     * bits thus the output would get scrambled w/o error indication
     * if we would use 13 bits.  For the uncompressing this does not
     * matter at all.
     */
    if( (rc = zfx->algo == 1? inflateInit2( zs, -15)
			    : inflateInit( zs )) != Z_OK ) {
	log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
			       rc == Z_MEM_ERROR ? "out of core" :
			       rc == Z_VERSION_ERROR ? "invalid lib version" :
						       "unknown error" );
    }

    zfx->inbufsize = 2048;
    zfx->inbuf = xmalloc( zfx->inbufsize );
    zs->avail_in = 0;
}

Project: gnupg
File: compress.c

push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo)
{
  push_compress_filter2(out,zfx,algo,0);
}

Project: gnupg
File: compress.c

push_compress_filter2(IOBUF out,compress_filter_context_t *zfx,
		      int algo,int rel)
{
  if(algo>=0)
    zfx->algo=algo;
  else
    zfx->algo=DEFAULT_COMPRESS_ALGO;

  switch(zfx->algo)
    {
    case COMPRESS_ALGO_NONE:
      break;

    case COMPRESS_ALGO_ZIP:
    case COMPRESS_ALGO_ZLIB:
      iobuf_push_filter2(out,compress_filter,zfx,rel);
      break;

#ifdef HAVE_BZIP2
    case COMPRESS_ALGO_BZIP2:
      iobuf_push_filter2(out,compress_filter_bz2,zfx,rel);
      break;
#endif

    default:
      BUG();
    }
}

Project: gnupg
File: compress.c

release_context (compress_filter_context_t *ctx)
{
  xfree (ctx);
}