Coverage Report

Created: 2026-08-25 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzip/lib/zip_open.c
Line
Count
Source
1
/*
2
  zip_open.c -- open zip archive by name
3
  Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner
4
5
  This file is part of libzip, a library to manipulate ZIP archives.
6
  The authors can be contacted at <info@libzip.org>
7
8
  Redistribution and use in source and binary forms, with or without
9
  modification, are permitted provided that the following conditions
10
  are met:
11
  1. Redistributions of source code must retain the above copyright
12
     notice, this list of conditions and the following disclaimer.
13
  2. Redistributions in binary form must reproduce the above copyright
14
     notice, this list of conditions and the following disclaimer in
15
     the documentation and/or other materials provided with the
16
     distribution.
17
  3. The names of the authors may not be used to endorse or promote
18
     products derived from this software without specific prior
19
     written permission.
20
21
  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27
  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29
  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31
  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
*/
33
34
#include <limits.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
39
#include "zipint.h"
40
41
typedef enum {
42
    EXISTS_ERROR = -1,
43
    EXISTS_NOT = 0,
44
    EXISTS_OK
45
} exists_t;
46
47
typedef enum {
48
    CDIR_OK,
49
    CDIR_INVALID,
50
    CDIR_NOT_FOUND
51
} cdir_status_t;
52
53
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error);
54
static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char *magic);
55
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error);
56
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error);
57
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir);
58
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len);
59
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error);
60
static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *);
61
static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error);
62
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error);
63
static cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error);
64
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last);
65
66
67
9.04k
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
9.04k
    zip_t *za;
69
9.04k
    zip_source_t *src;
70
9.04k
    struct zip_error error;
71
72
9.04k
    zip_error_init(&error);
73
9.04k
    if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) {
74
0
        _zip_set_open_error(zep, &error, 0);
75
0
        zip_error_fini(&error);
76
0
        return NULL;
77
0
    }
78
79
9.04k
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
3.84k
        zip_source_free(src);
81
3.84k
        _zip_set_open_error(zep, &error, 0);
82
3.84k
        zip_error_fini(&error);
83
3.84k
        return NULL;
84
3.84k
    }
85
86
5.20k
    zip_error_fini(&error);
87
5.20k
    return za;
88
9.04k
}
89
90
91
9.04k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
9.04k
    unsigned int flags;
93
9.04k
    zip_int64_t supported;
94
9.04k
    exists_t exists;
95
96
9.04k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
9.04k
    flags = (unsigned int)_flags;
101
102
9.04k
    supported = zip_source_supports(src);
103
9.04k
    if ((supported & ZIP_SOURCE_SUPPORTS_SEEKABLE) != ZIP_SOURCE_SUPPORTS_SEEKABLE) {
104
0
        zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
105
0
        return NULL;
106
0
    }
107
9.04k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
9.04k
    if ((flags & (ZIP_RDONLY | ZIP_TRUNCATE)) == (ZIP_RDONLY | ZIP_TRUNCATE)) {
112
0
        zip_error_set(error, ZIP_ER_RDONLY, 0);
113
0
        return NULL;
114
0
    }
115
116
9.04k
    exists = _zip_file_exists(src, error);
117
9.04k
    switch (exists) {
118
0
    case EXISTS_ERROR:
119
0
        return NULL;
120
121
0
    case EXISTS_NOT:
122
0
        if ((flags & ZIP_CREATE) == 0) {
123
0
            zip_error_set(error, ZIP_ER_NOENT, 0);
124
0
            return NULL;
125
0
        }
126
0
        return _zip_allocate_new(src, flags, error);
127
128
9.04k
    default: {
129
9.04k
        zip_t *za;
130
9.04k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
9.04k
        if (zip_source_open(src) < 0) {
135
0
            zip_error_set_from_source(error, src);
136
0
            return NULL;
137
0
        }
138
139
9.04k
        if (flags & ZIP_TRUNCATE) {
140
0
            za = _zip_allocate_new(src, flags, error);
141
0
        }
142
9.04k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
9.04k
            za = _zip_open(src, flags, error);
145
9.04k
        }
146
147
9.04k
        if (za == NULL) {
148
3.84k
            zip_source_close(src);
149
3.84k
            return NULL;
150
3.84k
        }
151
5.20k
        return za;
152
9.04k
    }
