~ubuntu-branches/ubuntu/jaunty/clamav/jaunty-backports

« back to all changes in this revision

Viewing changes to libclamav/pdf.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2010-10-02 15:36:00 UTC
  • mfrom: (10.1.6 sid)
  • mto: This revision was merged to the branch mainline in revision 13.
  • Revision ID: james.westby@ubuntu.com-20101002153600-2tx3vki1u55cdrjy
Tags: 0.96.3+dfsg-2ubuntu0.10.04.1
Microversion update to 0.96.3 for Lucid (LP: #653738)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 *  Copyright (C) 2007-2008 Sourcefire, Inc.
3
 
 *
4
 
 *  Authors: Nigel Horne
 
2
 *  Copyright (C) 2007-2008, 2010 Sourcefire, Inc.
 
3
 *
 
4
 *  Authors: Nigel Horne, Török Edvin
 
5
 *
 
6
 *  Also based on Matt Olney's pdf parser in snort-nrt.
5
7
 *
6
8
 *  This program is free software; you can redistribute it and/or modify
7
9
 *  it under the terms of the GNU General Public License version 2 as
48
50
#include "scanners.h"
49
51
#include "fmap.h"
50
52
#include "str.h"
 
53
#include "bytecode.h"
 
54
#include "bytecode_api.h"
51
55
 
52
56
#ifdef  CL_DEBUG
53
57
/*#define       SAVE_TMP        
54
58
 *Save the file being worked on in tmp */
55
59
#endif
56
60
 
57
 
static  int     try_flatedecode(unsigned char *buf, off_t real_len, off_t calculated_len, int fout, cli_ctx *ctx);
58
 
static  int     flatedecode(unsigned char *buf, off_t len, int fout, cli_ctx *ctx);
 
61
static  int     asciihexdecode(const char *buf, off_t len, char *output);
59
62
static  int     ascii85decode(const char *buf, off_t len, unsigned char *output);
60
63
static  const   char    *pdf_nextlinestart(const char *ptr, size_t len);
61
64
static  const   char    *pdf_nextobject(const char *ptr, size_t len);
62
65
 
 
66
#if 1
 
67
static int xrefCheck(const char *xref, const char *eof)
 
68
{
 
69
    const char *q;
 
70
    while (xref < eof && (*xref == ' ' || *xref == '\n' || *xref == '\r'))
 
71
        xref++;
 
72
    if (xref + 4 >= eof)
 
73
        return -1;
 
74
    if (!memcmp(xref, "xref", 4)) {
 
75
        cli_dbgmsg("cli_pdf: found xref\n");
 
76
        return 0;
 
77
    }
 
78
    /* could be xref stream */
 
79
    for (q=xref; q+5 < eof; q++) {
 
80
        if (!memcmp(q,"/XRef",4)) {
 
81
            cli_dbgmsg("cli_pdf: found /XRef\n");
 
82
            return 0;
 
83
        }
 
84
    }
 
85
    return -1;
 
86
}
 
87
 
 
88
struct pdf_struct {
 
89
    struct pdf_obj *objs;
 
90
    unsigned nobjs;
 
91
    unsigned flags;
 
92
    const char *map;
 
93
    off_t size;
 
94
    off_t offset;
 
95
    off_t startoff;
 
96
    cli_ctx *ctx;
 
97
    const char *dir;
 
98
    unsigned files;
 
99
};
 
100
 
 
101
static const char *findNextNonWSBack(const char *q, const char *start)
 
102
{
 
103
    while (q > start &&
 
104
           (*q == 0 || *q == 9 || *q == 0xa || *q == 0xc || *q == 0xd || *q == 0x20))
 
105
    {
 
106
        q--;
 
107
    }
 
108
    return q;
 
109
}
 
110
 
 
111
static int find_stream_bounds(const char *start, off_t bytesleft, off_t bytesleft2, off_t *stream, off_t *endstream)
 
112
{
 
113
    const char *q2, *q;
 
114
    if ((q2 = cli_memstr(start, bytesleft, "stream", 6))) {
 
115
        q2 += 6;
 
116
        if (q2[0] == '\xd' && q2[1] == '\xa')
 
117
            q2 += 2;
 
118
        if (q2[0] == '\xa')
 
119
            q2++;
 
120
        *stream = q2 - start;
 
121
        bytesleft2 -= q2 - start;
 
122
        if (bytesleft2 < 0)
 
123
            return 0;
 
124
        q = q2;
 
125
        q2 = cli_memstr(q, bytesleft2, "endstream", 9);
 
126
        if (!q2)
 
127
            q2 = q + bytesleft2-9; /* till EOF */
 
128
        *endstream = q2 - start;
 
129
        if (*endstream < *stream)
 
130
            *endstream = *stream;
 
131
        return 1;
 
132
    }
 
133
    return 0;
 
134
}
 
135
 
 
136
static int pdf_findobj(struct pdf_struct *pdf)
 
137
{
 
138
    const char *start, *q, *q2, *q3, *eof;
 
139
    struct pdf_obj *obj;
 
140
    off_t bytesleft;
 
141
    unsigned genid, objid;
 
142
 
 
143
    pdf->nobjs++;
 
144
    pdf->objs = cli_realloc2(pdf->objs, sizeof(*pdf->objs)*pdf->nobjs);
 
145
    if (!pdf->objs) {
 
146
        cli_warnmsg("cli_pdf: out of memory parsing objects (%u)\n", pdf->nobjs);
 
147
        return -1;
 
148
    }
 
149
    obj = &pdf->objs[pdf->nobjs-1];
 
150
    memset(obj, 0, sizeof(*obj));
 
151
    start = pdf->map+pdf->offset;
 
152
    bytesleft = pdf->size - pdf->offset;
 
153
    while (bytesleft > 0) {
 
154
        q2 = cli_memstr(start, bytesleft, "obj", 3);
 
155
        if (!q2)
 
156
            return 0;/* no more objs */
 
157
        q2--;
 
158
        bytesleft -= q2 - start;
 
159
        if (*q2 != 0 && *q2 != 9 && *q2 != 0xa && *q2 != 0xc && *q2 != 0xd && *q2 != 0x20) {
 
160
            start = q2+4;
 
161
            bytesleft -= 4;
 
162
            continue;
 
163
        }
 
164
        break;
 
165
    }
 
166
    if (bytesleft <= 0)
 
167
        return 0;
 
168
 
 
169
    q = findNextNonWSBack(q2-1, start);
 
170
    while (q > start && isdigit(*q)) { q--; }
 
171
    genid = atoi(q);
 
172
    q = findNextNonWSBack(q-1,start);
 
173
    while (q > start && isdigit(*q)) { q--; }
 
174
    objid = atoi(q);
 
175
    obj->id = (objid << 8) | (genid&0xff);
 
176
    obj->start = q2+4 - pdf->map;
 
177
    obj->flags = 0;
 
178
    bytesleft -= 4;
 
179
    eof = pdf->map + pdf->size;
 
180
    q = pdf->map + obj->start;
 
181
    while (q < eof && bytesleft > 0) {
 
182
        off_t p_stream, p_endstream;
 
183
        q2 = pdf_nextobject(q, bytesleft);
 
184
        if (!q2)
 
185
            q2 = pdf->map + pdf->size;
 
186
        bytesleft -= q2 - q;
 
187
        if (find_stream_bounds(q-1, q2-q, bytesleft + (q2-q), &p_stream, &p_endstream)) {
 
188
            obj->flags |= 1 << OBJ_STREAM;
 
189
            q2 = q-1 + p_endstream + 9;
 
190
            bytesleft -= q2 - q + 1;
 
191
            if (bytesleft < 0) {
 
192
                obj->flags |= 1 << OBJ_TRUNCATED;
 
193
                pdf->offset = pdf->size;
 
194
                return 1;/* truncated */
 
195
            }
 
196
        } else if ((q3 = cli_memstr(q-1, q2-q+1, "endobj", 6))) {
 
197
            q2 = q3 + 6;
 
198
            pdf->offset = q2 - pdf->map;
 
199
            return 1; /* obj found and offset positioned */
 
200
        } else {
 
201
            q2++;
 
202
            bytesleft--;
 
203
        }
 
204
        q = q2;
 
205
    }
 
206
    obj->flags |= 1 << OBJ_TRUNCATED;
 
207
    pdf->offset = pdf->size;
 
208
    return 1;/* truncated */
 
209
}
 
210
 
 
211
static int filter_writen(struct pdf_struct *pdf, struct pdf_obj *obj,
 
212
                         int fout, const char *buf, off_t len, off_t *sum)
 
213
{
 
214
    if (cli_checklimits("pdf", pdf->ctx, *sum, 0, 0))
 
215
        return len; /* pretend it was a successful write to suppress CL_EWRITE */
 
216
    *sum += len;
 
217
    return cli_writen(fout, buf, len);
 
218
}
 
219
 
 
220
static void pdfobj_flag(struct pdf_struct *pdf, struct pdf_obj *obj, enum pdf_flag flag)
 
221
{
 
222
    const char *s= "";
 
223
    pdf->flags |= 1 << flag;
 
224
    if (!cli_debug_flag)
 
225
        return;
 
226
    switch (flag) {
 
227
        case UNTERMINATED_OBJ_DICT:
 
228
            s = "dictionary not terminated";
 
229
            break;
 
230
        case ESCAPED_COMMON_PDFNAME:
 
231
            /* like /JavaScript */
 
232
            s = "escaped common pdfname";
 
233
            break;
 
234
        case BAD_STREAM_FILTERS:
 
235
            s = "duplicate stream filters";
 
236
            break;
 
237
        case BAD_PDF_VERSION:
 
238
            s = "bad pdf version";
 
239
            break;
 
240
        case BAD_PDF_HEADERPOS:
 
241
            s = "bad pdf header position";
 
242
            break;
 
243
        case BAD_PDF_TRAILER:
 
244
            s = "bad pdf trailer";
 
245
            break;
 
246
        case BAD_PDF_TOOMANYOBJS:
 
247
            s = "too many pdf objs";
 
248
            break;
 
249
        case BAD_FLATE:
 
250
            s = "bad deflate stream";
 
251
            break;
 
252
        case BAD_FLATESTART:
 
253
            s = "bad deflate stream start";
 
254
            break;
 
255
        case BAD_STREAMSTART:
 
256
            s = "bad stream start";
 
257
            break;
 
258
        case UNKNOWN_FILTER:
 
259
            s = "unknown filter used";
 
260
            break;
 
261
        case BAD_ASCIIDECODE:
 
262
            s = "bad ASCII decode";
 
263
            break;
 
264
        case HEX_JAVASCRIPT:
 
265
            s = "hex javascript";
 
266
            break;
 
267
        case BAD_INDOBJ:
 
268
            s = "referencing nonexistent obj";
 
269
            break;
 
270
        case HAS_OPENACTION:
 
271
            s = "has /OpenAction";
 
272
            break;
 
273
        case BAD_STREAMLEN:
 
274
            s = "bad /Length, too small";
 
275
            break;
 
276
        case ENCRYPTED_PDF:
 
277
            s = "PDF is encrypted";
 
278
            break;
 
279
        case LINEARIZED_PDF:
 
280
            s = "linearized PDF";
 
281
            break;
 
282
        case MANY_FILTERS:
 
283
            s = "more than 2 filters per obj";
 
284
            break;
 
285
    }
 
286
    cli_dbgmsg("cli_pdf: %s flagged in object %u %u\n", s, obj->id>>8, obj->id&0xff);
 
287
}
 
288
 
 
289
static int filter_flatedecode(struct pdf_struct *pdf, struct pdf_obj *obj,
 
290
                              const char *buf, off_t len, int fout, off_t *sum)
 
291
{
 
292
    int skipped = 0;
 
293
    int zstat;
 
294
    z_stream stream;
 
295
    off_t nbytes;
 
296
    char output[BUFSIZ];
 
297
 
 
298
    if (len == 0)
 
299
        return CL_CLEAN;
 
300
 
 
301
    if (*buf == '\r') {
 
302
        buf++;
 
303
        len--;
 
304
        pdfobj_flag(pdf, obj, BAD_STREAMSTART);
 
305
        /* PDF spec says stream is followed by \r\n or \n, but not \r alone.
 
306
         * Sample 0015315109, it has \r followed by zlib header.
 
307
         * Flag pdf as suspicious, and attempt to extract by skipping the \r.
 
308
         */
 
309
        if (!len)
 
310
            return CL_CLEAN;
 
311
    }
 
312
 
 
313
    memset(&stream, 0, sizeof(stream));
 
314
    stream.next_in = (Bytef *)buf;
 
315
    stream.avail_in = len;
 
316
    stream.next_out = (Bytef *)output;
 
317
    stream.avail_out = sizeof(output);
 
318
 
 
319
    zstat = inflateInit(&stream);
 
320
    if(zstat != Z_OK) {
 
321
        cli_warnmsg("cli_pdf: inflateInit failed\n");
 
322
        return CL_EMEM;
 
323
    }
 
324
 
 
325
    nbytes = 0;
 
326
    while(stream.avail_in) {
 
327
        int written;
 
328
        zstat = inflate(&stream, Z_NO_FLUSH);   /* zlib */
 
329
        switch(zstat) {
 
330
            case Z_OK:
 
331
                if(stream.avail_out == 0) {
 
332
                    if ((written=filter_writen(pdf, obj, fout, output, sizeof(output), sum))!=sizeof(output)) {
 
333
                        cli_errmsg("cli_pdf: failed to write output file\n");
 
334
                        inflateEnd(&stream);
 
335
                        return CL_EWRITE;
 
336
                    }
 
337
                    nbytes += written;
 
338
                    stream.next_out = (Bytef *)output;
 
339
                    stream.avail_out = sizeof(output);
 
340
                }
 
341
                continue;
 
342
            case Z_STREAM_END:
 
343
            default:
 
344
                written = sizeof(output) - stream.avail_out;
 
345
                if (!written && !nbytes && !skipped) {
 
346
                    /* skip till EOL, and try inflating from there, sometimes
 
347
                     * PDFs contain extra whitespace */
 
348
                    const char *q = pdf_nextlinestart(buf, len);
 
349
                    if (q) {
 
350
                        skipped = 1;
 
351
                        buf = q;
 
352
                        inflateEnd(&stream);
 
353
                        len -= q - buf;
 
354
                        stream.next_in = (Bytef *)buf;
 
355
                        stream.avail_in = len;
 
356
                        stream.next_out = (Bytef *)output;
 
357
                        stream.avail_out = sizeof(output);
 
358
                        zstat = inflateInit(&stream);
 
359
                        if(zstat != Z_OK) {
 
360
                            cli_warnmsg("cli_pdf: inflateInit failed\n");
 
361
                            return CL_EMEM;
 
362
                        }
 
363
                        pdfobj_flag(pdf, obj, BAD_FLATESTART);
 
364
                        continue;
 
365
                    }
 
366
                }
 
367
 
 
368
                if (filter_writen(pdf, obj, fout, output, written, sum)!=written) {
 
369
                    cli_errmsg("cli_pdf: failed to write output file\n");
 
370
                    inflateEnd(&stream);
 
371
                    return CL_EWRITE;
 
372
                }
 
373
                nbytes += written;
 
374
                stream.next_out = (Bytef *)output;
 
375
                stream.avail_out = sizeof(output);
 
376
                if (zstat == Z_STREAM_END)
 
377
                    break;
 
378
 
 
379
                if(stream.msg)
 
380
                    cli_dbgmsg("cli_pdf: after writing %lu bytes, got error \"%s\" inflating PDF stream in %u %u obj\n",
 
381
                               (unsigned long)nbytes,
 
382
                               stream.msg, obj->id>>8, obj->id&0xff);
 
383
                else
 
384
                    cli_dbgmsg("cli_pdf: after writing %lu bytes, got error %d inflating PDF stream in %u %u obj\n",
 
385
                               (unsigned long)nbytes, zstat, obj->id>>8, obj->id&0xff);
 
386
                /* mark stream as bad only if not encrypted */
 
387
                inflateEnd(&stream);
 
388
                if (!nbytes) {
 
389
                    cli_dbgmsg("cli_pdf: dumping raw stream (probably encrypted)\n");
 
390
                    if (filter_writen(pdf, obj, fout, buf, len, sum) != len) {
 
391
                        cli_errmsg("cli_pdf: failed to write output file\n");
 
392
                        return CL_EWRITE;
 
393
                    }
 
394
                    pdfobj_flag(pdf, obj, BAD_FLATESTART);
 
395
                } else {
 
396
                    pdfobj_flag(pdf, obj, BAD_FLATE);
 
397
                }
 
398
                return CL_CLEAN;
 
399
        }
 
400
        break;
 
401
    }
 
402
 
 
403
    if(stream.avail_out != sizeof(output)) {
 
404
        if(filter_writen(pdf, obj, fout, output, sizeof(output) - stream.avail_out, sum) < 0) {
 
405
            cli_errmsg("cli_pdf: failed to write output file\n");
 
406
            inflateEnd(&stream);
 
407
            return CL_EWRITE;
 
408
        }
 
409
    }
 
410
 
 
411
    inflateEnd(&stream);
 
412
    return CL_CLEAN;
 
413
}
 
414
 
 
415
static struct pdf_obj *find_obj(struct pdf_struct *pdf,
 
416
                                struct pdf_obj *obj, uint32_t objid)
 
417
{
 
418
    unsigned j;
 
419
    unsigned i;
 
420
 
 
421
    /* search starting at previous obj (if exists) */
 
422
    if (obj != pdf->objs)
 
423
        i = obj - pdf->objs;
 
424
    else
 
425
        i = 0;
 
426
    for (j=i;j<pdf->nobjs;j++) {
 
427
        obj = &pdf->objs[j];
 
428
        if (obj->id == objid)
 
429
            return obj;
 
430
    }
 
431
    /* restart search from beginning if not found */
 
432
    for (j=0;j<i;j++) {
 
433
        obj = &pdf->objs[j];
 
434
        if (obj->id == objid)
 
435
            return obj;
 
436
    }
 
437
    return NULL;
 
438
}
 
439
 
 
440
static int find_length(struct pdf_struct *pdf,
 
441
                       struct pdf_obj *obj,
 
442
                       const char *start, off_t len)
 
443
{
 
444
    int length;
 
445
    const char *q;
 
446
    q = cli_memstr(start, len, "/Length", 7);
 
447
    if (!q)
 
448
        return 0;
 
449
    q++;
 
450
    len -= q - start;
 
451
    start = pdf_nextobject(q, len);
 
452
    if (!start)
 
453
        return 0;
 
454
    /* len -= start - q; */
 
455
    q = start;
 
456
    length = atoi(q);
 
457
    while (isdigit(*q)) q++;
 
458
    if (*q == ' ') {
 
459
        int genid;
 
460
        q++;
 
461
        genid = atoi(q);
 
462
        while(isdigit(*q)) q++;
 
463
        if (q[0] == ' ' && q[1] == 'R') {
 
464
            cli_dbgmsg("cli_pdf: length is in indirect object %u %u\n", length, genid);
 
465
            obj = find_obj(pdf, obj, (length << 8) | (genid&0xff));
 
466
            if (!obj) {
 
467
                cli_dbgmsg("cli_pdf: indirect object not found\n");
 
468
                return 0;
 
469
            }
 
470
            q = pdf_nextobject(pdf->map+obj->start, pdf->size - obj->start);
 
471
            length = atoi(q);
 
472
        }
 
473
    }
 
474
    /* limit length */
 
475
    if (start - pdf->map + length+5 > pdf->size) {
 
476
        length = pdf->size - (start - pdf->map)-5;
 
477
    }
 
478
    return length;
 
479
}
 
480
 
 
481
#define DUMP_MASK ((1 << OBJ_FILTER_FLATE) | (1 << OBJ_FILTER_DCT) | (1 << OBJ_FILTER_AH) | (1 << OBJ_FILTER_A85) | (1 << OBJ_EMBEDDED_FILE) | (1 << OBJ_JAVASCRIPT) | (1 << OBJ_OPENACTION))
 
482
 
 
483
static int obj_size(struct pdf_struct *pdf, struct pdf_obj *obj, int binary)
 
484
{
 
485
    unsigned i = obj - pdf->objs;
 
486
    i++;
 
487
    if (i < pdf->nobjs) {
 
488
        int s = pdf->objs[i].start - obj->start - 4;
 
489
        if (s > 0) {
 
490
            if (!binary) {
 
491
                const char *p = pdf->map + obj->start;
 
492
                const char *q = p + s;
 
493
                while (q > p && (isspace(*q) || isdigit(*q)))
 
494
                       q--;
 
495
                if (q > p+5 && !memcmp(q-5,"endobj",6))
 
496
                    q -= 6;
 
497
                q = findNextNonWSBack(q, p);
 
498
                q++;
 
499
                return q - p;
 
500
            }
 
501
            return s;
 
502
        }
 
503
    }
 
504
    if (binary)
 
505
        return pdf->size - obj->start;
 
506
    return pdf->offset - obj->start - 6;
 
507
}
 
508
 
 
509
static int run_pdf_hooks(struct pdf_struct *pdf, enum pdf_phase phase, int fd,
 
510
                         int dumpid)
 
511
{
 
512
    int ret;
 
513
    struct cli_bc_ctx *bc_ctx;
 
514
    cli_ctx *ctx = pdf->ctx;
 
515
    fmap_t *map;
 
516
 
 
517
    bc_ctx = cli_bytecode_context_alloc();
 
518
    if (!bc_ctx) {
 
519
        cli_errmsg("cli_pdf: can't allocate memory for bc_ctx");
 
520
        return CL_EMEM;
 
521
    }
 
522
 
 
523
    map = *ctx->fmap;
 
524
    if (fd != -1) {
 
525
        map = fmap(fd, 0, 0);
 
526
        if (!map) {
 
527
            cli_warnmsg("can't mmap pdf extracted obj\n");
 
528
            map = *ctx->fmap;
 
529
            fd = -1;
 
530
        }
 
531
    }
 
532
    cli_bytecode_context_setpdf(bc_ctx, phase, pdf->nobjs, pdf->objs,
 
533
                                &pdf->flags, pdf->size, pdf->startoff);
 
534
    cli_bytecode_context_setctx(bc_ctx, ctx);
 
535
    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PDF, map, ctx->virname);
 
536
    cli_bytecode_context_destroy(bc_ctx);
 
537
    if (fd != -1) {
 
538
        funmap(map);
 
539
    }
 
540
    return ret;
 
541
}
 
