Coverage Report

Created: 2026-01-13 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libhtp/htp/htp_decompressors.c
Line
Count
Source
1
/***************************************************************************
2
 * Copyright (c) 2009-2010 Open Information Security Foundation
3
 * Copyright (c) 2010-2013 Qualys, Inc.
4
 * All rights reserved.
5
 * 
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are
8
 * met:
9
 * 
10
 * - Redistributions of source code must retain the above copyright
11
 *   notice, this list of conditions and the following disclaimer.
12
13
 * - Redistributions in binary form must reproduce the above copyright
14
 *   notice, this list of conditions and the following disclaimer in the
15
 *   documentation and/or other materials provided with the distribution.
16
17
 * - Neither the name of the Qualys, Inc. nor the names of its
18
 *   contributors may be used to endorse or promote products derived from
19
 *   this software without specific prior written permission.
20
 * 
21
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 ***************************************************************************/
33
34
/**
35
 * @file
36
 * @author Ivan Ristic <ivanr@webkreator.com>
37
 */
38
39
#include "htp_config_auto.h"
40
41
#include "htp_private.h"
42
43
44
29.4k
static void *SzAlloc(ISzAllocPtr p, size_t size) { return malloc(size); }
45
61.1k
static void SzFree(ISzAllocPtr p, void *address) { free(address); }
46
const ISzAlloc lzma_Alloc = { SzAlloc, SzFree };
47
48
49
/**
50
 *  @brief See if the header has extensions
51
 *  @return number of bytes to skip
52
 */
53
static size_t htp_gzip_decompressor_probe(const unsigned char *data,
54
                                       size_t data_len)
55
15.7k
{
56
15.7k
    if (data_len < 4)
57
3.95k
        return 0;
58
59
11.8k
    size_t consumed = 0;
60
61
11.8k
    if (data[0] == 0x1f && data[1] == 0x8b && data[3] != 0) {
62
3.81k
        if (data[3] & (1 << 3) || data[3] & (1 << 4)) {
63
            /* skip past
64
             * - FNAME extension, which is a name ended in a NUL terminator
65
             * or
66
             * - FCOMMENT extension, which is a commend ended in a NULL terminator
67
             */
68
69
2.61k
            size_t len;
70
93.5k
            for (len = 10; len < data_len && data[len] != '\0'; len++);
71
2.61k
            consumed = len + 1;
72
73
            //printf("skipped %u bytes for FNAME/FCOMMENT header (GZIP)\n", (uint)consumed);
74
75
2.61k
        } else if (data[3] & (1 << 1)) {
76
594
            consumed = 12;
77
            //printf("skipped %u bytes for FHCRC header (GZIP)\n", 12);
78
79
608
        } else {
80
            //printf("GZIP unknown/unsupported flags %02X\n", data[3]);
81
608
            consumed = 10;
82
608
        }
83
3.81k
    }
84
85
11.8k
    if (consumed > data_len)
86
3.05k
        return 0;
87
88
8.77k
    return consumed;
89
11.8k
}
90
91
/**
92
 *  @brief restart the decompressor
93
 *  @return 1 if it restarted, 0 otherwise
94
 */
95
static int htp_gzip_decompressor_restart(htp_decompressor_gzip_t *drec,
96
                                         const unsigned char *data,
97
                                         size_t data_len, size_t *consumed_back)