153
9.04k
    }
154
9.04k
}
155
156
157
1.47k
static bool _is_truncated_zip(zip_source_t *src) {
158
1.47k
    unsigned char data[4];
159
    /* check if the source is a truncated zip archive: true if yes, no
160
       if not or can't be determined */
161
1.47k
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
1.47k
    if (zip_source_read(src, data, 4) != 4) {
166
90
        return false;
167
90
    }
168
169
1.38k
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
128
        return true;
172
128
    }
173
1.25k
    return false;
174
1.38k
}
175
176
177
9.04k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
9.04k
    zip_t *za;
179
9.04k
    zip_cdir_t *cdir;
180
9.04k
    struct zip_stat st;
181
9.04k
    zip_uint64_t len, idx;
182
183
9.04k
    zip_stat_init(&st);
184
9.04k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
9.04k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
9.04k
    len = st.size;
193
194
195
9.04k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
9.04k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
9.04k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
3.84k
        _zip_error_copy(error, &za->error);
206
3.84k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
1.47k
            if (_is_truncated_zip(src)) {
209
128
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
128
            }
211
1.47k
        }
212
        /* keep src so discard does not get rid of it */
213
3.84k
        zip_source_keep(src);
214
3.84k
        zip_discard(za);
215
3.84k
        return NULL;
216
3.84k
    }
217
218
5.20k
    za->entry = cdir->entry;
219
5.20k
    za->nentry = cdir->nentry;
220
5.20k
    za->nentry_alloc = cdir->nentry_alloc;
221
222
5.20k
    zip_check_torrentzip(za, cdir);
223
224
5.20k
    if (ZIP_IS_TORRENTZIP(za)) {
225
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
226
0
        _zip_string_free(cdir->comment);
227
0
    }
228
5.20k
    else {
229
5.20k
        za->comment_orig = cdir->comment;
230
5.20k
    }
231
232
5.20k
    free(cdir);
233
234
5.20k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
33.4k
    for (idx = 0; idx < za->nentry; idx++) {
237
28.2k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
28.2k
        if (name == NULL) {
239
            /* keep src so discard does not get rid of it */
240
0
            zip_source_keep(src);
241
0
            zip_discard(za);
242
0
            return NULL;
243
0
        }
244
245
28.2k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
14
            if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) {
247
0
                _zip_error_copy(error, &za->error);
248
                /* keep src so discard does not get rid of it */
249
0
                zip_source_keep(src);
250
0
                zip_discard(za);
251
0
                return NULL;
252
0
            }
253
14
        }
254
28.2k
    }
255
256
5.20k
    za->ch_flags = za->flags;
257
258
5.20k
    return za;
259
5.20k
}
260
261
262
3.84k
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
3.84k
    if (err) {
264
3.84k
        ze = zip_error_code_zip(err);
265
3.84k
        switch (zip_error_system_type(err)) {
266
115
        case ZIP_ET_SYS:
267
2.03k
        case ZIP_ET_LIBZIP:
268
2.03k
            errno = zip_error_code_system(err);
269
2.03k
            break;
270
271
1.80k
        default:
272
1.80k
            break;
273
3.84k
        }
274
3.84k
    }
275
276
3.84k
    if (zep) {
277
3.84k
        *zep = ze;
278
3.84k
    }
279
3.84k
}
280
281
282
/* _zip_readcdir:
283
   tries to find a valid end-of-central-directory at the beginning of
284
   buf, and then the corresponding central directory entries.
285
   Returns a struct zip_cdir which contains the central directory
286
   entries, or NULL if unsuccessful. */
287
288
80.0k
static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error) {
289
80.0k
    zip_cdir_t *cd;
290
80.0k
    zip_uint16_t comment_len;
291
80.0k
    zip_uint64_t i, left;
292
80.0k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
80.0k
    zip_buffer_t *cd_buffer;
294
80.0k
    bool eocd64_found = false;
295
296
80.0k
    *cdirp = NULL;
297
298
80.0k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
289
        return false;
300
289
    }
301
302
79.8k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
2.35k
        eocd64_found = true;
304
2.35k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
2.35k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
129
        case CDIR_OK:
307
129
            break;
308
309
1.15k
        case CDIR_INVALID:
310
1.15k
            _zip_cdir_free(cd);
311
1.15k
            return true;
312
313
1.07k
        case CDIR_NOT_FOUND:
314
1.07k
            _zip_cdir_free(cd);
315
1.07k
            return false;
316
2.35k
        }
317
2.35k
    }
318
319
77.5k
    if ((cd->eocd_disk != 0 || cd->this_disk != 0) && !eocd64_found && cd->eocd_disk != cd->this_disk) {
320
        /* If the central directory doesn't start on this disk, we can't check that offset is valid. Check as much as we can instead. */
321
18.7k
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
7.46k
            _zip_cdir_free(cd);
324
7.46k
            return false;
325
7.46k
        }
326
11.2k
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
781
            _zip_cdir_free(cd);
329
781
            return false;
330
781
        }
331
11.2k
    }
332
333
69.3k
    if (!eocd64_found) {
334
69.2k
        if (cd->this_disk == 0 && cd->eocd_disk == 0 && cd->eocd_offset == 0 && cd->offset == 0 && cd->num_entries == 0) {
335
            /* An empty archive doesn't contain central directory entries. */
336
172
        }
337
69.0k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
62.7k
            _zip_cdir_free(cd);
339
62.7k
            return false;
340
62.7k
        }
341
69.2k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
6.61k
    if (!check_eocd(cd, za->open_flags, error)) {
346
78
        _zip_cdir_free(cd);
347
78
        return true;
348
78
    }
349
350
6.53k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
6.53k
    comment_len = _zip_buffer_get_16(buffer);
352
353
6.53k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
165
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
165
        _zip_cdir_free(cd);
357
165
        return true;
358
165
    }
359
360
6.36k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
2.25k
        zip_uint64_t tail_len;
362
363
2.25k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
2.25k
        tail_len = _zip_buffer_left(buffer);
365
366
2.25k
        if (tail_len != comment_len) {
367
2.23k
            if (za->open_flags & ZIP_CHECKCONS) {
368
0
                zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID);
369
0
                _zip_cdir_free(cd);
370
0
                return true;
371
0
            }
372
2.23k
            if (tail_len < comment_len) {
373
1.95k
                comment_len = tail_len;
374
1.95k
            }
375
2.23k
        }
376
377
2.25k
        if (comment_len) {
378
1.90k
            if ((cd->comment = _zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) {
379
0
                _zip_cdir_free(cd);
380
0
                return true;
381
0
            }
382
1.90k
        }
383
2.25k
    }
384
385
6.36k
    if (cd->offset >= buf_offset) {
386
5.78k
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
5.78k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
5.78k
        if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) {
391
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
392
0
            _zip_cdir_free(cd);
393
0
            return true;
394
0
        }
395
5.78k
        if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) {
396
0
            zip_error_set(error, ZIP_ER_MEMORY, 0);
397
0
            _zip_cdir_free(cd);
398
0
            return true;
399
0
        }
400
5.78k
    }
401
583
    else {
402
583
        cd_buffer = NULL;
403
404
583
        if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) {
405
0
            zip_error_set_from_source(error, za->src);
406
0
            _zip_cdir_free(cd);
407
0
            return true;
408
0
        }
409
410
        /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */
411
583
        if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) {
412
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
413
0
            _zip_cdir_free(cd);
414
0
            return true;
415
0
        }
416
583
    }
417
418
6.36k
    if (!_zip_cdir_grow(cd, cd->num_entries, error)) {
419
0
        _zip_cdir_free(cd);
420
0
        _zip_buffer_free(cd_buffer);
421
0
        return true;
422
0
    }
423
6.36k
    left = (zip_uint64_t)cd->size;
424
6.36k
    i = 0;
425
36.7k
    while (left > 0) {
426
31.4k
        bool grown = false;
427
31.4k
        zip_int64_t entry_size;
428
429
31.4k
        if (i == cd->nentry) {
430
            /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */
431
            /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */
432
433
708
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
103
                break;
435
103
            }
436
437
605
            if (!_zip_cdir_grow(cd, 0x10000, error)) {
438
0
                _zip_cdir_free(cd);
439
0
                _zip_buffer_free(cd_buffer);
440
0
                return true;
441
0
            }
442
605
            grown = true;
443
605
        }