542
 
 
543
static int pdf_extract_obj(struct pdf_struct *pdf, struct pdf_obj *obj)
 
544
{
 
545
    char fullname[NAME_MAX + 1];
 
546
    int fout;
 
547
    off_t sum = 0;
 
548
    int rc = CL_SUCCESS;
 
549
    char *ascii_decoded = NULL;
 
550
    int dump = 1;
 
551
 
 
552
    /* TODO: call bytecode hook here, allow override dumpability */
 
553
    if ((!(obj->flags & (1 << OBJ_STREAM)) ||
 
554
        (obj->flags & (1 << OBJ_HASFILTERS)))
 
555
        && !(obj->flags & DUMP_MASK)) {
 
556
        /* don't dump all streams */
 
557
        dump = 0;
 
558
    }
 
559
    if ((obj->flags & (1 << OBJ_IMAGE)) &&
 
560
        !(obj->flags & (1 << OBJ_FILTER_DCT))) {
 
561
        /* don't dump / scan non-JPG images */
 
562
        dump = 0;
 
563
    }
 
564
    if (obj->flags & (1 << OBJ_FORCEDUMP)) {
 
565
        /* bytecode can force dump by setting this flag */
 
566
        dump = 1;
 
567
    }
 
568
    if (!dump)
 
569
        return CL_CLEAN;
 
570
    cli_dbgmsg("cli_pdf: dumping obj %u %u\n", obj->id>>8, obj->id);
 
571
    snprintf(fullname, sizeof(fullname), "%s"PATHSEP"pdf%02u", pdf->dir, pdf->files++);
 
572
    fout = open(fullname,O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600);
 
573
    if (fout < 0) {
 
574
        char err[128];
 
575
        cli_errmsg("cli_pdf: can't create temporary file %s: %s\n", fullname, cli_strerror(errno, err, sizeof(err)));
 
576
        free(ascii_decoded);
 
577
        return CL_ETMPFILE;
 
578
    }
 
579
 
 
580
    do {
 
581
    if (obj->flags & (1 << OBJ_STREAM)) {
 
582
        const char *start = pdf->map + obj->start;
 
583
        off_t p_stream = 0, p_endstream = 0;
 
584
        off_t length;
 
585
        find_stream_bounds(start, pdf->size - obj->start,
 
586
                           pdf->size - obj->start,
 
587
                           &p_stream, &p_endstream);
 
588
        if (p_stream && p_endstream) {
 
589
            const char *flate_in;
 
590
            long ascii_decoded_size = 0;
 
591
            size_t size = p_endstream - p_stream;
 
592
            off_t orig_length;
 
593
 
 
594
            length = find_length(pdf, obj, start, p_stream);
 
595
            if (length < 0)
 
596
                length = 0;
 
597
            orig_length = length;
 
598
            if (length > pdf->size || obj->start + p_stream + length > pdf->size) {
 
599
                cli_dbgmsg("cli_pdf: length out of file: %ld + %ld > %ld\n",
 
600
                           p_stream, length, pdf->size);
 
601
                length = pdf->size - (obj->start + p_stream);
 
602
            }
 
603
            if (!(obj->flags & (1 << OBJ_FILTER_FLATE)) && length <= 0) {
 
604
                const char *q = start + p_endstream;
 
605
                length = size;
 
606
                q--;
 
607
                if (*q == '\n') {
 
608
                    q--;
 
609
                    length--;
 
610
                    if (*q == '\r')
 
611
                        length--;
 
612
                } else if (*q == '\r') {
 
613
                    length--;
 
614
                }
 
615
                if (length < 0)
 
616
                    length = 0;
 
617
                cli_dbgmsg("cli_pdf: calculated length %ld\n", length);
 
618
            } else {
 
619
                if (size > length+2) {
 
620
                    cli_dbgmsg("cli_pdf: calculated length %ld < %ld\n",
 
621
                               length, size);
 
622
                    length = size;
 
623
                }
 
624
            }
 
625
            if (orig_length && size > orig_length + 20) {
 
626
                cli_dbgmsg("cli_pdf: orig length: %ld, length: %ld, size: %ld\n",
 
627
                           orig_length, length, size);
 
628
                pdfobj_flag(pdf, obj, BAD_STREAMLEN);
 
629
            }
 
630
            if (!length)
 
631
                length = size;
 
632
 
 
633
            if (obj->flags & (1 << OBJ_FILTER_AH)) {
 
634
                ascii_decoded = cli_malloc(length/2 + 1);
 
635
                if (!ascii_decoded) {
 
636
                    cli_errmsg("Cannot allocate memory for asciidecode\n");
 
637
                    rc = CL_EMEM;
 
638
                    break;
 
639
                }
 
640
                ascii_decoded_size = asciihexdecode(start + p_stream,
 
641
                                                    length,
 
642
                                                    ascii_decoded);
 
643
            } else if (obj->flags & (1 << OBJ_FILTER_A85)) {
 
644
                ascii_decoded = cli_malloc(length*5);
 
645
                if (!ascii_decoded) {
 
646
                    cli_errmsg("Cannot allocate memory for asciidecode\n");
 
647
                    rc = CL_EMEM;
 
648
                    break;
 
649
                }
 
650
                ascii_decoded_size = ascii85decode(start+p_stream,
 
651
                                                   length,
 
652
                                                   (unsigned char*)ascii_decoded);
 
653
            }
 
654
            if (ascii_decoded_size < 0) {
 
655
                /* don't flag for images or truncated objs*/
 
656
                if (!(obj->flags &
 
657
                      ((1 << OBJ_IMAGE) | (1 << OBJ_TRUNCATED))))
 
658
                    pdfobj_flag(pdf, obj, BAD_ASCIIDECODE);
 
659
                cli_dbgmsg("cli_pdf: failed to asciidecode in %u %u obj\n", obj->id>>8,obj->id&0xff);
 
660
                free(ascii_decoded);
 
661
                ascii_decoded = NULL;
 
662
                /* attempt to directly flatedecode it */
 
663
            }
 
664
            /* either direct or ascii-decoded input */
 
665
            if (!ascii_decoded)
 
666
                ascii_decoded_size = length;
 
667
            flate_in = ascii_decoded ? ascii_decoded : start+p_stream;
 
668
 
 
669
            if (obj->flags & (1 << OBJ_FILTER_FLATE)) {
 
670
                cli_dbgmsg("cli_pdf: deflate len %ld (orig %ld)\n", ascii_decoded_size, (long)orig_length);
 
671
                rc = filter_flatedecode(pdf, obj, flate_in, ascii_decoded_size, fout, &sum);
 
672
            } else {
 
673
                if (filter_writen(pdf, obj, fout, flate_in, ascii_decoded_size, &sum) != ascii_decoded_size)
 
674
                    rc = CL_EWRITE;
 
675
            }
 
676
        }
 
677
    } else if (obj->flags & (1 << OBJ_JAVASCRIPT)) {
 
678
        const char *q2;
 
679
        const char *q = pdf->map+obj->start;
 
680
        /* TODO: get obj-endobj size */
 
681
        off_t bytesleft = obj_size(pdf, obj, 0);
 
682
        if (bytesleft < 0)
 
683
            break;
 
684
 
 
685
        q2 = cli_memstr(q, bytesleft, "/JavaScript", 11);
 
686
        if (!q2)
 
687
            break;
 
688
        bytesleft -= q2 - q;
 
689
        do {
 
690
        q2++;
 
691
        bytesleft--;
 
692
        q = pdf_nextobject(q2, bytesleft);
 
693
        if (!q)
 
694
            break;
 
695
        bytesleft -= q - q2;
 
696
        q2 = q;
 
697
        } while (*q == '/');
 
698
        if (!q)
 
699
            break;
 
700
        if (*q == '(') {
 
701
            if (filter_writen(pdf, obj, fout, q+1, bytesleft-1, &sum) != (bytesleft-1)) {
 
702
                rc = CL_EWRITE;
 
703
                break;
 
704
            }
 
705
        } else if (*q == '<') {
 
706
            char *decoded;
 
707
            q2 = memchr(q+1, '>', bytesleft);
 
708
            if (!q2) q2 = q + bytesleft;
 
709
            decoded = cli_malloc(q2 - q);
 
710
            if (!decoded) {
 
711
                rc = CL_EMEM;
 
712
                break;
 
713
            }
 
714
            cli_hex2str_to(q2, decoded, q2-q-1);
 
715
            decoded[q2-q-1] = '\0';
 
716
            cli_dbgmsg("cli_pdf: found hexadecimal encoded javascript in %u %u obj\n",
 
717
                       obj->id>>8, obj->id&0xff);
 
718
            pdfobj_flag(pdf, obj, HEX_JAVASCRIPT);
 
719
            filter_writen(pdf, obj, fout, decoded, q2-q-1, &sum);
 
720
            free(decoded);
 
721
        }
 
722
    } else {
 
723
        off_t bytesleft = obj_size(pdf, obj, 0);
 
724
        if (filter_writen(pdf, obj, fout , pdf->map + obj->start, bytesleft,&sum) != bytesleft)
 
725
            rc = CL_EWRITE;
 
726
    }
 
727
    } while (0);
 
728
    cli_dbgmsg("cli_pdf: extracted %ld bytes %u %u obj to %s\n", sum, obj->id>>8, obj->id&0xff, fullname);
 
729
    if (sum) {
 
730
        int rc2;
 
731
        cli_updatelimits(pdf->ctx, sum);
 
732
        /* TODO: invoke bytecode on this pdf obj with metainformation associated
 
733
         * */
 
734
        lseek(fout, 0, SEEK_SET);
 
735
        rc2 = cli_magic_scandesc(fout, pdf->ctx);
 
736
        if (rc2 == CL_VIRUS || rc == CL_SUCCESS)
 
737
            rc = rc2;
 
738
        if (rc == CL_CLEAN) {
 
739
            rc2 = run_pdf_hooks(pdf, PDF_PHASE_POSTDUMP, fout, obj - pdf->objs);
 
740
            if (rc2 == CL_VIRUS)
 
741
                rc = rc2;
 
742
        }
 
743
    }
 
744
    close(fout);
 
745
    free(ascii_decoded);
 
746
    if (!pdf->ctx->engine->keeptmp)
 
747
        if (cli_unlink(fullname) && rc != CL_VIRUS)
 
748
            rc = CL_EUNLINK;
 
749
    return rc;
 
750
}
 
