Coverage Report

Created: 2026-11-12 06:52

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
0
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
0
    zip_t *za;
69
0
    zip_source_t *src;
70
0
    struct zip_error error;
71
72
0
    zip_error_init(&error);
73
0
    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
0
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
0
        zip_source_free(src);
81
0
        _zip_set_open_error(zep, &error, 0);
82
0
        zip_error_fini(&error);
83
0
        return NULL;
84
0
    }
85
86
0
    zip_error_fini(&error);
87
0
    return za;
88
0
}
89
90
91
5.09k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
5.09k
    unsigned int flags;
93
5.09k
    zip_int64_t supported;
94
5.09k
    exists_t exists;
95
96
5.09k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
5.09k
    flags = (unsigned int)_flags;
101
102
5.09k
    supported = zip_source_supports(src);
103
5.09k
    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
5.09k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
5.09k
    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
5.09k
    exists = _zip_file_exists(src, error);
117
5.09k
    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
5.09k
    default: {
129
5.09k
        zip_t *za;
130
5.09k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
5.09k
        if (zip_source_open(src) < 0) {
135
0
            zip_error_set_from_source(error, src);
136
0
            return NULL;
137
0
        }
138
139
5.09k
        if (flags & ZIP_TRUNCATE) {
140
0
            za = _zip_allocate_new(src, flags, error);
141
0
        }
142
5.09k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
5.09k
            za = _zip_open(src, flags, error);
145
5.09k
        }
146
147
5.09k
        if (za == NULL) {
148
2.77k
            zip_source_close(src);
149
2.77k
            return NULL;
150
2.77k
        }
151
2.32k
        return za;
152
5.09k
    }
153
5.09k
    }
154
5.09k
}
155
156
157
585
static bool _is_truncated_zip(zip_source_t *src) {
158
585
    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
585
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
585
    if (zip_source_read(src, data, 4) != 4) {
166
3
        return false;
167
3
    }
168
169
582
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
19
        return true;
172
19
    }
173
563
    return false;
174
582
}
175
176
177
5.09k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
5.09k
    zip_t *za;
179
5.09k
    zip_cdir_t *cdir;
180
5.09k
    struct zip_stat st;
181
5.09k
    zip_uint64_t len, idx;
182
183
5.09k
    zip_stat_init(&st);
184
5.09k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
5.09k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
5.09k
    len = st.size;
193
194
195
5.09k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
5.09k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
5.09k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
2.77k
        _zip_error_copy(error, &za->error);
206
2.77k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
585
            if (_is_truncated_zip(src)) {
209
19
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
19
            }
211
585
        }
212
        /* keep src so discard does not get rid of it */
213
2.77k
        zip_source_keep(src);
214
2.77k
        zip_discard(za);
215
2.77k
        return NULL;
216
2.77k
    }
217
218
2.32k
    za->entry = cdir->entry;
219
2.32k
    za->nentry = cdir->nentry;
220
2.32k
    za->nentry_alloc = cdir->nentry_alloc;
221
222
2.32k
    zip_check_torrentzip(za, cdir);
223
224
2.32k
    if (ZIP_IS_TORRENTZIP(za)) {
225
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
226
4
        _zip_string_free(cdir->comment);
227
4
    }
228
2.31k
    else {
229
2.31k
        za->comment_orig = cdir->comment;
230
2.31k
    }
231
232
2.32k
    free(cdir);
233
234
2.32k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
41.5k
    for (idx = 0; idx < za->nentry; idx++) {
237
39.2k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
39.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
39.2k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
25.5k
            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
25.5k
        }
254
39.2k
    }
255
256
2.32k
    za->ch_flags = za->flags;
257
258
2.32k
    return za;
259
2.32k
}
260
261
262
0
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
0
    if (err) {
264
0
        ze = zip_error_code_zip(err);
265
0
        switch (zip_error_system_type(err)) {
266
0
        case ZIP_ET_SYS:
267
0
        case ZIP_ET_LIBZIP:
268
0
            errno = zip_error_code_system(err);
269
0
            break;
270
271
0
        default:
272
0
            break;
273
0
        }
274
0
    }
275
276
0
    if (zep) {
277
0
        *zep = ze;
278
0
    }
279
0
}
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
48.2k
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
48.2k
    zip_cdir_t *cd;
290
48.2k
    zip_uint16_t comment_len;
291
48.2k
    zip_uint64_t i, left;
292
48.2k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
48.2k
    zip_buffer_t *cd_buffer;
294
48.2k
    bool eocd64_found = false;
295
296
48.2k
    *cdirp = NULL;