98
20.7k
{
99
20.7k
    size_t consumed = 0;
100
20.7k
    int rc = 0;
101
102
20.7k
    if (drec->restart < 3) {
103
104
        // first retry with the existing type, but now consider the
105
        // extensions
106
17.4k
        if (drec->restart == 0) {
107
7.46k
            consumed = htp_gzip_decompressor_probe(data, data_len);
108
109
7.46k
            if (drec->zlib_initialized == HTP_COMPRESSION_GZIP) {
110
                //printf("GZIP restart, consumed %u\n", (uint)consumed);
111
4.95k
                rc = inflateInit2(&drec->stream, 15 + 32);
112
4.95k
            } else {
113
                //printf("DEFLATE restart, consumed %u\n", (uint)consumed);
114
2.51k
                rc = inflateInit2(&drec->stream, -15);
115
2.51k
            }
116
7.46k
            if (rc != Z_OK)
117
0
                return 0;
118
119
7.46k
            goto restart;
120
121
        // if that still fails, try the other method we support
122
123
9.95k
        } else if (drec->zlib_initialized == HTP_COMPRESSION_DEFLATE) {
124
3.39k
            rc = inflateInit2(&drec->stream, 15 + 32);
125
3.39k
            if (rc != Z_OK)
126
0
                return 0;
127
128
3.39k
            drec->zlib_initialized = HTP_COMPRESSION_GZIP;
129
3.39k
            consumed = htp_gzip_decompressor_probe(data, data_len);
130
#if 0
131
            printf("DEFLATE -> GZIP consumed %u\n", (uint)consumed);
132
#endif
133
3.39k
            goto restart;
134
135
6.56k
        } else if (drec->zlib_initialized == HTP_COMPRESSION_GZIP) {
136
4.92k
            rc = inflateInit2(&drec->stream, -15);
137
4.92k
            if (rc != Z_OK)
138
0
                return 0;
139
140
4.92k
            drec->zlib_initialized = HTP_COMPRESSION_DEFLATE;
141
4.92k
            consumed = htp_gzip_decompressor_probe(data, data_len);
142
#if 0
143
            printf("GZIP -> DEFLATE consumed %u\n", (uint)consumed);
144
#endif
145
4.92k
            goto restart;
146
4.92k
        }
147
17.4k
    }
148
5.01k
    return 0;
149
150
15.7k
restart:
151
#if 0
152
    gz_header y;
153
    gz_headerp x = &y;
154
    int res = inflateGetHeader(&drec->stream, x);
155
    printf("HEADER res %d x.os %d x.done %d\n", res, x->os, x->done);
156
#endif
157
15.7k
    *consumed_back = consumed;
158
15.7k
    drec->restart++;
159
15.7k
    return 1;
160
20.7k
}
161
162
/**
163
 * Ends decompressor.
164
 *
165
 * @param[in] drec
166
 */
167
985k
static void htp_gzip_decompressor_end(htp_decompressor_gzip_t *drec) {
168
985k
    if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) {
169
10.4k
        LzmaDec_Free(&drec->state, &lzma_Alloc);
170
10.4k
        drec->zlib_initialized = 0;
171
975k
    } else if (drec->zlib_initialized) {
172
8.35k
        inflateEnd(&drec->stream);
173
8.35k
        drec->zlib_initialized = 0;
174
8.35k
    }
175
985k
}
176
177
/**
178
 * Decompress a chunk of gzip-compressed data.
179
 * If we have more than one decompressor, call this function recursively.
180
 *
181
 * @param[in] drec
182
 * @param[in] d
183
 * @return HTP_OK on success, HTP_ERROR or some other negative integer on failure.
184
 */
185
3.11M
htp_status_t htp_gzip_decompressor_decompress(htp_decompressor_t *drec1, htp_tx_data_t *d) {
186
3.11M
    size_t consumed = 0;
187
3.11M
    int rc = 0;
188
3.11M
    htp_status_t callback_rc;
189
3.11M
    htp_decompressor_gzip_t *drec = (htp_decompressor_gzip_t*) drec1;
190
191
    // Pass-through the NULL chunk, which indicates the end of the stream.
192
193
3.11M
    if (drec->super.passthrough) {
194
881k
        htp_tx_data_t d2;
195
881k
        d2.tx = d->tx;
196
881k
        d2.data = d->data;
197
881k
        d2.len = d->len;
198
881k
        d2.is_last = d->is_last;
199
200
881k
        callback_rc = drec->super.callback(&d2);
201
881k
        if (callback_rc != HTP_OK) {
202
795k
            return HTP_ERROR;
203
795k
        }
204
205
85.1k
        return HTP_OK;
206
2.23M
    } else if (drec->zlib_initialized == HTP_COMPRESSION_OVER) {
207
28.2k
        return HTP_ERROR;
208
28.2k
    }
209
210
2.20M
    if (d->data == NULL) {
211
        // Prepare data for callback.
212
5.81k
        htp_tx_data_t dout;
213
5.81k
        dout.tx = d->tx;
214
        // This is last call, so output uncompressed data so far
215
5.81k
        dout.len = GZIP_BUF_SIZE - drec->stream.avail_out;
216
5.81k
        if (dout.len > 0) {
217
4.61k
            dout.data = drec->buffer;
218
4.61k
        } else {
219
1.20k
            dout.data = NULL;
220
1.20k
        }
221
5.81k
        dout.is_last = d->is_last;
222
5.81k
        if (drec->super.next != NULL && drec->zlib_initialized) {
223
392
            return htp_gzip_decompressor_decompress(drec->super.next, &dout);
224
5.41k
        } else {
225
            // Send decompressed data to the callback.
226
5.41k
            callback_rc = drec->super.callback(&dout);
227
5.41k
            if (callback_rc != HTP_OK) {
228
640
                htp_gzip_decompressor_end(drec);
229
640
                return callback_rc;
230
640
            }
231
5.41k
        }
232
233
4.77k
        return HTP_OK;
234
5.81k
    }
235
236
2.21M
restart:
237
2.21M
    if (consumed > d->len || d->len > UINT32_MAX ) {
238
0
        htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "GZip decompressor: consumed > d->len");
239
0
        return HTP_ERROR;
240
0
    }