751
 
 
752
enum objstate {
 
753
    STATE_NONE,
 
754
    STATE_S,
 
755
    STATE_FILTER,
 
756
    STATE_JAVASCRIPT,
 
757
    STATE_OPENACTION,
 
758
    STATE_LINEARIZED,
 
759
    STATE_ANY /* for actions table below */
 
760
};
 
761
 
 
762
struct pdfname_action {
 
763
    const char *pdfname;
 
764
    enum pdf_objflags set_objflag;/* OBJ_DICT is noop */
 
765
    enum objstate from_state;/* STATE_NONE is noop */
 
766
    enum objstate to_state;
 
767
};
 
768
 
 
769
static struct pdfname_action pdfname_actions[] = {
 
770
    {"ASCIIHexDecode", OBJ_FILTER_AH, STATE_FILTER, STATE_FILTER},
 
771
    {"ASCII85Decode", OBJ_FILTER_A85, STATE_FILTER, STATE_FILTER},
 
772
    {"A85", OBJ_FILTER_A85, STATE_FILTER, STATE_FILTER},
 
773
    {"AHx", OBJ_FILTER_AH, STATE_FILTER, STATE_FILTER},
 
774
    {"EmbeddedFile", OBJ_EMBEDDED_FILE, STATE_NONE, STATE_NONE},
 
775
    {"FlateDecode", OBJ_FILTER_FLATE, STATE_FILTER, STATE_FILTER},
 
776
    {"Fl", OBJ_FILTER_FLATE, STATE_FILTER, STATE_FILTER},
 
777
    {"Image", OBJ_IMAGE, STATE_NONE, STATE_NONE},
 
778
    {"LZWDecode", OBJ_FILTER_LZW, STATE_FILTER, STATE_FILTER},
 
779
    {"LZW", OBJ_FILTER_LZW, STATE_FILTER, STATE_FILTER},
 
780
    {"RunLengthDecode", OBJ_FILTER_RL, STATE_FILTER, STATE_FILTER},
 
781
    {"RL", OBJ_FILTER_RL, STATE_FILTER, STATE_FILTER},
 
782
    {"CCITTFaxDecode", OBJ_FILTER_FAX, STATE_FILTER, STATE_FILTER},
 
783
    {"CCF", OBJ_FILTER_FAX, STATE_FILTER, STATE_FILTER},
 
784
    {"JBIG2Decode", OBJ_FILTER_DCT, STATE_FILTER, STATE_FILTER},
 
785
    {"DCTDecode", OBJ_FILTER_DCT, STATE_FILTER, STATE_FILTER},
 
786
    {"DCT", OBJ_FILTER_DCT, STATE_FILTER, STATE_FILTER},
 
787
    {"JPXDecode", OBJ_FILTER_JPX, STATE_FILTER, STATE_FILTER},
 
788
    {"Crypt",  OBJ_FILTER_CRYPT, STATE_FILTER, STATE_NONE},
 
789
    {"Standard", OBJ_FILTER_CRYPT, STATE_FILTER, STATE_FILTER},
 
790
    {"Sig",    OBJ_SIGNED, STATE_ANY, STATE_NONE},
 
791
    {"V",     OBJ_SIGNED, STATE_ANY, STATE_NONE},
 
792
    {"R",     OBJ_SIGNED, STATE_ANY, STATE_NONE},
 
793
    {"Linearized", OBJ_DICT, STATE_NONE, STATE_LINEARIZED},
 
794
    {"Filter", OBJ_HASFILTERS, STATE_ANY, STATE_FILTER},
 
795
    {"JavaScript", OBJ_JAVASCRIPT, STATE_S, STATE_JAVASCRIPT},
 
796
    {"Length", OBJ_DICT, STATE_FILTER, STATE_NONE},
 
797
    {"S", OBJ_DICT, STATE_NONE, STATE_S},
 
798
    {"Type", OBJ_DICT, STATE_NONE, STATE_NONE},
 
799
    {"OpenAction", OBJ_OPENACTION, STATE_ANY, STATE_OPENACTION}
 
800
};
 
