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 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 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 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 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 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 * 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 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 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 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