444
445
31.3k
        if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, cd->is_zip64, 0, za->open_flags & ZIP_CHECKCONS, error)) < 0) {
446
1.03k
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
661
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
661
            }
449
374
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
63
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
63
            }
452
1.03k
            _zip_cdir_free(cd);
453
1.03k
            _zip_buffer_free(cd_buffer);
454
1.03k
            return true;
455
1.03k
        }
456
30.3k
        i++;
457
30.3k
        left -= (zip_uint64_t)entry_size;
458
30.3k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
5.33k
    if (i != cd->nentry || left > 0) {
462
132
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
132
        _zip_buffer_free(cd_buffer);
464
132
        _zip_cdir_free(cd);
465
132
        return true;
466
132
    }
467
468
5.20k
    if (za->open_flags & ZIP_CHECKCONS) {
469
0
        bool ok;
470
471
0
        if (cd_buffer) {
472
0
            ok = _zip_buffer_eof(cd_buffer);
473
0
        }
474
0
        else {
475
0
            zip_int64_t offset = zip_source_tell(za->src);
476
477
0
            if (offset < 0) {
478
0
                zip_error_set_from_source(error, za->src);
479
0
                _zip_cdir_free(cd);
480
0
                return true;
481
0
            }
482
0
            ok = ((zip_uint64_t)offset == cd->offset + cd->size);
483
0
        }
484
485
0
        if (!ok) {
486
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
487
0
            _zip_buffer_free(cd_buffer);
488
0
            _zip_cdir_free(cd);
489
0
            return true;
490
0
        }
491
0
    }
492
493
5.20k
    _zip_buffer_free(cd_buffer);
494
5.20k
    *cdirp = cd;
495
5.20k
    return true;
496
5.20k
}
497
498
499
71.3k
static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char *magic) {
500
71.3k
    if (buffer_offset <= offset) {
501
67.3k
        zip_uint8_t *data;
502
67.3k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
56.4k
            return false;
504
56.4k
        }
505
10.8k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
67.3k
    }
507
4.03k
    else {
508
4.03k
        zip_uint8_t data[MAGIC_LEN];
509
510
4.03k
        if (zip_source_seek(src, offset, SEEK_SET) < 0 || zip_source_read(src, data, MAGIC_LEN) != MAGIC_LEN) {
511
0
            return false;
512
0
        }
513
4.03k
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
4.03k
    }
515
71.3k
}
516
517
518
/* _zip_checkcons:
519
   Checks the consistency of the central directory by comparing central
520
   directory entries with local headers and checking for plausible
521
   file and header offsets. Returns -1 if not plausible, else the
522
   difference between the lowest and the highest fileposition reached */
523
524
0
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) {
525
0
    zip_uint64_t i;
526
0
    zip_uint64_t min, max, j, tail_length;
527
0
    struct zip_dirent temp;
528
0
    int detail;
529
530
0
    _zip_dirent_init(&temp);
531
0
    if (cd->nentry) {
532
0
        max = cd->entry[0].orig->offset;
533
0
        min = cd->entry[0].orig->offset;
534
0
    }
535
0
    else {
536
0
        min = max = 0;
537
0
    }
538
539
    /*
540
        Currently we use the presence of a Zip64 End of Central Directory do decide
541
        if the archive is Zip64 and use that to decide the size of data descriptors
542
        in local headers. This is not guaranteed to be correct. We could also assume
543
        Zip64 format if any of the central directory entries has a Zip64 extra field.
544
545
        Since we only read local headers when checking consistency, this is not a
546
        problem for normal use, but only when checking consistency for archives using
547
        data descriptors.
548
    */
549
550
0
    for (i = 0; i < cd->nentry; i++) {
551
0
        if (cd->entry[i].orig->offset < min) {
552
0
            min = cd->entry[i].orig->offset;
553
0
        }
554
0
        if (min > (zip_uint64_t)cd->offset) {
555
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
556
0
            return -1;
557
0
        }
558
559
0
        tail_length = _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE;
560
0
        if (ZIP_CHECK_ADD_OVERFLOW(cd->entry[i].orig->comp_size, tail_length) || ZIP_CHECK_ADD_OVERFLOW(cd->entry[i].orig->offset + tail_length, cd->entry[i].orig->comp_size)) {
561
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
562
0
            return -1;
563
0
        }
564
0
        j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + tail_length;
565
566
0
        if (j > max) {
567
0
            max = j;
568
0
        }
569
0
        if (max > (zip_uint64_t)cd->offset) {
570
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
571
0
            return -1;
572
0
        }
573
574
0
        if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) {
575
0
            zip_error_set_from_source(error, za->src);
576
0
            return -1;
577
0
        }
578
579
0
        if (_zip_dirent_read(&temp, za->src, NULL, true, cd->is_zip64, cd->entry[i].orig->comp_size, true, error) == -1) {
580
0
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
581
0
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
582
0
            }
583
0
            _zip_dirent_finalize(&temp);
584
0
            return -1;
585
0
        }