801
 
 
802
#define KNOWN_FILTERS ((1 << OBJ_FILTER_AH) | (1 << OBJ_FILTER_RL) | (1 << OBJ_FILTER_A85) | (1 << OBJ_FILTER_FLATE) | (1 << OBJ_FILTER_LZW) | (1 << OBJ_FILTER_FAX) | (1 << OBJ_FILTER_DCT) | (1 << OBJ_FILTER_JPX) | (1 << OBJ_FILTER_CRYPT))
 
803
 
 
804
static void handle_pdfname(struct pdf_struct *pdf, struct pdf_obj *obj,
 
805
                           const char *pdfname, int escapes,
 
806
                           enum objstate *state)
 
807
{
 
808
    struct pdfname_action *act = NULL;
 
809
    unsigned j;
 
810
    for (j=0;j<sizeof(pdfname_actions)/sizeof(pdfname_actions[0]);j++) {
 
811
        if (!strcmp(pdfname, pdfname_actions[j].pdfname)) {
 
812
            act = &pdfname_actions[j];
 
813
            break;
 
814
        }
 
815
    }
 
816
    if (!act) {
 
817
        if (*state == STATE_FILTER &&
 
818
            !(obj->flags & (1 << OBJ_SIGNED)) &&
 
819
            /* these are digital signature objects, filter doesn't matter,
 
820
             * we don't need them anyway */
 
821
            !(obj->flags & KNOWN_FILTERS)) {
 
822
            cli_dbgmsg("cli_pdf: unknown filter %s\n", pdfname);
 
823
            obj->flags |= 1 << OBJ_FILTER_UNKNOWN;
 
824
        }
 
825
        return;
 
826
    }
 
827
    if (escapes) {
 
828
        /* if a commonly used PDF name is escaped that is certainly
 
829
           suspicious. */
 
830
        cli_dbgmsg("cli_pdf: pdfname %s is escaped\n", pdfname);
 
831
        pdfobj_flag(pdf, obj, ESCAPED_COMMON_PDFNAME);
 
832
    }
 
833
    if (act->from_state == *state ||
 
834
        act->from_state == STATE_ANY) {
 
835
        *state = act->to_state;
 
836
 
 
837
        if (*state == STATE_FILTER &&
 
838
            act->set_objflag !=OBJ_DICT &&
 
839
            (obj->flags & (1 << act->set_objflag))) {
 
840
            cli_dbgmsg("cli_pdf: duplicate stream filter %s\n", pdfname);
 
841
            pdfobj_flag(pdf, obj, BAD_STREAM_FILTERS);
 
842
        }
 
843
        obj->flags |= 1 << act->set_objflag;
 
844
    } else {
 
845
        /* auto-reset states */
 
846
        switch (*state) {
 
847
            case STATE_S:
 
848
                *state = STATE_NONE;
 
849
                break;
 
850
            default:
 
851
                break;
 
852
        }
 
853
    }
 
854
}
 
