1 /* $OpenBSD: zipopen.c,v 1.1 2022/10/22 14:41:27 millert Exp $ */
2
3 /*
4 * Copyright (c) 2022 Todd C. Miller <Todd.Miller@sudo.ws>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <limits.h>
25 #include <zlib.h>
26 #include "compress.h"
27
28 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
29
30 /* Signatures for zip file headers we use. */
31 #define ZIPMAG 0x4b50 /* first two bytes of the zip signature */
32 #define LOCREM 0x0403 /* remaining two bytes in zip signature */
33 #define LOCSIG 0x04034b50 /* local file header signature */
34 #define EXTSIG 0x08074b50 /* extended local header signature */
35
36 /* Header sizes. */
37 #define LOCHDR 30 /* size of local header, including signature */
38 #define EXTHDR 16 /* size of extended local header, inc sig */
39
40 /* General purpose flag bits. */
41 #define CRPFLG 1 /* flag bit for encrypted entry */
42 #define EXTFLG 8 /* flag bit for extended local header */
43
44 /* Extra field definitions */
45 #define EF_ZIP64 0x0001 /* zip64 support */
46 #define EF_TIME 0x5455 /* mtime, atime, ctime in UTC ("UT") */
47 #define EF_IZUNIX 0x5855 /* UNIX extra field ID ("UX") */
48
49 #define Z_STORED 0 /* Stored uncompressed in .zip */
50
51 struct zip_state {
52 z_stream z_stream; /* libz stream */
53 uint8_t z_buf[Z_BUFSIZE]; /* I/O buffer */
54 uint8_t z_eof; /* set if end of input file */
55 uint8_t z_zip64; /* 64-bit file sizes */
56 uint16_t z_method; /* Z_DEFLATE or Z_STORED */
57 uint16_t z_flags; /* general purpose flags */
58 int z_fd; /* zip file descriptor */
59 uint32_t z_time; /* timestamp (mtime) */
60 uint32_t z_crc; /* crc32 of uncompressed data */
61 uint32_t z_ocrc; /* crc32 of uncompressed data (from header) */
62 uint32_t z_hlen; /* length of the zip header */
63 uint64_t z_ulen; /* uncompressed data length (from header) */
64 uint64_t z_total_in; /* # bytes in */
65 uint64_t z_total_out; /* # bytes out */
66 };
67
68 static int
get_byte(struct zip_state * s)69 get_byte(struct zip_state *s)
70 {
71 if (s->z_eof)
72 return EOF;
73
74 if (s->z_stream.avail_in == 0) {
75 ssize_t nread = read(s->z_fd, s->z_buf, Z_BUFSIZE);
76 if (nread <= 0) {
77 s->z_eof = 1;
78 return EOF;
79 }
80 s->z_stream.avail_in = nread;
81 s->z_stream.next_in = s->z_buf;
82 }
83 s->z_stream.avail_in--;
84 return *s->z_stream.next_in++;
85 }
86
87 static uint16_t
get_uint16(struct zip_state * s)88 get_uint16(struct zip_state *s)
89 {
90 uint16_t x;
91
92 x = ((uint16_t)(get_byte(s) & 0xff));
93 x |= ((uint16_t)(get_byte(s) & 0xff))<<8;
94 return x;
95 }
96
97 static uint32_t
get_uint32(struct zip_state * s)98 get_uint32(struct zip_state *s)
99 {
100 uint32_t x;
101
102 x = ((uint32_t)(get_byte(s) & 0xff));
103 x |= ((uint32_t)(get_byte(s) & 0xff))<<8;
104 x |= ((uint32_t)(get_byte(s) & 0xff))<<16;
105 x |= ((uint32_t)(get_byte(s) & 0xff))<<24;
106 return x;
107 }
108
109 static uint64_t
get_uint64(struct zip_state * s)110 get_uint64(struct zip_state *s)
111 {
112 uint64_t x;
113
114 x = ((uint64_t)(get_byte(s) & 0xff));
115 x |= ((uint64_t)(get_byte(s) & 0xff))<<8;
116 x |= ((uint64_t)(get_byte(s) & 0xff))<<16;
117 x |= ((uint64_t)(get_byte(s) & 0xff))<<24;
118 x |= ((uint64_t)(get_byte(s) & 0xff))<<32;
119 x |= ((uint64_t)(get_byte(s) & 0xff))<<40;
120 x |= ((uint64_t)(get_byte(s) & 0xff))<<48;
121 x |= ((uint64_t)(get_byte(s) & 0xff))<<56;
122 return x;
123 }
124
125 static int
get_header(struct zip_state * s,char * name,int gotmagic)126 get_header(struct zip_state *s, char *name, int gotmagic)
127 {
128 int c, got_mtime = 0;
129 uint16_t namelen, extlen;
130 uint32_t sig;
131
132 /* Check the zip local file header signature. */
133 if (!gotmagic) {
134 sig = get_uint32(s);
135 if (sig != LOCSIG) {
136 errno = EFTYPE;
137 return -1;
138 }
139 } else {
140 sig = get_uint16(s);
141 if (sig != LOCREM) {
142 errno = EFTYPE;
143 return -1;
144 }
145 }
146
147 /* Read the local header fields. */
148 get_uint16(s); /* min version */
149 s->z_flags = get_uint16(s); /* general purpose flags */
150 s->z_method = get_uint16(s); /* compression method */
151 get_uint32(s); /* DOS format mtime */
152 s->z_ocrc = get_uint32(s); /* 32-bit CRC */
153 get_uint32(s); /* compressed size */
154 s->z_ulen = get_uint32(s); /* uncompressed size */
155 namelen = get_uint16(s); /* file name length */
156 extlen = get_uint16(s); /* length of extra fields */
157 s->z_hlen = LOCHDR;
158
159 /* Encrypted files not supported. */
160 if (s->z_flags & CRPFLG) {
161 errno = EFTYPE;
162 return -1;
163 }
164
165 /* Supported compression methods are deflate and store. */
166 if (s->z_method != Z_DEFLATED && s->z_method != Z_STORED) {
167 errno = EFTYPE;
168 return -1;
169 }
170
171 /* Store the original file name if present. */
172 if (namelen != 0 && name != NULL) {
173 const char *ep = name + PATH_MAX - 1;
174 for (; namelen > 0; namelen--) {
175 if ((c = get_byte(s)) == EOF)
176 break;
177 s->z_hlen++;
178 if (c == '\0')
179 break;
180 if (name < ep)
181 *name++ = c;
182 }
183 *name = '\0';
184 }
185
186 /* Parse extra fields, if any. */
187 while (extlen >= 4) {
188 uint16_t sig;
189 int fieldlen;
190
191 sig = get_uint16(s);
192 fieldlen = get_uint16(s);
193 s->z_hlen += 4;
194 extlen -= 4;
195
196 switch (sig) {
197 case EF_ZIP64:
198 /* 64-bit file sizes */
199 s->z_zip64 = 1;
200 if (fieldlen >= 8) {
201 s->z_ulen = get_uint64(s);
202 s->z_hlen += 8;
203 extlen -= 8;
204 fieldlen -= 8;
205 }
206 break;
207 case EF_TIME:
208 /* UTC timestamps */
209 if ((c = get_byte(s)) == EOF)
210 break;
211 s->z_hlen++;
212 extlen--;
213 fieldlen--;
214 if (c & 1) {
215 got_mtime = 1;
216 s->z_time = get_uint32(s);
217 s->z_hlen += 4;
218 extlen -= 4;
219 fieldlen -= 4;
220 }
221 break;
222 case EF_IZUNIX:
223 /* We prefer EF_TIME if it is present. */
224 if (got_mtime)
225 break;
226
227 /* skip atime, store mtime. */
228 (void)get_uint32(s);
229 s->z_time = get_uint32(s);
230 s->z_hlen += 8;
231 extlen -= 8;
232 fieldlen -= 8;
233 break;
234 default:
235 break;
236 }
237
238 /* Consume any unparsed bytes in the field. */
239 for (; fieldlen > 0; fieldlen--) {
240 if (get_byte(s) == EOF)
241 break;
242 s->z_hlen++;
243 extlen--;
244 }
245 }
246 for (; extlen > 0; extlen--) {
247 if (get_byte(s) == EOF)
248 break;
249 s->z_hlen++;
250 }
251
252 return 0;
253 }
254
255 void *
zip_ropen(int fd,char * name,int gotmagic)256 zip_ropen(int fd, char *name, int gotmagic)
257 {
258 struct zip_state *s;
259
260 if (fd < 0)
261 return NULL;
262
263 if ((s = calloc(1, sizeof(*s))) == NULL)
264 return NULL;
265
266 s->z_fd = fd;
267 s->z_crc = crc32(0, NULL, 0);
268
269 /* Request a raw inflate, there is no zlib/gzip header present. */
270 if (inflateInit2(&s->z_stream, -MAX_WBITS) != Z_OK) {
271 free(s);
272 return NULL;
273 }
274 s->z_stream.next_in = s->z_buf;
275 s->z_stream.avail_out = sizeof(s->z_buf);
276
277 /* Read the zip header. */
278 if (get_header(s, name, gotmagic) != 0) {
279 zip_close(s, NULL, NULL, NULL);
280 s = NULL;
281 }
282
283 return s;
284 }
285
286 static int
zip_store(struct zip_state * s)287 zip_store(struct zip_state *s)
288 {
289 int error = Z_OK;
290 uLong copy_len;
291
292 if ((int)s->z_stream.avail_in <= 0)
293 return s->z_stream.avail_in == 0 ? Z_STREAM_END : Z_DATA_ERROR;
294
295 /* For stored files we rely on z_ulen being set. */
296 copy_len = MINIMUM(s->z_stream.avail_out, s->z_stream.avail_in);
297 if (copy_len >= s->z_ulen - s->z_total_out) {
298 /* Don't copy past the end of the file. */
299 copy_len = s->z_ulen - s->z_total_out;
300 error = Z_STREAM_END;
301 }
302
303 memcpy(s->z_stream.next_out, s->z_stream.next_in, copy_len);
304 s->z_stream.next_out += copy_len;
305 s->z_stream.avail_out -= copy_len;
306 s->z_stream.next_in += copy_len;
307 s->z_stream.avail_in -= copy_len;
308 s->z_total_in += copy_len;
309 s->z_total_out += copy_len;
310
311 return error;
312 }
313
314 int
zip_read(void * cookie,char * buf,int len)315 zip_read(void *cookie, char *buf, int len)
316 {
317 struct zip_state *s = cookie;
318 Bytef *ubuf = buf;
319 int error = Z_OK;
320
321 s->z_stream.next_out = ubuf;
322 s->z_stream.avail_out = len;
323
324 while (error == Z_OK && !s->z_eof && s->z_stream.avail_out != 0) {
325 if (s->z_stream.avail_in == 0) {
326 ssize_t nread = read(s->z_fd, s->z_buf, Z_BUFSIZE);
327 switch (nread) {
328 case -1:
329 goto bad;
330 case 0:
331 s->z_eof = 1;
332 continue;
333 default:
334 s->z_stream.avail_in = nread;
335 s->z_stream.next_in = s->z_buf;
336 }
337 }
338
339 if (s->z_method == Z_DEFLATED) {
340 /*
341 * Prevent overflow of z_stream.total_{in,out}
342 * which may be 32-bit.
343 */
344 uLong prev_total_in = s->z_stream.total_in;
345 uLong prev_total_out = s->z_stream.total_out;
346 error = inflate(&s->z_stream, Z_NO_FLUSH);
347 s->z_total_in += s->z_stream.total_in - prev_total_in;
348 s->z_total_out += s->z_stream.total_out - prev_total_out;
349 } else {
350 /* File stored uncompressed. */
351 error = zip_store(s);
352 }
353 }
354
355 switch (error) {
356 case Z_OK:
357 s->z_crc = crc32(s->z_crc, ubuf,
358 (uInt)(s->z_stream.next_out - ubuf));
359 break;
360 case Z_STREAM_END:
361 s->z_eof = 1;
362
363 /*
364 * Check CRC and original size.
365 * These may be found in the local header or, if
366 * EXTFLG is set, immediately following the file.
367 */
368 s->z_crc = crc32(s->z_crc, ubuf,
369 (uInt)(s->z_stream.next_out - ubuf));
370
371 if (s->z_flags & EXTFLG) {
372 /*
373 * Read data descriptor:
374 * signature 0x08074b50: 4 bytes
375 * CRC-32: 4 bytes
376 * compressed size: 4 or 8 bytes
377 * uncompressed size: 4 or 8 bytes
378 */
379 get_uint32(s);
380 s->z_ocrc = get_uint32(s);
381 if (s->z_zip64) {
382 get_uint64(s);
383 s->z_ulen = get_uint64(s);
384 s->z_hlen += 8;
385 } else {
386 get_uint32(s);
387 s->z_ulen = get_uint32(s);
388 }
389 s->z_hlen += EXTHDR;
390 }
391 if (s->z_ulen != s->z_total_out) {
392 errno = EIO;
393 goto bad;
394 }
395 if (s->z_ocrc != s->z_crc) {
396 errno = EINVAL;
397 goto bad;
398 }
399 break;
400 case Z_DATA_ERROR:
401 errno = EINVAL;
402 goto bad;
403 case Z_BUF_ERROR:
404 errno = EIO;
405 goto bad;
406 default:
407 goto bad;
408 }
409
410 return len - s->z_stream.avail_out;
411 bad:
412 return -1;
413 }
414
415 int
zip_close(void * cookie,struct z_info * info,const char * name,struct stat * sb)416 zip_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
417 {
418 struct zip_state *s = cookie;
419 int error = 0;
420
421 if (s == NULL) {
422 errno = EINVAL;
423 return -1;
424 }
425
426 if (info != NULL) {
427 info->mtime = s->z_time;
428 info->crc = s->z_crc;
429 info->hlen = s->z_hlen;
430 info->total_in = s->z_total_in;
431 info->total_out = s->z_total_out;
432 }
433
434 if (s->z_stream.state != NULL) {
435 /* inflateEnd() overwrites errno. */
436 (void)inflateEnd(&s->z_stream);
437 }
438
439 /*
440 * Check for the presence of additional files in the .zip.
441 * Do not remove the original if we cannot extract all the files.
442 */
443 s->z_eof = 0;
444 if (get_header(s, NULL, 0) == 0) {
445 errno = EEXIST;
446 error = -1;
447 }
448
449 (void)close(s->z_fd);
450
451 free(s);
452
453 return error;
454 }
455