241
2.21M
    drec->stream.next_in = (unsigned char *) (d->data + consumed);
242
2.21M
    drec->stream.avail_in = (uint32_t) (d->len - consumed);
243
244
4.90M
    while (drec->stream.avail_in != 0) {
245
        // If there's no more data left in the
246
        // buffer, send that information out.
247
4.71M
        if (drec->stream.avail_out == 0) {
248
3.45M
            drec->crc = crc32(drec->crc, drec->buffer, GZIP_BUF_SIZE);
249
250
            // Prepare data for callback.
251
3.45M
            htp_tx_data_t d2;
252
3.45M
            d2.tx = d->tx;
253
3.45M
            d2.data = drec->buffer;
254
3.45M
            d2.len = GZIP_BUF_SIZE;
255
3.45M
            d2.is_last = d->is_last;
256
257
3.45M
            if (drec->super.next != NULL && drec->zlib_initialized) {
258
1.16M
                callback_rc = htp_gzip_decompressor_decompress(drec->super.next, &d2);
259
2.29M
            } else {
260
                // Send decompressed data to callback.
261
2.29M
                callback_rc = drec->super.callback(&d2);
262
2.29M
            }
263
3.45M
            if (callback_rc != HTP_OK) {
264
960k
                htp_gzip_decompressor_end(drec);
265
960k
                return callback_rc;
266
960k
            }
267
268
2.49M
            drec->stream.next_out = drec->buffer;
269
2.49M
            drec->stream.avail_out = GZIP_BUF_SIZE;
270
2.49M
        }
271
272
3.75M
        if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) {
273
1.80M
            if (drec->header_len < LZMA_PROPS_SIZE + 8) {
274
20.5k
                consumed = LZMA_PROPS_SIZE + 8 - drec->header_len;
275
20.5k
                if (consumed > drec->stream.avail_in) {
276
5.74k
                    consumed = drec->stream.avail_in;
277
5.74k
                }
278
20.5k
                memcpy(drec->header + drec->header_len, drec->stream.next_in, consumed);
279
20.5k
                drec->stream.next_in = (unsigned char *) (d->data + consumed);
280
20.5k
                drec->stream.avail_in = (uint32_t) (d->len - consumed);
281
20.5k
                drec->header_len += consumed;
282
20.5k
            }
283
1.80M
            if (drec->header_len == LZMA_PROPS_SIZE + 8) {
284
15.4k
                rc = LzmaDec_Allocate(&drec->state, drec->header, LZMA_PROPS_SIZE, &lzma_Alloc);
285
15.4k
                if (rc != SZ_OK)
286
674
                    return rc;
287
14.7k
                LzmaDec_Init(&drec->state);
288
                // hacky to get to next step end retry allocate in case of failure
289
14.7k
                drec->header_len++;
290
14.7k
            }
291
1.79M
            if (drec->header_len > LZMA_PROPS_SIZE + 8) {
292
1.79M
                size_t inprocessed = drec->stream.avail_in;
293
1.79M
                size_t outprocessed = drec->stream.avail_out;
294
1.79M
                ELzmaStatus status;
295
1.79M
                rc = LzmaDec_DecodeToBuf(&drec->state, drec->stream.next_out, &outprocessed,
296
1.79M
                                          drec->stream.next_in, &inprocessed, LZMA_FINISH_ANY, &status, d->tx->cfg->lzma_memlimit);
297
1.79M
                drec->stream.avail_in -= inprocessed;
298
1.79M
                drec->stream.next_in += inprocessed;
299
1.79M
                drec->stream.avail_out -= outprocessed;
300
1.79M
                drec->stream.next_out += outprocessed;
301
1.79M
                switch (rc) {
302
1.78M
                    case SZ_OK:
303
1.78M
                        rc = Z_OK;
304
1.78M
                        if (status == LZMA_STATUS_FINISHED_WITH_MARK) {
305
262
                            rc = Z_STREAM_END;
306
262
                        }
307
1.78M
                        break;
308
409
                    case SZ_ERROR_MEM:
309
409
                        htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "LZMA decompressor: memory limit reached");
310
                        // fall through
311
5.69k
                    default:
312
5.69k
                        rc = Z_DATA_ERROR;
313
1.79M
                }
314
1.79M
            }