297
298
48.2k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
298
        return false;
300
298
    }
301
302
47.9k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
12.3k
        eocd64_found = true;
304
12.3k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
12.3k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
206
        case CDIR_OK:
307
206
            break;
308
309
841
        case CDIR_INVALID:
310
841
            _zip_cdir_free(cd);
311
841
            return true;
312
313
11.3k
        case CDIR_NOT_FOUND:
314
11.3k
            _zip_cdir_free(cd);
315
11.3k
            return false;
316
12.3k
        }
317
12.3k
    }
318
319
35.8k
    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
25.6k
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
3.15k
            _zip_cdir_free(cd);
324
3.15k
            return false;
325
3.15k
        }
326
22.5k
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
2.20k
            _zip_cdir_free(cd);
329
2.20k
            return false;
330
2.20k
        }
331
22.5k
    }
332
333
30.4k
    if (!eocd64_found) {
334
30.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
350
        }
337
29.8k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
26.7k
            _zip_cdir_free(cd);
339
26.7k
            return false;
340
26.7k
        }
341
30.2k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
3.73k
    if (!check_eocd(cd, za->open_flags, error)) {
346
114
        _zip_cdir_free(cd);
347
114
        return true;
348
114
    }
349
350
3.62k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
3.62k
    comment_len = _zip_buffer_get_16(buffer);
352
353
3.62k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
137
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
137
        _zip_cdir_free(cd);
357
137
        return true;
358
137
    }
359
360
3.48k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
2.92k
        zip_uint64_t tail_len;
362
363
2.92k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
2.92k
        tail_len = _zip_buffer_left(buffer);
365
366
2.92k
        if (tail_len != comment_len) {
367
2.74k
            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.74k
            if (tail_len < comment_len) {
373
2.63k
                comment_len = (zip_uint16_t)tail_len;
374
2.63k
            }
375
2.74k
        }
376
377
2.92k
        if (comment_len) {
378
711
            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
711
        }
383
2.92k
    }
384
385
3.48k
    if (cd->offset >= buf_offset) {
386
3.27k
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
3.27k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
3.27k
        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
3.27k
        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
3.27k
    }
401
213
    else {
402
213
        cd_buffer = NULL;
403
404
213
        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
213
        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
213
    }
417
418
3.48k
    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
3.48k
    left = (zip_uint64_t)cd->size;
424
3.48k
    i = 0;
425
97.8k
    while (left > 0) {
426
95.4k
        bool grown = false;
427
95.4k
        zip_int64_t entry_size;
428
429
95.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
265
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
60
                break;
435
60
            }
436
437
205
            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
205
            grown = true;
443
205
        }
444
445
95.4k
        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.00k
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
648
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
648
            }
449
359
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
35
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
35
            }
452
1.00k
            _zip_cdir_free(cd);
453
1.00k
            _zip_buffer_free(cd_buffer);
454
1.00k
            return true;
455
1.00k
        }
456
94.4k
        i++;
457
94.4k
        left -= (zip_uint64_t)entry_size;
458
94.4k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
2.47k
    if (i != cd->nentry || left > 0) {
462
154
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
154
        _zip_buffer_free(cd_buffer);
464
154
        _zip_cdir_free(cd);
465
154
        return true;
466
154
    }
467
468
2.32k
    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
2.32k
    _zip_buffer_free(cd_buffer);
494
2.32k
    *cdirp = cd;
495
2.32k
    return true;
496
2.32k
}
497
498
499
42.2k
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
42.2k
    if (buffer_offset <= offset) {
501
40.1k
        zip_uint8_t *data;
502
40.1k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
31.2k
            return false;
504
31.2k
        }
505
8.91k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
40.1k
    }
507
2.12k
    else {
508
2.12k
        zip_uint8_t data[MAGIC_LEN];
509
510
2.12k
        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
2.12k
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
2.12k
    }
515
42.2k
}
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
5.09k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
644
5.09k
    zip_t *za;
645
646
5.09k
    if ((za = _zip_new(error)) == NULL) {
647
0
        return NULL;
648
0
    }
649
650
5.09k
    za->src = src;
651
5.09k
    za->open_flags = flags;
652
5.09k
    za->flags = 0;
653
5.09k
    za->ch_flags = 0;
654
5.09k
    za->write_crc = NULL;
655
656
5.09k
    if (flags & ZIP_RDONLY) {
657
0
        za->flags |= ZIP_AFL_RDONLY;
658
0
        za->ch_flags |= ZIP_AFL_RDONLY;
659
0
    }
660
661
5.09k
    return za;