586
587
0
        if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
588
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_ENTRY_HEADER_MISMATCH, i));
589
0
            _zip_dirent_finalize(&temp);
590
0
            return -1;
591
0
        }
592
593
0
        cd->entry[i].orig->extra_fields.local = temp.extra_fields.local;
594
0
        cd->entry[i].orig->local_extra_fields_read = 1;
595
0
        temp.extra_fields.local = NULL;
596
597
0
        _zip_dirent_finalize(&temp);
598
599
0
        if ((detail = zip_dirent_check_consistency(cd->entry[i].orig)) != 0) {
600
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(detail, i));
601
0
            return -1;
602
0
        }
603
0
    }
604
605
0
    return (max - min) < ZIP_INT64_MAX ? (zip_int64_t)(max - min) : ZIP_INT64_MAX;
606
0
}
607
608
609
/* _zip_headercomp:
610
   compares a central directory entry and a local file header
611
   Return 0 if they are consistent, -1 if not. */
612
613
0
static int _zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) {
614
0
    if ((central->version_needed < local->version_needed)
615
#if 0
616
  /* some zip-files have different values in local
617
     and global headers for the bitflags */
618
  || (central->bitflags != local->bitflags)
619
#endif
620
0
        || (central->comp_method != local->comp_method) || (central->last_mod.time != local->last_mod.time) || (central->last_mod.date != local->last_mod.date) || !_zip_string_equal(central->filename, local->filename))
621
0
        return -1;
622
623
0
    if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) {
624
        /* InfoZip stores valid values in local header even when data descriptor is used.
625
           This is in violation of the appnote.
626
           macOS Archive sets the compressed size even when data descriptor is used ( but not the others),
627
           also in violation of the appnote.
628
        */
629
        /* if data descriptor is not used, the values must match */
630
0
        if ((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0) {
631
0
            return -1;
632
0
        }
633
        /* when using a data descriptor, the local header value must be zero or match */
634
0
        if ((local->crc != 0 && central->crc != local->crc) || (local->comp_size != 0 && central->comp_size != local->comp_size) || (local->uncomp_size != 0 && central->uncomp_size != local->uncomp_size)) {
635
0
            return -1;
636
0
        }
637
0
    }
638
639
0
    return 0;
640
0
}
641
642
643
9.04k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
644
9.04k
    zip_t *za;
645
646
9.04k
    if ((za = _zip_new(error)) == NULL) {
647
0
        return NULL;
648
0
    }
649
650
9.04k
    za->src = src;
651
9.04k
    za->open_flags = flags;
652
9.04k
    za->flags = 0;
653
9.04k
    za->ch_flags = 0;
654
9.04k
    za->write_crc = NULL;
655
656
9.04k
    if (flags & ZIP_RDONLY) {
657
0
        za->flags |= ZIP_AFL_RDONLY;
658
0
        za->ch_flags |= ZIP_AFL_RDONLY;
659
0
    }
660
661
9.04k
    return za;
662
9.04k
}
663
664
665
/*
666
 * tests for file existence
667
 */
668
9.04k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
669
9.04k
    struct zip_stat st;
670
671
9.04k
    zip_stat_init(&st);
672
9.04k
    if (zip_source_stat(src, &st) != 0) {
673
0
        zip_error_t *src_error = zip_source_error(src);
674
0
        if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) {
675
0
            return EXISTS_NOT;
676
0
        }
677
0
        _zip_error_copy(error, src_error);
678
0
        return EXISTS_ERROR;
679
0
    }