315
1.95M
        } else if (drec->zlib_initialized) {
316
1.94M
            rc = inflate(&drec->stream, Z_NO_FLUSH);
317
1.94M
        } else {
318
            // no initialization means previous error on stream
319
2.23k
            return HTP_ERROR;
320
2.23k
        }
321
3.74M
        int error_after_data = (rc == Z_DATA_ERROR && drec->restart == 0 && GZIP_BUF_SIZE > drec->stream.avail_out);
322
3.74M
        if (rc == Z_STREAM_END || error_after_data) {
323
            // How many bytes do we have?
324
1.04M
            size_t len = GZIP_BUF_SIZE - drec->stream.avail_out;
325
326
            // Update CRC
327
328
            // Prepare data for the callback.
329
1.04M
            htp_tx_data_t d2;
330
1.04M
            d2.tx = d->tx;
331
1.04M
            d2.data = drec->buffer;
332
1.04M
            d2.len = len;
333
1.04M
            d2.is_last = d->is_last;
334
335
1.04M
            if (drec->super.next != NULL && drec->zlib_initialized) {
336
1.73k
                callback_rc = htp_gzip_decompressor_decompress(drec->super.next, &d2);
337
1.03M
            } else {
338
                // Send decompressed data to the callback.
339
1.03M
                callback_rc = drec->super.callback(&d2);
340
1.03M
            }
341
1.04M
            if (callback_rc != HTP_OK) {
342
736
                htp_gzip_decompressor_end(drec);
343
736
                return callback_rc;
344
736
            }
345
1.03M
            drec->stream.avail_out = GZIP_BUF_SIZE;
346
1.03M
            drec->stream.next_out = drec->buffer;
347
            // TODO Handle trailer.
348
349
1.03M
            if (error_after_data) {
350
                // There is data even if there is an error
351
                // So use this data and log a warning
352
3.70k
                htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "GZip decompressor: inflate failed with %d", rc);
353
3.70k
                if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) {
354
2.84k
                    LzmaDec_Free(&drec->state, &lzma_Alloc);
355
2.84k
                }
356
3.70k
                drec->zlib_initialized = HTP_COMPRESSION_OVER;
357
3.70k
                return HTP_ERROR;
358
3.70k
            }
359
1.03M
            return HTP_OK;
360
1.03M
        }
361
2.70M
        else if (rc != Z_OK) {
362
20.7k
            htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "GZip decompressor: inflate failed with %d", rc);
363
20.7k
            if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) {
364
2.51k
                LzmaDec_Free(&drec->state, &lzma_Alloc);
365
                // so as to clean zlib ressources after restart
366
2.51k
                drec->zlib_initialized = HTP_COMPRESSION_NONE;
367
18.2k
            } else {
368
18.2k
                inflateEnd(&drec->stream);
369
18.2k
            }
370
371
            // see if we want to restart the decompressor
372
20.7k
            if (htp_gzip_decompressor_restart(drec,
373
20.7k
                                              d->data, d->len, &consumed) == 1)
374
15.7k
            {
375
                // we'll be restarting the compressor
376
15.7k
                goto restart;
377
15.7k
            }
378
379
5.01k
            drec->zlib_initialized = 0;
380
381
            // all our inflate attempts have failed, simply
382
            // pass the raw data on to the callback in case
383
            // it's not compressed at all
384
385
5.01k
            htp_tx_data_t d2;
386
5.01k
            d2.tx = d->tx;
387
5.01k
            d2.data = d->data;