662
5.09k
}
663
664
665
/*
666
 * tests for file existence
667
 */
668
5.09k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
669
5.09k
    struct zip_stat st;
670
671
5.09k
    zip_stat_init(&st);
672
5.09k
    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
5.09k
    return EXISTS_OK;
682
5.09k
}
683
684
685
5.09k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
686
5.09k
    zip_cdir_t *cdir;
687
5.09k
    const zip_uint8_t *match;
688
5.09k
    zip_int64_t buf_offset;
689
5.09k
    zip_uint64_t buflen;
690
5.09k
    zip_error_t error;
691
5.09k
    zip_buffer_t *buffer;
692
693
5.09k
    if (len < EOCDLEN) {
694
45
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
695
45
        return NULL;
696
45
    }
697
698
5.05k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
699
5.05k
    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
5.05k
    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
5.05k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
713
0
        return NULL;
714
0
    }
715
716
5.05k
    cdir = NULL;
717
5.05k
    if (buflen >= CDBUFSIZE) {
718
        /* EOCD64 locator is before EOCD, so leave place for it */
719
286
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
720
286
    }
721
5.05k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
722
723
5.05k
    match = NULL;
724
48.7k
    while ((match = find_eocd(buffer, match)) != NULL) {
725
48.2k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
726
48.2k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
727
4.57k
            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
4.57k
            break;
732
4.57k
        }
733
48.2k
    }
734
735
5.05k
    _zip_buffer_free(buffer);
736
737
5.05k
    if (cdir == NULL) {
738
2.72k
        _zip_error_copy(&za->error, &error);
739
2.72k
    }
740
5.05k
    return cdir;
741
5.05k
}
742
743
744
48.7k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
745
48.7k
    const unsigned char *data = _zip_buffer_data(buffer);
746
48.7k
    const unsigned char *p;
747
748
48.7k
    if (last == NULL) {
749
5.05k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
750
0
            return NULL;
751
0
        }
752
5.05k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
753
5.05k
    }
754
43.7k
    else {
755
43.7k
        if (last == _zip_buffer_data(buffer)) {
756
231
            return NULL;
757
231
        }
758
43.4k
        last -= 1;
759
43.4k
    }
760
761
5.42M
    for (p = last; p >= data; p -= 1) {
762
5.42M
        if (*p == EOCD_MAGIC[0]) {
763
204k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
764
48.2k
                return p;
765
48.2k
            }
766
204k
        }
767
5.37M
        if (p == data) {
768
            /* Avoid undefined behavior by creating pointer outside buffer */
769
244
            break;
770
244
        }
771
5.37M
    }
772
773
244
    return NULL;
774
48.5k
}
775
776
777
48.2k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
778
48.2k
    zip_cdir_t *cd;
779
780
48.2k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
781
298
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
782
298
        return NULL;
783
298
    }
784
785
47.9k
    if ((cd = _zip_cdir_new(error)) == NULL) {
786
0
        return NULL;
787
0
    }
788
789
47.9k
    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
47.9k
    _zip_buffer_skip(buffer, MAGIC_LEN);
792
47.9k
    cd->is_zip64 = false;
793
47.9k
    cd->this_disk = _zip_buffer_get_16(buffer);
794
47.9k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
795
796
    /* number of cdir-entries on this disk */
797
47.9k
    cd->disk_entries = _zip_buffer_get_16(buffer);
798
    /* number of cdir-entries */
799
47.9k
    cd->num_entries = _zip_buffer_get_16(buffer);
800
47.9k
    cd->size = _zip_buffer_get_32(buffer);
801
47.9k
    cd->offset = _zip_buffer_get_32(buffer);
802
803
47.9k
    return cd;
804
47.9k
}
805
806
3.73k
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
807
3.73k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
808
114
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
809
114
        return false;
810
114
    }
811
812
3.62k
    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
3.62k
    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
3.62k
    return true;
823
3.62k
}
824
825
826
12.3k
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
12.3k
    zip_uint64_t offset;
828
12.3k
    zip_uint8_t eocd[EOCD64LEN];
829
12.3k
    zip_uint64_t eocd_offset;
830
12.3k
    zip_uint64_t size, nentry, i, eocdloc_offset;
831
12.3k
    bool free_buffer;
832
12.3k
    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
12.3k
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
836
837
12.3k
    _zip_buffer_get(buffer, 4); /* magic already verified */
838
839
12.3k
    eocd_disk = _zip_buffer_get_32(buffer);
840
12.3k
    eocd_offset = _zip_buffer_get_64(buffer);
841
12.3k
    num_disks = _zip_buffer_get_32(buffer);
