X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/a832773496d46caa5e328d36a15b4918f24a804e..5fd70d0768f86274b22e73f3cd8f59fec0325e36:/sshzlib.c diff --git a/sshzlib.c b/sshzlib.c index 58069b60..05fa5772 100644 --- a/sshzlib.c +++ b/sshzlib.c @@ -38,9 +38,28 @@ */ #include +#include #include +#ifdef ZLIB_STANDALONE + +/* + * This module also makes a handy zlib decoding tool for when + * you're picking apart Zip files or PDFs or PNGs. If you compile + * it with ZLIB_STANDALONE defined, it builds on its own and + * becomes a command-line utility. + * + * Therefore, here I provide a self-contained implementation of the + * macros required from the rest of the PuTTY sources. + */ +#define snew(type) ( (type *) malloc(sizeof(type)) ) +#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) ) +#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) ) +#define sfree(x) ( free((x)) ) + +#else #include "ssh.h" +#endif #ifndef FALSE #define FALSE 0 @@ -126,7 +145,7 @@ static int lz77_init(struct LZ77Context *ctx) struct LZ77InternalContext *st; int i; - st = (struct LZ77InternalContext *) smalloc(sizeof(*st)); + st = snew(struct LZ77InternalContext); if (!st) return 0; @@ -206,6 +225,7 @@ static void lz77_compress(struct LZ77Context *ctx, } st->npending -= i; + defermatch.distance = 0; /* appease compiler */ defermatch.len = 0; deferchr = '\0'; while (len > 0) { @@ -354,7 +374,7 @@ static void outbits(struct Outbuf *out, unsigned long bits, int nbits) while (out->noutbits >= 8) { if (out->outlen >= out->outsize) { out->outsize = out->outlen + 64; - out->outbuf = srealloc(out->outbuf, out->outsize); + out->outbuf = sresize(out->outbuf, out->outsize, unsigned char); } out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF); out->outbits >>= 8; @@ -583,13 +603,13 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len) void *zlib_compress_init(void) { struct Outbuf *out; - struct LZ77Context *ectx = smalloc(sizeof(struct LZ77Context)); + struct LZ77Context *ectx = snew(struct LZ77Context); lz77_init(ectx); ectx->literal = zlib_literal; ectx->match = zlib_match; - out = smalloc(sizeof(struct Outbuf)); + out = snew(struct Outbuf); out->outbits = out->noutbits = 0; out->firstblock = 1; out->comp_disabled = FALSE; @@ -602,6 +622,8 @@ void zlib_compress_cleanup(void *handle) { struct LZ77Context *ectx = (struct LZ77Context *)handle; sfree(ectx->userdata); + sfree(ectx->ictx); + sfree(ectx); } /* @@ -610,7 +632,7 @@ void zlib_compress_cleanup(void *handle) * length adjustment (which is only valid for packets < 65536 * bytes, but that seems reasonable enough). */ -int zlib_disable_compression(void *handle) +static int zlib_disable_compression(void *handle) { struct LZ77Context *ectx = (struct LZ77Context *)handle; struct Outbuf *out = (struct Outbuf *) ectx->userdata; @@ -678,9 +700,9 @@ int zlib_compress_block(void *handle, unsigned char *block, int len, /* * Start a Deflate (RFC1951) uncompressed block. We - * transmit a zero bit (BFINAL=0), followed by a zero - * bit and a one bit (BTYPE=00). Of course these are in - * the wrong order (00 0). + * transmit a zero bit (BFINAL=0), followed by two more + * zero bits (BTYPE=00). Of course these are in the + * wrong order (00 0), not that it matters. */ outbits(out, 0, 3); @@ -806,11 +828,11 @@ static struct zlib_table *zlib_mkonetab(int *codes, unsigned char *lengths, int nsyms, int pfx, int pfxbits, int bits) { - struct zlib_table *tab = smalloc(sizeof(struct zlib_table)); + struct zlib_table *tab = snew(struct zlib_table); int pfxmask = (1 << pfxbits) - 1; int nbits, i, j, code; - tab->table = smalloc((1 << bits) * sizeof(struct zlib_tableentry)); + tab->table = snewn(1 << bits, struct zlib_tableentry); tab->mask = (1 << bits) - 1; for (code = 0; code <= tab->mask; code++) { @@ -941,8 +963,7 @@ struct zlib_decompress_ctx { void *zlib_decompress_init(void) { - struct zlib_decompress_ctx *dctx = - smalloc(sizeof(struct zlib_decompress_ctx)); + struct zlib_decompress_ctx *dctx = snew(struct zlib_decompress_ctx); unsigned char lengths[288]; memset(lengths, 8, 144); @@ -964,17 +985,19 @@ void *zlib_decompress_init(void) void zlib_decompress_cleanup(void *handle) { struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle; - + if (dctx->currlentable && dctx->currlentable != dctx->staticlentable) zlib_freetable(&dctx->currlentable); if (dctx->currdisttable && dctx->currdisttable != dctx->staticdisttable) zlib_freetable(&dctx->currdisttable); if (dctx->lenlentable) zlib_freetable(&dctx->lenlentable); + zlib_freetable(&dctx->staticlentable); + zlib_freetable(&dctx->staticdisttable); sfree(dctx); } -int zlib_huflookup(unsigned long *bitsp, int *nbitsp, +static int zlib_huflookup(unsigned long *bitsp, int *nbitsp, struct zlib_table *tab) { unsigned long bits = *bitsp; @@ -993,6 +1016,16 @@ int zlib_huflookup(unsigned long *bitsp, int *nbitsp, *nbitsp = nbits; return ent->code; } + + if (!tab) { + /* + * There was a missing entry in the table, presumably + * due to an invalid Huffman table description, and the + * subsequent data has attempted to use the missing + * entry. Return a decoding failure. + */ + return -2; + } } } @@ -1002,7 +1035,7 @@ static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c) dctx->winpos = (dctx->winpos + 1) & (WINSIZE - 1); if (dctx->outlen >= dctx->outsize) { dctx->outsize = dctx->outlen + 512; - dctx->outblk = srealloc(dctx->outblk, dctx->outsize); + dctx->outblk = sresize(dctx->outblk, dctx->outsize, unsigned char); } dctx->outblk[dctx->outlen++] = c; } @@ -1014,13 +1047,14 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, { struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle; const coderecord *rec; - int code, blktype, rep, dist, nlen; + int code, blktype, rep, dist, nlen, header; static const unsigned char lenlenmap[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - dctx->outblk = NULL; - dctx->outsize = dctx->outlen = 0; + dctx->outblk = snewn(256, unsigned char); + dctx->outsize = 256; + dctx->outlen = 0; while (len > 0 || dctx->nbits > 0) { while (dctx->nbits < 24 && len > 0) { @@ -1030,10 +1064,35 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, } switch (dctx->state) { case START: - /* Expect 16-bit zlib header, which we'll dishonourably ignore. */ + /* Expect 16-bit zlib header. */ if (dctx->nbits < 16) goto finished; /* done all we can */ - EATBITS(16); + + /* + * The header is stored as a big-endian 16-bit integer, + * in contrast to the general little-endian policy in + * the rest of the format :-( + */ + header = (((dctx->bits & 0xFF00) >> 8) | + ((dctx->bits & 0x00FF) << 8)); + EATBITS(16); + + /* + * Check the header: + * + * - bits 8-11 should be 1000 (Deflate/RFC1951) + * - bits 12-15 should be at most 0111 (window size) + * - bit 5 should be zero (no dictionary present) + * - we don't care about bits 6-7 (compression rate) + * - bits 0-4 should be set up to make the whole thing + * a multiple of 31 (checksum). + */ + if ((header & 0x0F00) != 0x0800 || + (header & 0xF000) > 0x7000 || + (header & 0x0020) != 0x0000 || + (header % 31) != 0) + goto decode_error; + dctx->state = OUTSIDEBLK; break; case OUTSIDEBLK: @@ -1100,6 +1159,8 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->lenlentable); if (code == -1) goto finished; + if (code == -2) + goto decode_error; if (code < 16) dctx->lengths[dctx->lenptr++] = code; else { @@ -1129,6 +1190,8 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->currlentable); if (code == -1) goto finished; + if (code == -2) + goto decode_error; if (code < 256) zlib_emit_char(dctx, code); else if (code == 256) { @@ -1161,6 +1224,8 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, dctx->currdisttable); if (code == -1) goto finished; + if (code == -2) + goto decode_error; dctx->state = GOTDISTSYM; dctx->sym = code; break; @@ -1195,6 +1260,8 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, goto finished; nlen = dctx->bits & 0xFFFF; EATBITS(16); + if (dctx->uncomplen != (nlen ^ 0xFFFF)) + goto decode_error; if (dctx->uncomplen == 0) dctx->state = OUTSIDEBLK; /* block is empty */ else @@ -1214,12 +1281,98 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, finished: *outblock = dctx->outblk; *outlen = dctx->outlen; - return 1; + + decode_error: + sfree(dctx->outblk); + *outblock = dctx->outblk = NULL; + *outlen = 0; + return 0; } +#ifdef ZLIB_STANDALONE + +#include +#include + +int main(int argc, char **argv) +{ + unsigned char buf[16], *outbuf; + int ret, outlen; + void *handle; + int noheader = FALSE, opts = TRUE; + char *filename = NULL; + FILE *fp; + + while (--argc) { + char *p = *++argv; + + if (p[0] == '-' && opts) { + if (!strcmp(p, "-d")) + noheader = TRUE; + else if (!strcmp(p, "--")) + opts = FALSE; /* next thing is filename */ + else { + fprintf(stderr, "unknown command line option '%s'\n", p); + return 1; + } + } else if (!filename) { + filename = p; + } else { + fprintf(stderr, "can only handle one filename\n"); + return 1; + } + } + + handle = zlib_decompress_init(); + + if (noheader) { + /* + * Provide missing zlib header if -d was specified. + */ + zlib_decompress_block(handle, "\x78\x9C", 2, &outbuf, &outlen); + assert(outlen == 0); + } + + if (filename) + fp = fopen(filename, "rb"); + else + fp = stdin; + + if (!fp) { + assert(filename); + fprintf(stderr, "unable to open '%s'\n", filename); + return 1; + } + + while (1) { + ret = fread(buf, 1, sizeof(buf), fp); + if (ret <= 0) + break; + zlib_decompress_block(handle, buf, ret, &outbuf, &outlen); + if (outbuf) { + if (outlen) + fwrite(outbuf, 1, outlen, stdout); + sfree(outbuf); + } else { + fprintf(stderr, "decoding error\n"); + return 1; + } + } + + zlib_decompress_cleanup(handle); + + if (filename) + fclose(fp); + + return 0; +} + +#else + const struct ssh_compress ssh_zlib = { "zlib", + "zlib@openssh.com", /* delayed version */ zlib_compress_init, zlib_compress_cleanup, zlib_compress_block, @@ -1229,3 +1382,5 @@ const struct ssh_compress ssh_zlib = { zlib_disable_compression, "zlib (RFC1950)" }; + +#endif