388
5.01k
            d2.len = d->len;
389
5.01k
            d2.is_last = d->is_last;
390
391
5.01k
            callback_rc = drec->super.callback(&d2);
392
5.01k
            if (callback_rc != HTP_OK) {
393
416
                return HTP_ERROR;
394
416
            }
395
396
4.59k
            drec->stream.avail_out = GZIP_BUF_SIZE;
397
4.59k
            drec->stream.next_out = drec->buffer;
398
399
            /* successfully passed through, lets continue doing that */
400
4.59k
            drec->super.passthrough = 1;
401
4.59k
            return HTP_OK;
402
5.01k
        }
403
3.74M
    }
404
405
191k
    return HTP_OK;
406
2.21M
}
407
408
/**
409
 * Shut down gzip decompressor.
410
 *
411
 * @param[in] drec
412
 */
413
23.8k
void htp_gzip_decompressor_destroy(htp_decompressor_t *drec1) {
414
23.8k
    htp_decompressor_gzip_t *drec = (htp_decompressor_gzip_t*) drec1;
415
23.8k
    if (drec == NULL) return;
416
417
23.8k
    htp_gzip_decompressor_end(drec);
418
419
23.8k
    free(drec->buffer);
420
23.8k
    free(drec);
421
23.8k
}
422
423
/**
424
 * Create a new decompressor instance.
425
 *
426
 * @param[in] connp
427
 * @param[in] format
428
 * @return New htp_decompressor_t instance on success, or NULL on failure.
429
 */
430
23.8k
htp_decompressor_t *htp_gzip_decompressor_create(htp_connp_t *connp, enum htp_content_encoding_t format) {
431
23.8k
    htp_decompressor_gzip_t *drec = calloc(1, sizeof (htp_decompressor_gzip_t));
432
23.8k
    if (drec == NULL) return NULL;
433
434
23.8k
    drec->super.decompress = NULL;
435
23.8k
    drec->super.destroy = NULL;
436
23.8k
    drec->super.next = NULL;
437
438
23.8k
    drec->buffer = malloc(GZIP_BUF_SIZE);
439
23.8k
    if (drec->buffer == NULL) {
440
0
        free(drec);
441
0
        return NULL;
442
0
    }
443
444
    // Initialize zlib.
445
23.8k
    int rc;
446
447
23.8k
    switch (format) {
448
15.8k
        case HTP_COMPRESSION_LZMA:
449
15.8k
            if (connp->cfg->lzma_memlimit > 0 &&
450
15.8k
                connp->cfg->response_lzma_layer_limit > 0) {
451
15.8k
                LzmaDec_Construct(&drec->state);
452
15.8k
            } else {
453
0
                htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "LZMA decompression disabled");
454
0
                drec->super.passthrough = 1;
455
0
            }
456
15.8k
            rc = Z_OK;
457
15.8k
            break;
458
0
        case HTP_COMPRESSION_DEFLATE:
459
            // Negative values activate raw processing,
460
            // which is what we need for deflate.
461
0
            rc = inflateInit2(&drec->stream, -15);
462
0
            break;
463
8.00k
        case HTP_COMPRESSION_GZIP:
464
            // Increased windows size activates gzip header processing.
465
8.00k
            rc = inflateInit2(&drec->stream, 15 + 32);
466
8.00k
            break;
467
0
        default:
468
            // do nothing
469
0
            rc = Z_DATA_ERROR;
470
23.8k
    }
471
472
23.8k
    if (rc != Z_OK) {
473
0
        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "GZip decompressor: inflateInit2 failed with code %d", rc);
474
475
0
        if (format == HTP_COMPRESSION_DEFLATE || format == HTP_COMPRESSION_GZIP) {
476
0
            inflateEnd(&drec->stream);
477
0
        }
478
0
        free(drec->buffer);
479
0
        free(drec);
480
481
0
        return NULL;
482
0
    }
483
484
23.8k
    drec->zlib_initialized = format;
485
23.8k
    drec->stream.avail_out = GZIP_BUF_SIZE;
486
23.8k
    drec->stream.next_out = drec->buffer;
487
488
    #if 0
489
    if (format == COMPRESSION_DEFLATE) {
490
        drec->initialized = 1;
491
    }
492
    #endif
493
494
23.8k
    return (htp_decompressor_t *) drec;
495
23.8k
}