855
 
 
856
static void pdf_parseobj(struct pdf_struct *pdf, struct pdf_obj *obj)
 
857
{
 
858
    /* enough to hold common pdf names, we don't need all the names */
 
859
    char pdfname[64];
 
860
    const char *q2, *q3;
 
861
    const char *q = obj->start + pdf->map;
 
862
    const char *dict, *start;
 
863
    off_t dict_length;
 
864
    off_t bytesleft = obj_size(pdf, obj, 1);
 
865
    unsigned i, filters=0;
 
866
    enum objstate objstate = STATE_NONE;
 
867
 
 
868
    if (bytesleft < 0)
 
869
        return;
 
870
    start = q;
 
871
    /* find start of dictionary */
 
872
    do {
 
873
        q2 = pdf_nextobject(q, bytesleft);
 
874
        bytesleft -= q2 -q;
 
875
        if (!q2 || bytesleft < 0) {
 
876
            return;
 
877
        }
 
878
        q3 = memchr(q-1, '<', q2-q+1);
 
879
        q2++;
 
880
        bytesleft--;
 
881
        q = q2;
 
882
    } while (!q3 || q3[1] != '<');
 
883
    dict = q3+2;
 
884
    q = dict;
 
885
    bytesleft = obj_size(pdf, obj, 1) - (q - start);
 
886
    /* find end of dictionary */
 
887
    do {
 
888
        q2 = pdf_nextobject(q, bytesleft);
 
889
        bytesleft -= q2 -q;
 
890
        if (!q2 || bytesleft < 0) {
 
891
            return;
 
892
        }
 
893
        q3 = memchr(q-1, '>', q2-q+1);
 
894
        q2++;
 
895
        bytesleft--;
 
896
        q = q2;
 
897
    } while (!q3 || q3[1] != '>');
 
898
    obj->flags |= 1 << OBJ_DICT;
 
899
    dict_length = q3 - dict;
 
900
 
 
901
    /*  process pdf names */
 
902
    for (q = dict;dict_length;) {
 
903
        int escapes = 0;
 
904
        q2 = memchr(q, '/', dict_length);
 
905
        if (!q2)
 
906
            break;
 
907
        dict_length -= q2 - q;
 
908
        q = q2;
 
909
        /* normalize PDF names */
 
910
        for (i = 0;dict_length && (i < sizeof(pdfname)-1); i++) {
 
911
            q++;
 
912
            dict_length--;
 
913
            if (*q == '#') {
 
914
                cli_hex2str_to(q+1, pdfname+i, 2);
 
915
                q += 2;
 
916
                dict_length -= 2;
 
917
                escapes = 1;
 
918
                continue;
 
919
            }
 
920
            if (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n' ||
 
921
                *q == '/' || *q == '>' || *q == ']' || *q == '[' || *q == '<')
 
922
                break;
 
923
            pdfname[i] = *q;
 
924
        }
 
925
        pdfname[i] = '\0';
 
926
 
 
927
        handle_pdfname(pdf, obj, pdfname, escapes, &objstate);
 
928
        if (objstate == STATE_LINEARIZED) {
 
929
            pdfobj_flag(pdf, obj, LINEARIZED_PDF);
 
930
            objstate = STATE_NONE;
 
931
        }
 
932
        if (objstate == STATE_JAVASCRIPT ||
 
933
            objstate == STATE_OPENACTION) {
 
934
            if (objstate == STATE_OPENACTION)
 
935
                pdfobj_flag(pdf, obj, HAS_OPENACTION);
 
936
            q2 = pdf_nextobject(q, dict_length);
 
937
            if (q2 && isdigit(*q2)) {
 
938
                uint32_t objid = atoi(q2) << 8;
 
939
                while (isdigit(*q2)) q2++;
 
940
                q2 = pdf_nextobject(q2, dict_length);
 
941
                if (q2 && isdigit(*q2)) {
 
942
                    objid |= atoi(q2) & 0xff;
 
943
                    q2 = pdf_nextobject(q2, dict_length);
 
944
                    if (*q2 == 'R') {
 
945
                        struct pdf_obj *obj2;
 
946
                        cli_dbgmsg("cli_pdf: found %s stored in indirect object %u %u\n",
 
947
                                   pdfname,
 
948
                                   objid >> 8, objid&0xff);
 
949
                        obj2 = find_obj(pdf, obj, objid);
 
950
                        if (obj2) {
 
951
                            enum pdf_objflags flag = objstate == STATE_JAVASCRIPT ?
 
952
                                OBJ_JAVASCRIPT : OBJ_OPENACTION;
 
953
                            obj2->flags |= 1 << flag;
 
954
                            obj->flags &= ~(1 << flag);
 
955
                        } else {
 
956
                            pdfobj_flag(pdf, obj, BAD_INDOBJ);
 
957
                        }
 
958
                    }
 
959
                }
 
960
            }
 
961
            objstate = STATE_NONE;
 
962
        }
 
963
    }
 
964
    for (i=0;i<sizeof(pdfname_actions)/sizeof(pdfname_actions[0]);i++) {
 
965
        const struct pdfname_action *act = &pdfname_actions[i];
 
966
        if ((obj->flags & (1 << act->set_objflag)) &&
 
967
            act->from_state == STATE_FILTER &&
 
968
            act->to_state == STATE_FILTER &&
 
969
            act->set_objflag != OBJ_FILTER_CRYPT) {
 
970
            filters++;
 
971
        }
 
972
    }
 
973
    if (filters > 2) { /* more than 2 non-crypt filters */
 
974
        pdfobj_flag(pdf, obj, MANY_FILTERS);
 
975
    }
 
976
    if (obj->flags & ((1 << OBJ_SIGNED) | KNOWN_FILTERS))
 
977
        obj->flags &= ~(1 << OBJ_FILTER_UNKNOWN);
 
978
    if (obj->flags & (1 << OBJ_FILTER_UNKNOWN))
 
979
        pdfobj_flag(pdf, obj, UNKNOWN_FILTER);
 
980
    cli_dbgmsg("cli_pdf: %u %u obj flags: %02x\n", obj->id>>8, obj->id&0xff, obj->flags);
 
981
}
 