680
681
9.04k
    return EXISTS_OK;
682
9.04k
}
683
684
685
9.04k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
686
9.04k
    zip_cdir_t *cdir;
687
9.04k
    const zip_uint8_t *match;
688
9.04k
    zip_int64_t buf_offset;
689
9.04k
    zip_uint64_t buflen;
690
9.04k
    zip_error_t error;
691
9.04k
    zip_buffer_t *buffer;
692
693
9.04k
    if (len < EOCDLEN) {
694
298
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
695
298
        return NULL;
696
298
    }
697
698
8.74k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
699
8.74k
    if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) {
700
0
        zip_error_t *src_error = zip_source_error(za->src);
701
0
        if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) {
702
            /* seek before start of file on my machine */
703
0
            _zip_error_copy(&za->error, src_error);
704
0
            return NULL;
705
0
        }
706
0
    }
707
8.74k
    if ((buf_offset = zip_source_tell(za->src)) < 0) {
708
0
        zip_error_set_from_source(&za->error, za->src);
709
0
        return NULL;
710
0
    }
711
712
8.74k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
713
0
        return NULL;
714
0
    }
715
716
8.74k
    cdir = NULL;
717
8.74k
    if (buflen >= CDBUFSIZE) {
718
        /* EOCD64 locator is before EOCD, so leave place for it */
719
838
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
720
838
    }
721
8.74k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
722
723
8.74k
    match = NULL;
724
81.0k
    while ((match = find_eocd(buffer, match)) != NULL) {
725
80.0k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
726
80.0k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
727
7.76k
            if (cdir != NULL && (za->open_flags & ZIP_CHECKCONS) && _zip_checkcons(za, cdir, &error) < 0) {
728
0
                _zip_cdir_free(cdir);
729
0
                cdir = NULL;
730
0
            }
731
7.76k
            break;
732
7.76k
        }
733
80.0k
    }
734
735
8.74k
    _zip_buffer_free(buffer);
736
737
8.74k
    if (cdir == NULL) {
738
3.54k
        _zip_error_copy(&za->error, &error);
739
3.54k
    }
740
8.74k
    return cdir;
741
8.74k
}
742
743
744
81.0k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
745
81.0k
    const unsigned char *data = _zip_buffer_data(buffer);
746
81.0k
    const unsigned char *p;
747
748
81.0k
    if (last == NULL) {
749
8.74k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
750
0
            return NULL;
751
0
        }
752
8.74k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
753
8.74k
    }
754
72.3k
    else {
755
72.3k
        if (last == _zip_buffer_data(buffer)) {
756
203
            return NULL;
757
203
        }
758
72.1k
        last -= 1;
759
72.1k
    }
760
761
24.1M
    for (p = last; p >= data; p -= 1) {
762
24.1M
        if (*p == EOCD_MAGIC[0]) {
763
265k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
764
80.0k
                return p;
765
80.0k
            }
766
265k
        }
767
24.0M
        if (p == data) {
768
            /* Avoid undefined behavior by creating pointer outside buffer */
769
780
            break;
770
780
        }
771
24.0M
    }
772
773
780
    return NULL;
774
80.8k
}
775
776
777
80.0k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
778
80.0k
    zip_cdir_t *cd;
779
780
80.0k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
781
289
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
782
289
        return NULL;
783
289
    }
784
785
79.8k
    if ((cd = _zip_cdir_new(error)) == NULL) {
786
0
        return NULL;
787
0
    }
788
789
79.8k
    cd->eocd_offset = buf_offset + _zip_buffer_offset(buffer);
790
    /* This function is only called where EOCD magic was found, so no need to check that here. */
791
79.8k
    _zip_buffer_skip(buffer, MAGIC_LEN);
792
79.8k
    cd->is_zip64 = false;
793
79.8k
    cd->this_disk = _zip_buffer_get_16(buffer);
794
79.8k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
795
796
    /* number of cdir-entries on this disk */
797
79.8k
    cd->disk_entries = _zip_buffer_get_16(buffer);
798
    /* number of cdir-entries */
799
79.8k
    cd->num_entries = _zip_buffer_get_16(buffer);
800
79.8k
    cd->size = _zip_buffer_get_32(buffer);
801
79.8k
    cd->offset = _zip_buffer_get_32(buffer);