842
843
12.3k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
844
11.3k
        return CDIR_NOT_FOUND;
845
11.3k
    }
846
847
1.04k
    if (num_disks != 1) {
848
47
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
849
47
        return CDIR_INVALID;
850
47
    }
851
852
    /* valid seek value for start of EOCD */
853
1.00k
    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.00k
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
860
26
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
861
26
        return CDIR_INVALID;
862
26
    }
863
864
    /* make sure current position of buffer is beginning of EOCD */
865
974
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
866
947
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
867
947
        free_buffer = false;
868
947
    }
869
27
    else {
870
27
        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
27
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
875
0
            return CDIR_INVALID;
876
0
        }
877
27
        free_buffer = true;
878
27
    }
879
880
974
    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
974
    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
974
    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
974
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
901
902
974
    this_disk = _zip_buffer_get_32(buffer);
903
974
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
904
97
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
905
97
        if (free_buffer) {
906
11
            _zip_buffer_free(buffer);
907
11
        }
908
97
        return CDIR_INVALID;
909
97
    }
910
911
877
    i = _zip_buffer_get_64(buffer);
912
877
    nentry = _zip_buffer_get_64(buffer);
913
914
877
    if (nentry != i) {
915
89
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
916
89
        if (free_buffer) {
917
2
            _zip_buffer_free(buffer);
918
2
        }
919
89
        return CDIR_INVALID;
920
89
    }
921
922
788
    size = _zip_buffer_get_64(buffer);
923
788
    offset = _zip_buffer_get_64(buffer);
924
925
    /* did we read past the end of the buffer? */
926
788
    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
788
    if (free_buffer) {
935
14
        _zip_buffer_free(buffer);
936
14
    }
937
938
788
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
939
67
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
940
67
        return CDIR_INVALID;
941
67
    }
942
943
721
    if (nentry > size / CDENTRYSIZE) {
944
82
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
945
82
        return CDIR_INVALID;
946
82
    }
947
948
639
    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
433
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
950
433
        return CDIR_INVALID;
951
433
    }
952
953
206
    cdir->is_zip64 = true;
954
206
    cdir->size = size;
955
206
    cdir->offset = offset;
956
206
    cdir->disk_entries = i;
957
206
    cdir->num_entries = nentry;
958
206
    cdir->this_disk = this_disk;
959
206
    cdir->eocd_disk = eocd_disk;
960
206
    cdir->eocd_offset = eocd_offset;
961
962
206
    return CDIR_OK;
963
639
}
964
965
966
672
static int decode_hex(char c) {
967
672
    if (c >= '0' && c <= '9') {
968
371
        return c - '0';
969
371
    }
970
301
    else if (c >= 'A' && c <= 'F') {
971
240
        return c - 'A' + 10;
972
240
    }
973
61
    else {
974
61
        return -1;
975
61
    }
976
672
}
977
978
/* _zip_check_torrentzip:
979
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
980
981
2.32k
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
982
2.32k
    zip_uint32_t crc_should;
983
2.32k
    char buf[8 + 1];
984
2.32k
    size_t i;
985
986
2.32k
    if (cdir == NULL) {
987
0
        return;
988
0
    }
989
990
2.32k
    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
2.22k
        return;
992
2.22k
    }
993
994
102
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
995
102
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
996
102
    crc_should = 0;
997
400
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
998
336
        int low, high;
999
336
        high = decode_hex((buf[i]));
1000
336
        low = decode_hex(buf[i + 1]);
1001
336
        if (high < 0 || low < 0) {
1002
38
            return;
1003
38
        }
1004
298
        crc_should = (crc_should << 8) + (high << 4) + low;
1005
298
    }
1006
1007
64
    {
1008
64
        zip_stat_t st;
1009
64
        zip_source_t *src_window;
1010
64
        zip_source_t *src_crc;
1011
64
        zip_uint8_t buffer[512];
1012
64
        zip_int64_t ret;
1013
1014
64
        zip_stat_init(&st);
1015
64
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1016
64
        st.size = cdir->size;
1017
64
        st.crc = crc_should;
1018
64
        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
64
        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
64
        if (zip_source_open(src_crc) != 0) {
1026
0
            zip_source_free(src_crc);
1027
0
            return;
1028
0
        }
1029
236
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1030
172
        }
1031
64
        zip_source_free(src_crc);
1032
64
        if (ret < 0) {
1033
60
            return;
1034
60
        }
1035
64
    }
1036
1037
    /* TODO: if check consistency, check cdir entries for valid values */
1038
4
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1039
4
}