982
 
 
983
int cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
 
984
{
 
985
    struct pdf_struct pdf;
 
986
    fmap_t *map = *ctx->fmap;
 
987
    size_t size = map->len - offset;
 
988
    off_t versize = size > 1032 ? 1032 : size;
 
989
    off_t map_off, bytesleft;
 
990
    long xref;
 
991
    const char *pdfver, *start, *eofmap, *q, *eof;
 
992
    int rc;
 
993
    unsigned i;
 
994
 
 
995
    cli_dbgmsg("in cli_pdf(%s)\n", dir);
 
996
    memset(&pdf, 0, sizeof(pdf));
 
997
    pdf.ctx = ctx;
 
998
    pdf.dir = dir;
 
999
 
 
1000
    pdfver = start = fmap_need_off_once(map, offset, versize);
 
1001
 
 
1002
    /* Check PDF version */
 
1003
    if (!pdfver) {
 
1004
        cli_errmsg("cli_pdf: mmap() failed (1)\n");
 
1005
        return CL_EMAP;
 
1006
    }
 
1007
    /* offset is 0 when coming from filetype2 */
 
1008
    pdfver = cli_memstr(pdfver, versize, "%PDF-", 5);
 
1009
    if (!pdfver) {
 
1010
        cli_dbgmsg("cli_pdf: no PDF- header found\n");
 
1011
        return CL_SUCCESS;
 
1012
    }
 
1013
    /* Check for PDF-1.[0-9]. Although 1.7 is highest now, allow for future
 
1014
     * versions */
 
1015
    if (pdfver[5] != '1' || pdfver[6] != '.' ||
 
1016
        pdfver[7] < '1' || pdfver[7] > '9') {
 
1017
        pdf.flags |= 1 << BAD_PDF_VERSION;
 
1018
        cli_dbgmsg("cli_pdf: bad pdf version: %.8s\n", pdfver);
 
1019
    }
 
1020
    if (pdfver != start || offset) {
 
1021
        pdf.flags |= 1 << BAD_PDF_HEADERPOS;
 
1022
        cli_dbgmsg("cli_pdf: PDF header is not at position 0: %ld\n",pdfver-start+offset);
 
1023
    }
 
1024
    offset += pdfver - start;
 
1025
 
 
1026
    /* find trailer and xref, don't fail if not found */
 
1027
    map_off = map->len - 2048;
 
1028
    if (map_off < 0)
 
1029
        map_off = 0;
 
1030
    bytesleft = map->len - map_off;
 
1031
    eofmap = fmap_need_off_once(map, map_off, bytesleft);
 
1032
    if (!eofmap) {
 
1033
        cli_errmsg("cli_pdf: mmap() failed (2)\n");
 
1034
        return CL_EMAP;
 
1035
    }
 
1036
    eof = eofmap + bytesleft;
 
1037
    for (q=&eofmap[bytesleft-5]; q > eofmap; q--) {
 
1038
        if (memcmp(q, "%%EOF", 5) == 0)
 
1039
            break;
 
1040
    }
 
1041
    if (q <= eofmap) {
 
1042
        pdf.flags |= 1 << BAD_PDF_TRAILER;
 
1043
        cli_dbgmsg("cli_pdf: %%%%EOF not found\n");
 
1044
    } else {
 
1045
        const char *t;
 
1046
        size = q - eofmap + map_off;
 
1047
        for (;q > eofmap;q--) {
 
1048
            if (memcmp(q, "startxref", 9) == 0)
 
1049
                break;
 
1050
        }
 
1051
        if (q <= eofmap) {
 
1052
            pdf.flags |= 1 << BAD_PDF_TRAILER;
 
1053
            cli_dbgmsg("cli_pdf: startxref not found\n");
 
1054
        } else {
 
1055
            for (t=q;t > eofmap; t--) {
 
1056
                if (memcmp(t,"trailer",7) == 0)
 
1057
                    break;
 
1058
            }
 
1059
            if (t > eofmap) {
 
1060
                if (cli_memstr(t, q-t, "/Encrypt", 8)) {
 
1061
                    pdf.flags |= 1 << ENCRYPTED_PDF;
 
1062
                    cli_dbgmsg("cli_pdf: encrypted pdf found, stream will probably fail to decompress!\n");
 
1063
                }
 
1064
            }
 
1065
            q += 9;
 
1066
            while (q < eof && (*q == ' ' || *q == '\n' || *q == '\r')) { q++; }
 
1067
            xref = atol(q);
 
1068
            bytesleft = map->len - offset - xref;
 
1069
            if (bytesleft > 4096)
 
1070
                bytesleft = 4096;
 
1071
            q = fmap_need_off_once(map, offset + xref, bytesleft);
 
1072
            if (!q || xrefCheck(q, q+bytesleft) == -1) {
 
1073
                cli_dbgmsg("cli_pdf: did not find valid xref\n");
 
1074
                pdf.flags |= 1 << BAD_PDF_TRAILER;
 
1075
            }
 
1076
        }
 
1077
    }
 
1078
    size -= offset;
 
1079
 
 
1080
    pdf.size = size;
 
1081
    pdf.map = fmap_need_off(map, offset, size);
 
1082
    pdf.startoff = offset;
 
1083
    if (!pdf.map) {
 
1084
        cli_errmsg("cli_pdf: mmap() failed (3)\n");
 
1085
        return CL_EMAP;
 
1086
    }
 
1087
    rc = run_pdf_hooks(&pdf, PDF_PHASE_PRE, -1, -1);
 
1088
    if (rc) {
 
1089
        cli_dbgmsg("cli_pdf: returning %d\n", rc);
 
1090
        return rc;
 
1091
    }
 
1092
    /* parse PDF and find obj offsets */
 
1093
    while ((rc = pdf_findobj(&pdf)) > 0) {
 
1094
        struct pdf_obj *obj = &pdf.objs[pdf.nobjs-1];
 
1095
        cli_dbgmsg("found %d %d obj @%ld\n", obj->id >> 8, obj->id&0xff, obj->start + offset);
 
1096
    }
 
1097
    if (pdf.nobjs)
 
1098
        pdf.nobjs--;
 
1099
    if (rc == -1)
 
1100
        pdf.flags |= 1 << BAD_PDF_TOOMANYOBJS;
 
1101
 
 
1102
    /* must parse after finding all objs, so we can flag indirect objects */
 
1103
    for (i=0;i<pdf.nobjs;i++) {
 
1104
        struct pdf_obj *obj = &pdf.objs[i];
 
1105
        pdf_parseobj(&pdf, obj);
 
1106
    }
 
1107
 
 
1108
    rc = run_pdf_hooks(&pdf, PDF_PHASE_PARSED, -1, -1);
 
1109
    /* extract PDF objs */
 
1110
    for (i=0;!rc && i<pdf.nobjs;i++) {
 
1111
        struct pdf_obj *obj = &pdf.objs[i];
 
1112
        rc = pdf_extract_obj(&pdf, obj);
 
1113
    }
 
1114
 
 
1115
    if (pdf.flags & (1 << ENCRYPTED_PDF))
 
1116
        pdf.flags &= ~ ((1 << BAD_FLATESTART) | (1 << BAD_STREAMSTART) |
 
1117
            (1 << BAD_ASCIIDECODE));
 
1118
 
 
1119
   if (pdf.flags && !rc) {
 
1120
        cli_dbgmsg("cli_pdf: flags 0x%02x\n", pdf.flags);
 
1121
        rc = run_pdf_hooks(&pdf, PDF_PHASE_END, -1, -1);
 
1122
        if (!rc && (ctx->options & CL_SCAN_ALGORITHMIC)) {
 
1123
            if (pdf.flags & (1 << ESCAPED_COMMON_PDFNAME)) {
 
1124
                /* for example /Fl#61te#44#65#63#6f#64#65 instead of /FlateDecode */
 
1125
                *ctx->virname = "Heuristics.PDF.ObfuscatedNameObject";
 
1126
                rc = cli_found_possibly_unwanted(ctx);
 
1127
            }
 
1128
        }
 
1129
#if 0
 
1130
        /* TODO: find both trailers, and /Encrypt settings */
 
1131
        if (pdf.flags & (1 << LINEARIZED_PDF))
 
1132
            pdf.flags &= ~ (1 << BAD_ASCIIDECODE);
 
1133
        if (pdf.flags & (1 << MANY_FILTERS))
 
1134
            pdf.flags &= ~ (1 << BAD_ASCIIDECODE);
 
1135
        if (!rc && (pdf.flags &
 
1136
            ((1 << BAD_PDF_TOOMANYOBJS) | (1 << BAD_STREAM_FILTERS) |
 
1137
             (1<<BAD_FLATE) | (1<<BAD_ASCIIDECODE)|
 
1138
             (1<<UNTERMINATED_OBJ_DICT) | (1<<UNKNOWN_FILTER)))) {
 
1139
            rc = CL_EUNPACK;
 
1140
        }
 
1141
#endif
 
1142
    }
 
1143
    cli_dbgmsg("cli_pdf: returning %d\n", rc);
 
1144
    free(pdf.objs);
 
1145
    return rc;
 
1146
}
 
