AmendHub

Download

jcs

/

subtext

/

zip.c

 

(View History)

jcs   zip: Make zip_read_file return an error code depending on failure Latest amendment: 455 on 2023-03-27

1 /*
2 * Basic PKZIP parser
3 * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
4 * https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
5 *
6 * Copyright (c) 2023 joshua stein <jcs@jcs.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "logger.h"
27
28 #include "puff.h"
29 #include "util.h"
30 #include "zip.h"
31
32 static unsigned char zip_file_magic[] = { 0x50, 0x4b, 0x03, 0x04 };
33 static unsigned char zip_dir_magic[] = { 0x50, 0x4b, 0x01, 0x02 };
34
35 size_t zip_read(short frefnum, void *buf, size_t len);
36
37 size_t
38 zip_read(short frefnum, void *buf, size_t len)
39 {
40 short error;
41 long rlen = len;
42
43 error = FSRead(frefnum, &rlen, buf);
44 if (error) {
45 logger_printf("[zip] error reading zip: %d", error);
46 if (error != eofErr)
47 warn("error reading zip: %d", error);
48 return 0;
49 }
50 if (rlen != len) {
51 logger_printf("[zip] short read: got %ld wanted %ld", rlen, len);
52 return 0;
53 }
54 return rlen;
55 }
56
57 bool
58 zip_is_zip_file(Str255 path)
59 {
60 char buf[32];
61 short error, frefnum;
62 bool ret = false;
63
64 error = FSOpen(path, 0, &frefnum);
65 if (error) {
66 warn("failed opening %s: %d", PtoCstr(path), error);
67 CtoPstr(path);
68 return false;
69 }
70
71 if (zip_read(frefnum, &buf, 4) == 4 &&
72 memcmp(buf, &zip_file_magic, 4) == 0)
73 ret = true;
74
75 read_done:
76 FSClose(frefnum);
77 return ret;
78
79 }
80
81 short
82 zip_read_file(Str255 path, zip_extract_decider *decider,
83 zip_extract_processor *processor)
84 {
85 char buf[32];
86 char filename[256];
87 unsigned char *comp, *uncomp;
88 unsigned long comp_len, uncomp_len;
89 u_int16_t t, fn_len, ex_len;
90 short ret, fret, error, frefnum;
91
92 error = FSOpen(path, 0, &frefnum);
93 if (error) {
94 warn("failed opening %s: %d", PtoCstr(path), error);
95 CtoPstr(path);
96 return ZIP_FAILED_OPEN;
97 }
98
99 for (;;) {
100 fret = 0;
101
102 if (zip_read(frefnum, &buf, 4) != 4) {
103 fret = ZIP_SHORT_READ;
104 goto read_fail;
105 }
106 if (memcmp(buf, &zip_dir_magic, 4) == 0)
107 break;
108 if (memcmp(buf, &zip_file_magic, 4) != 0) {
109 fret = ZIP_BAD_FILE;
110 goto read_fail;
111 }
112
113 /* version */
114 if (zip_read(frefnum, &buf, 2) != 2) {
115 fret = ZIP_UNSUPPORTED;
116 goto read_fail;
117 }
118
119 /* flags */
120 if (zip_read(frefnum, &buf, 2) != 2) {
121 fret = ZIP_SHORT_READ;
122 goto read_fail;
123 }
124 t = GET_U16(&buf);
125 if (t & (1 << 0)) {
126 logger_printf("[zip] Encryption not supported");
127 fret = ZIP_UNSUPPORTED;
128 goto read_fail;
129 }
130
131 /* compression */
132 if (zip_read(frefnum, &buf, 2) != 2) {
133 fret = ZIP_SHORT_READ;
134 goto read_fail;
135 }
136 t = GET_U16(&buf);
137 if (t != 8) {
138 logger_printf("[zip] Compression method %d not supported", t);
139 fret = ZIP_UNSUPPORTED;
140 goto read_fail;
141 }
142
143 /* mod time/date */
144 if (zip_read(frefnum, &buf, 4) != 4) {
145 fret = ZIP_SHORT_READ;
146 goto read_fail;
147 }
148
149 /* crc32 */
150 if (zip_read(frefnum, &buf, 4) != 4) {
151 fret = ZIP_SHORT_READ;
152 goto read_fail;
153 }
154
155 /* compressed size */
156 if (zip_read(frefnum, &buf, 4) != 4) {
157 fret = ZIP_SHORT_READ;
158 goto read_fail;
159 }
160 comp_len = GET_U32(&buf);
161 if (comp_len == 0xffffffff) {
162 logger_printf("[zip] ZIP64 not supported");
163 fret = ZIP_UNSUPPORTED;
164 goto read_fail;
165 }
166 if ((signed long)comp_len < 0) {
167 logger_printf("[zip] Bogus compressed length (%lu)", comp_len);
168 fret = ZIP_BAD_FILE;
169 goto read_fail;
170 }
171 if (comp_len == 0) {
172 logger_printf("[zip] Data descriptor not supported");
173 fret = ZIP_UNSUPPORTED;
174 goto read_fail;
175 }
176
177 /* uncompressed size */
178 if (zip_read(frefnum, &buf, 4) != 4) {
179 fret = ZIP_SHORT_READ;
180 goto read_fail;
181 }
182 uncomp_len = GET_U32(&buf);
183 if (uncomp_len == 0xffffffff) {
184 logger_printf("[zip] ZIP64 not supported");
185 fret = ZIP_UNSUPPORTED;
186 goto read_fail;
187 }
188 if ((signed long)uncomp_len < 0) {
189 logger_printf("[zip] Bogus uncompressed length (%lu)",
190 uncomp_len);
191 fret = ZIP_BAD_FILE;
192 goto read_fail;
193 }
194 if (uncomp_len == 0) {
195 logger_printf("[zip] Data descriptor not supported");
196 fret = ZIP_UNSUPPORTED;
197 goto read_fail;
198 }
199
200 /* file name len */
201 if (zip_read(frefnum, &buf, 2) != 2) {
202 fret = ZIP_SHORT_READ;
203 goto read_fail;
204 }
205 fn_len = GET_U16(&buf);
206 if (fn_len >= sizeof(buf)) {
207 logger_printf("[zip] Filename len %d too big", fn_len);
208 fret = ZIP_UNSUPPORTED;
209 goto read_fail;
210 }
211
212 /* extra field len */
213 if (zip_read(frefnum, &buf, 2) != 2) {
214 fret = ZIP_SHORT_READ;
215 goto read_fail;
216 }
217 ex_len = GET_U16(&buf);
218
219 /* filename */
220 if (fn_len > sizeof(filename)) {
221 logger_printf("[zip] Filename too long (%ld)", fn_len);
222 fret = ZIP_UNSUPPORTED;
223 goto read_fail;
224 }
225
226 if (zip_read(frefnum, &filename, fn_len) != fn_len) {
227 fret = ZIP_SHORT_READ;
228 goto read_fail;
229 }
230 filename[fn_len] = '\0';
231
232 /* extra field */
233 if (zip_read(frefnum, &buf, ex_len) != ex_len) {
234 fret = ZIP_SHORT_READ;
235 goto read_fail;
236 }
237
238 if (decider(filename, uncomp_len)) {
239 comp = xmalloc(comp_len);
240 if (comp == NULL) {
241 logger_printf("[zip] Failed to malloc(%ld) for "
242 "compressed data", comp_len);
243 fret = ZIP_NO_MEMORY;
244 goto read_fail;
245 }
246
247 /* read compressed data */
248 if (zip_read(frefnum, comp, comp_len) != comp_len) {
249 xfree(&comp);
250 fret = ZIP_SHORT_READ;
251 goto read_fail;
252 }
253
254 uncomp = xmalloc(uncomp_len);
255 if (uncomp == NULL) {
256 logger_printf("[zip] Failed to malloc(%ld) for "
257 "uncompressed data", uncomp_len);
258 fret = ZIP_NO_MEMORY;
259 xfree(&comp);
260 goto read_fail;
261 }
262
263 ret = puff(uncomp, &uncomp_len, comp, &comp_len);
264 if (ret != 0) {
265 logger_printf("[zip] Unzip failed: %d", ret);
266 xfree(&comp);
267 xfree(&uncomp);
268 fret = ZIP_BAD_FILE;
269 goto read_fail;
270 }
271 xfree(&comp);
272
273 processor(filename, uncomp, uncomp_len);
274 xfree(&uncomp);
275 } else {
276 /* skip over it */
277 SetFPos(frefnum, fsFromMark, comp_len);
278 }
279 }
280
281 FSClose(frefnum);
282 return ZIP_OK;
283
284 read_fail:
285 FSClose(frefnum);
286 if (fret == 0)
287 fret = ZIP_BAD_FILE;
288 return fret;
289
290 }