802
803
79.8k
    return cd;
804
79.8k
}
805
806
6.61k
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
807
6.61k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
808
78
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
809
78
        return false;
810
78
    }
811
812
6.53k
    if (cd->offset + cd->size < cd->offset) {
813
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
814
0
        return false;
815
0
    }
816
817
6.53k
    if ((flags & ZIP_CHECKCONS) && cd->offset + cd->size != cd->eocd_offset) {
818
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
819
0
            return false;
820
0
        }
821
822
6.53k
    return true;
823
6.53k
}
824
825
826
2.35k
cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) {
827
2.35k
    zip_uint64_t offset;
828
2.35k
    zip_uint8_t eocd[EOCD64LEN];
829
2.35k
    zip_uint64_t eocd_offset;
830
2.35k
    zip_uint64_t size, nentry, i, eocdloc_offset;
831
2.35k
    bool free_buffer;
832
2.35k
    zip_uint32_t num_disks, eocd_disk, this_disk;
833
834
    /* The offset of the end of the buffer is less than ZIP_UINT64_MAX, so this can't overflow. */
835
2.35k
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
836
837
2.35k
    _zip_buffer_get(buffer, 4); /* magic already verified */
838
839
2.35k
    eocd_disk = _zip_buffer_get_32(buffer);
840
2.35k
    eocd_offset = _zip_buffer_get_64(buffer);
841
2.35k
    num_disks = _zip_buffer_get_32(buffer);
842
843
2.35k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
844
1.07k
        return CDIR_NOT_FOUND;
845
1.07k
    }
846
847
1.28k
    if (num_disks != 1) {
848
56
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
849
56
        return CDIR_INVALID;
850
56
    }
851
852
    /* valid seek value for start of EOCD */
853
1.22k
    if (eocd_offset > ZIP_INT64_MAX) {
854
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
855
0
        return CDIR_INVALID;
856
0
    }
857
858
    /* does EOCD fit before EOCD locator? */
859
1.22k
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
860
16
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
861
16
        return CDIR_INVALID;
862
16
    }
863
864
    /* make sure current position of buffer is beginning of EOCD */
865
1.20k
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
866
1.15k
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
867
1.15k
        free_buffer = false;
868
1.15k
    }
869
51
    else {
870
51
        if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) {
871
0
            zip_error_set_from_source(error, src);
872
0
            return CDIR_INVALID;
873
0
        }
874
51
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
875
0
            return CDIR_INVALID;
876
0
        }
877
51
        free_buffer = true;
878
51
    }
879
880
1.20k
    if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) {
881
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_WRONG_MAGIC);
882
0
        if (free_buffer) {
883
0
            _zip_buffer_free(buffer);
884
0
        }
885
0
        return CDIR_INVALID;
886
0
    }
887
888
    /* size of EOCD */
889
1.20k
    size = _zip_buffer_get_64(buffer);
890
891
    /* Is there a hole between EOCD and EOCD locator, or do they overlap? Also check for overflow. */
892
1.20k
    if ((flags & ZIP_CHECKCONS) && (ZIP_CHECK_ADD_OVERFLOW(size, eocd_offset + 12) || size + eocd_offset + 12 != eocdloc_offset)) {
893
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
894
0
        if (free_buffer) {
895
0
            _zip_buffer_free(buffer);
896
0
        }
897
0
        return CDIR_INVALID;
898
0
    }
899
900
1.20k
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
901
902
1.20k
    this_disk = _zip_buffer_get_32(buffer);
903
1.20k
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
904
118
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
905
118
        if (free_buffer) {
906
21
            _zip_buffer_free(buffer);
907
21
        }
908
118
        return CDIR_INVALID;
909
118
    }
910
911
1.09k
    i = _zip_buffer_get_64(buffer);
912
1.09k
    nentry = _zip_buffer_get_64(buffer);
913
914
1.09k
    if (nentry != i) {
915
169
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
916
169
        if (free_buffer) {
917
12
            _zip_buffer_free(buffer);
918
12
        }
919
169
        return CDIR_INVALID;
920
169
    }
921
922
922
    size = _zip_buffer_get_64(buffer);
923
922
    offset = _zip_buffer_get_64(buffer);
924
925
    /* did we read past the end of the buffer? */