1147
 
 
1148
#else
 
1149
static  int     try_flatedecode(unsigned char *buf, off_t real_len, off_t calculated_len, int fout, cli_ctx *ctx);
 
1150
static  int     flatedecode(unsigned char *buf, off_t len, int fout, cli_ctx *ctx);
63
1151
int
64
1152
cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
65
1153
{
451
1539
                        }
452
1540
                        if(ret) {
453
1541
                                unsigned char *t;
 
1542
                                unsigned size;
454
1543
 
455
1544
                                real_streamlen = ret;
456
1545
                                /* free unused trailing bytes */
457
 
                                t = (unsigned char *)cli_realloc(tmpbuf,calculated_streamlen);
 
1546
                                size = real_streamlen > calculated_streamlen ? real_streamlen : calculated_streamlen;
 
1547
                                t = (unsigned char *)cli_realloc(tmpbuf,size);
458
1548
                                if(t == NULL) {
459
1549
                                        free(tmpbuf);
460
1550
                                        close(fout);
650
1740
        inflateEnd(&stream);
651
1741
        return CL_CLEAN;
652
1742
}
 
1743
#endif
653
1744
 
 
1745
static int asciihexdecode(const char *buf, off_t len, char *output)
 
1746
{
 
1747
    unsigned i,j;
 
1748
    for (i=0,j=0;i+1<len;i++) {
 
1749
        if (buf[i] == ' ')
 
1750
            continue;
 
1751
        if (buf[i] == '>')
 
1752
            break;
 
1753
        if (cli_hex2str_to(buf+i, output+j++, 2) == -1) {
 
1754
            if (len - i < 4)
 
1755
                continue;
 
1756
            return -1;
 
1757
        }
 
1758
        i++;
 
1759
    }
 
1760
    return j;
 
1761
}
654
1762
/*
655
1763
 * ascii85 inflation, returns number of bytes in output, -1 for error
656
1764
 *
715
1823
                                ret += quintet;
716
1824
                                for(i = 0; i < quintet - 1; i++)
717
1825
                                        *output++ = (unsigned char)((sum >> (24 - 8 * i)) & 0xFF);
718
 
                                quintet = 0;
719
1826
                        }
720
 
                        len = 0;
721
1827
                        break;
722
1828
                } else if(!isspace(byte)) {
723
1829
                        cli_dbgmsg("ascii85Decode: invalid character 0x%x, len %lu\n",
782
1888
                                break;
783
1889
                        case '/':       /* Start of a name object */
784
1890
                                return ptr;
 
1891
                        case '(': /* start of JS */
 
1892
                                return ptr;
785
1893
                        default:
786
1894
                                if(!inobject)
787
1895
                                        /* TODO: parse and return object type */