926
922
    if (!_zip_buffer_ok(buffer)) {
927
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
928
0
        if (free_buffer) {
929
0
            _zip_buffer_free(buffer);
930
0
        }
931
0
        return CDIR_INVALID;
932
0
    }
933
934
922
    if (free_buffer) {
935
18
        _zip_buffer_free(buffer);
936
18
    }
937
938
922
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
939
108
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
940
108
        return CDIR_INVALID;
941
108
    }
942
943
814
    if (nentry > size / CDENTRYSIZE) {
944
79
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
945
79
        return CDIR_INVALID;
946
79
    }
947
948
735
    if ((cdir->size != 0xffffffff && cdir->size != size) || (cdir->offset != 0xffffffff && cdir->offset != offset) || (cdir->num_entries != 0xffff && cdir->num_entries != nentry) || (cdir->disk_entries != 0xffff && cdir->disk_entries != i) || (cdir->this_disk != 0xffff && cdir->this_disk != this_disk) || (cdir->eocd_disk != 0xffff && cdir->eocd_disk != eocd_disk)) {
949
606
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
950
606
        return CDIR_INVALID;
951
606
    }
952
953
129
    cdir->is_zip64 = true;
954
129
    cdir->size = size;
955
129
    cdir->offset = offset;
956
129
    cdir->disk_entries = i;
957
129
    cdir->num_entries = nentry;
958
129
    cdir->this_disk = this_disk;
959
129
    cdir->eocd_disk = eocd_disk;
960
129
    cdir->eocd_offset = eocd_offset;
961
962
129
    return CDIR_OK;
963
735
}
964
965
966
506
static int decode_hex(char c) {
967
506
    if (c >= '0' && c <= '9') {
968
370
        return c - '0';
969
370
    }
970
136
    else if (c >= 'A' && c <= 'F') {
971
61
        return c - 'A' + 10;
972
61
    }
973
75
    else {
974
75
        return -1;
975
75
    }
976
506
}
977
978
/* _zip_check_torrentzip:
979
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
980
981
5.20k
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
982
5.20k
    zip_uint32_t crc_should;
983
5.20k
    char buf[8 + 1];
984
5.20k
    size_t i;
985
986
5.20k
    if (cdir == NULL) {
987
0
        return;
988
0
    }
989
990
5.20k
    if (_zip_string_length(cdir->comment) != TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH || strncmp((const char *)cdir->comment->raw, TORRENTZIP_SIGNATURE, TORRENTZIP_SIGNATURE_LENGTH) != 0) {
991
5.11k
        return;
992
5.11k
    }
993
994
85
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
995
85
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
996
85
    crc_should = 0;
997
284
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
998
253
        int low, high;
999
253
        high = decode_hex((buf[i]));
1000
253
        low = decode_hex(buf[i + 1]);
1001
253
        if (high < 0 || low < 0) {
1002
54
            return;
1003
54
        }
1004
199
        crc_should = (crc_should << 8) + (high << 4) + low;
1005
199
    }
1006
1007
31
    {
1008
31
        zip_stat_t st;
1009
31
        zip_source_t *src_window;
1010
31
        zip_source_t *src_crc;
1011
31
        zip_uint8_t buffer[512];
1012
31
        zip_int64_t ret;
1013
1014
31
        zip_stat_init(&st);
1015
31
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1016
31
        st.size = cdir->size;
1017
31
        st.crc = crc_should;
1018
31
        if ((src_window = _zip_source_window_new(za->src, cdir->offset, cdir->size, &st, 0, NULL, NULL, NULL, 0, false, NULL)) == NULL) {
1019
0
            return;
1020
0
        }
1021
31
        if ((src_crc = zip_source_crc_create(src_window, 1, NULL)) == NULL) {
1022
0
            zip_source_free(src_window);
1023
0
            return;
1024
0
        }
1025
31
        if (zip_source_open(src_crc) != 0) {
1026
0
            zip_source_free(src_crc);
1027
0
            return;
1028
0
        }
1029
62
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1030
31
        }
1031
31
        zip_source_free(src_crc);
1032
31
        if (ret < 0) {
1033
31
            return;
1034
31
        }
1035
31
    }
1036
1037
    /* TODO: if check consistency, check cdir entries for valid values */
1038
0
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1039
0
}