/src/clamav/libclamav/unarj.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Extract component parts of ARJ archives. |
3 | | * |
4 | | * Copyright (C) 2013-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
5 | | * Copyright (C) 2007-2013 Sourcefire, Inc. |
6 | | * |
7 | | * Authors: Trog |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify |
10 | | * it under the terms of the GNU General Public License version 2 as |
11 | | * published by the Free Software Foundation. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
21 | | * MA 02110-1301, USA. |
22 | | */ |
23 | | |
24 | | #if HAVE_CONFIG_H |
25 | | #include "clamav-config.h" |
26 | | #endif |
27 | | |
28 | | #include <stdio.h> |
29 | | #include <string.h> |
30 | | #include <stdlib.h> |
31 | | #ifdef HAVE_UNISTD_H |
32 | | #include <unistd.h> |
33 | | #endif |
34 | | #include <sys/types.h> |
35 | | #include <sys/stat.h> |
36 | | #include <fcntl.h> |
37 | | #include <ctype.h> |
38 | | |
39 | | #include "clamav.h" |
40 | | #include "str.h" |
41 | | #include "others.h" |
42 | | #include "unarj.h" |
43 | | #include "textnorm.h" |
44 | | |
45 | 3.82M | #define FIRST_HDR_SIZE 30 |
46 | 3.82M | #define COMMENT_MAX 2048 |
47 | 3.82M | #define FNAME_MAX 512 |
48 | 3.82M | #define HEADERSIZE_MAX (FIRST_HDR_SIZE + 10 + FNAME_MAX + COMMENT_MAX) |
49 | 15.0M | #define MAXDICBIT 16 |
50 | 4.30G | #define DDICSIZ 26624 |
51 | 1.01G | #define THRESHOLD 3 |
52 | | #ifndef UCHAR_MAX |
53 | | #define UCHAR_MAX (255) |
54 | | #endif |
55 | | #ifndef CHAR_BIT |
56 | | #define CHAR_BIT (8) |
57 | | #endif |
58 | 991M | #define MAXMATCH 256 |
59 | | #ifndef FALSE |
60 | | #define FALSE (0) |
61 | | #define TRUE (1) |
62 | | #endif |
63 | | |
64 | 1.18G | #define CODE_BIT 16 |
65 | 2.05M | #define NT (CODE_BIT + 3) |
66 | 57.5k | #define PBIT 5 |
67 | 57.5k | #define TBIT 5 |
68 | 977M | #define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) |
69 | 15.0M | #define NP (MAXDICBIT + 1) |
70 | 109k | #define CBIT 9 |
71 | 88.5M | #define CTABLESIZE 4096 |
72 | 42.2k | #define PTABLESIZE 256 |
73 | 50.3M | #define STRTP 9 |
74 | 55.6M | #define STOPP 13 |
75 | | |
76 | 760M | #define STRTL 0 |
77 | 443M | #define STOPL 7 |
78 | | |
79 | | #if NT > NP |
80 | 890k | #define NPT NT |
81 | | #else |
82 | | #define NPT NP |
83 | | #endif |
84 | | |
85 | 1.18M | #define GARBLE_FLAG 0x01 |
86 | | |
87 | | #ifndef HAVE_ATTRIB_PACKED |
88 | | #define __attribute__(x) |
89 | | #endif |
90 | | |
91 | | #ifdef HAVE_PRAGMA_PACK |
92 | | #pragma pack(1) |
93 | | #endif |
94 | | |
95 | | #ifdef HAVE_PRAGMA_PACK_HPPA |
96 | | #pragma pack 1 |
97 | | #endif |
98 | | |
99 | | typedef struct arj_main_hdr_tag { |
100 | | uint8_t first_hdr_size; /* must be 30 bytes */ |
101 | | uint8_t version; |
102 | | uint8_t min_version; |
103 | | uint8_t host_os; |
104 | | uint8_t flags; |
105 | | uint8_t security_version; |
106 | | uint8_t file_type; |
107 | | uint8_t pad; |
108 | | uint32_t time_created __attribute__((packed)); |
109 | | uint32_t time_modified __attribute__((packed)); |
110 | | uint32_t archive_size __attribute__((packed)); |
111 | | uint32_t sec_env_file_position __attribute__((packed)); |
112 | | uint16_t entryname_pos __attribute__((packed)); |
113 | | uint16_t sec_trail_size __attribute__((packed)); |
114 | | uint16_t host_data __attribute__((packed)); |
115 | | } arj_main_hdr_t; |
116 | | |
117 | | typedef struct arj_file_hdr_tag { |
118 | | uint8_t first_hdr_size; /* must be 30 bytes */ |
119 | | uint8_t version; |
120 | | uint8_t min_version; |
121 | | uint8_t host_os; |
122 | | uint8_t flags; |
123 | | uint8_t method; |
124 | | uint8_t file_type; |
125 | | uint8_t password_mod; |
126 | | uint32_t time_modified __attribute__((packed)); |
127 | | uint32_t comp_size __attribute__((packed)); |
128 | | uint32_t orig_size __attribute__((packed)); |
129 | | uint32_t orig_crc __attribute__((packed)); |
130 | | uint16_t entryname_pos __attribute__((packed)); |
131 | | uint16_t file_mode __attribute__((packed)); |
132 | | uint16_t host_data __attribute__((packed)); |
133 | | } arj_file_hdr_t; |
134 | | |
135 | | #ifdef HAVE_PRAGMA_PACK |
136 | | #pragma pack() |
137 | | #endif |
138 | | |
139 | | #ifdef HAVE_PRAGMA_PACK_HPPA |
140 | | #pragma pack |
141 | | #endif |
142 | | |
143 | | typedef struct arj_decode_tag { |
144 | | unsigned char *text; |
145 | | fmap_t *map; |
146 | | size_t offset; |
147 | | const uint8_t *buf; |
148 | | const void *bufend; |
149 | | uint16_t blocksize; |
150 | | uint16_t bit_buf; |
151 | | int bit_count; |
152 | | uint32_t comp_size; |
153 | | int16_t getlen, getbuf; |
154 | | uint16_t left[2 * NC - 1]; |
155 | | uint16_t right[2 * NC - 1]; |
156 | | unsigned char c_len[NC]; |
157 | | uint16_t c_table[CTABLESIZE]; |
158 | | unsigned char pt_len[NPT]; |
159 | | unsigned char sub_bit_buf; |
160 | | uint16_t pt_table[PTABLESIZE]; |
161 | | int status; |
162 | | } arj_decode_t; |
163 | | |
164 | | static cl_error_t fill_buf(arj_decode_t *decode_data, int n) |
165 | 1.36G | { |
166 | 1.36G | if (decode_data->status == CL_EFORMAT) |
167 | 8.91k | return CL_EFORMAT; |
168 | 1.36G | if (((uint64_t)decode_data->bit_buf) * (n > 0 ? 2 << (n - 1) : 0) > UINT32_MAX) |
169 | 11.5k | return CL_EFORMAT; |
170 | 1.36G | decode_data->bit_buf = (((uint64_t)decode_data->bit_buf) << n) & 0xFFFF; |
171 | 1.82G | while (n > decode_data->bit_count) { |
172 | 460M | decode_data->bit_buf |= decode_data->sub_bit_buf << (n -= decode_data->bit_count); |
173 | 460M | if (decode_data->comp_size != 0) { |
174 | 139M | decode_data->comp_size--; |
175 | 139M | if (decode_data->buf == decode_data->bufend) { |
176 | 101k | size_t len; |
177 | 101k | decode_data->buf = fmap_need_off_once_len(decode_data->map, decode_data->offset, 8192, &len); |
178 | 101k | if (!decode_data->buf || !len) { |
179 | | /* the file is most likely corrupted, so |
180 | | * we return CL_EFORMAT instead of CL_EREAD |
181 | | */ |
182 | 26.1k | decode_data->status = CL_EFORMAT; |
183 | 26.1k | return CL_EFORMAT; |
184 | 26.1k | } |
185 | 75.4k | decode_data->bufend = decode_data->buf + len; |
186 | 75.4k | } |
187 | 139M | decode_data->sub_bit_buf = *decode_data->buf++; |
188 | 139M | decode_data->offset++; |
189 | 321M | } else { |
190 | 321M | decode_data->sub_bit_buf = 0; |
191 | 321M | } |
192 | 460M | decode_data->bit_count = CHAR_BIT; |
193 | 460M | } |
194 | 1.36G | decode_data->bit_buf |= decode_data->sub_bit_buf >> (decode_data->bit_count -= n); |
195 | 1.36G | return CL_SUCCESS; |
196 | 1.36G | } |
197 | | |
198 | | static cl_error_t init_getbits(arj_decode_t *decode_data) |
199 | 66.5k | { |
200 | 66.5k | decode_data->bit_buf = 0; |
201 | 66.5k | decode_data->sub_bit_buf = 0; |
202 | 66.5k | decode_data->bit_count = 0; |
203 | 66.5k | return fill_buf(decode_data, 2 * CHAR_BIT); |
204 | 66.5k | } |
205 | | |
206 | | static unsigned short arj_getbits(arj_decode_t *decode_data, int n) |
207 | 3.78M | { |
208 | 3.78M | unsigned short x; |
209 | | |
210 | 3.78M | x = decode_data->bit_buf >> (2 * CHAR_BIT - n); |
211 | 3.78M | fill_buf(decode_data, n); |
212 | 3.78M | return x; |
213 | 3.78M | } |
214 | | |
215 | | static cl_error_t decode_start(arj_decode_t *decode_data) |
216 | 37.8k | { |
217 | 37.8k | decode_data->blocksize = 0; |
218 | 37.8k | return init_getbits(decode_data); |
219 | 37.8k | } |
220 | | |
221 | | static cl_error_t write_text(int ofd, unsigned char *data, size_t length) |
222 | 98.9k | { |
223 | 98.9k | size_t count; |
224 | | |
225 | 98.9k | count = cli_writen(ofd, data, length); |
226 | 98.9k | if (count != length) { |
227 | 0 | return CL_EWRITE; |
228 | 0 | } |
229 | 98.9k | return CL_SUCCESS; |
230 | 98.9k | } |
231 | | |
232 | | static cl_error_t make_table(arj_decode_t *decode_data, int nchar, unsigned char *bitlen, int tablebits, |
233 | | unsigned short *table, int tablesize) |
234 | 58.5k | { |
235 | 58.5k | unsigned short count[17], weight[17], start[18], *p; |
236 | 58.5k | unsigned int i, k, len, ch, jutbits, avail, nextcode, mask; |
237 | | |
238 | 995k | for (i = 1; i <= 16; i++) { |
239 | 937k | count[i] = 0; |
240 | 937k | } |
241 | 9.14M | for (i = 0; (int)i < nchar; i++) { |
242 | 9.09M | if (bitlen[i] >= 17) { |
243 | 1.18k | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
244 | 1.18k | decode_data->status = CL_EUNPACK; |
245 | 1.18k | return CL_EUNPACK; |
246 | 1.18k | } |
247 | 9.09M | count[bitlen[i]]++; |
248 | 9.09M | } |
249 | | |
250 | 57.3k | start[1] = 0; |
251 | 975k | for (i = 1; i <= 16; i++) { |
252 | 918k | start[i + 1] = start[i] + (count[i] << (16 - i)); |
253 | 918k | } |
254 | 57.3k | if (start[17] != (unsigned short)(1 << 16)) { |
255 | 23.0k | decode_data->status = CL_EUNPACK; |
256 | 23.0k | return CL_EUNPACK; |
257 | 23.0k | } |
258 | | |
259 | 34.3k | jutbits = 16 - tablebits; |
260 | 34.3k | if (tablebits >= 17) { |
261 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
262 | 0 | decode_data->status = CL_EUNPACK; |
263 | 0 | return CL_EUNPACK; |
264 | 0 | } |
265 | 367k | for (i = 1; (int)i <= tablebits; i++) { |
266 | 333k | start[i] >>= jutbits; |
267 | 333k | weight[i] = 1 << (tablebits - i); |
268 | 333k | } |
269 | 250k | while (i <= 16) { |
270 | 216k | weight[i] = 1 << (16 - i); |
271 | 216k | i++; |
272 | 216k | } |
273 | | |
274 | 34.3k | i = start[tablebits + 1] >> jutbits; |
275 | 34.3k | if (i != (unsigned short)(1 << 16)) { |
276 | 0 | k = 1 << tablebits; |
277 | 0 | while (i != k) { |
278 | 0 | if (i >= (unsigned int)tablesize) { |
279 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
280 | 0 | decode_data->status = CL_EUNPACK; |
281 | 0 | return CL_EUNPACK; |
282 | 0 | } |
283 | 0 | table[i++] = 0; |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | 34.3k | avail = nchar; |
288 | 34.3k | mask = 1 << (15 - tablebits); |
289 | 7.75M | for (ch = 0; (int)ch < nchar; ch++) { |
290 | 7.72M | if ((len = bitlen[ch]) == 0) { |
291 | 7.37M | continue; |
292 | 7.37M | } |
293 | 345k | if (len >= 17) { |
294 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
295 | 0 | decode_data->status = CL_EUNPACK; |
296 | 0 | return CL_EUNPACK; |
297 | 0 | } |
298 | 345k | k = start[len]; |
299 | 345k | nextcode = k + weight[len]; |
300 | 345k | if ((int)len <= tablebits) { |
301 | 345k | if (nextcode > (unsigned int)tablesize) { |
302 | 2.15k | decode_data->status = CL_EUNPACK; |
303 | 2.15k | return CL_EUNPACK; |
304 | 2.15k | } |
305 | 29.3M | for (i = start[len]; i < nextcode; i++) { |
306 | 28.9M | table[i] = ch; |
307 | 28.9M | } |
308 | 343k | } else { |
309 | 0 | p = &table[k >> jutbits]; |
310 | 0 | i = len - tablebits; |
311 | 0 | while (i != 0) { |
312 | 0 | if (*p == 0) { |
313 | 0 | if (avail >= (2 * NC - 1)) { |
314 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
315 | 0 | decode_data->status = CL_EUNPACK; |
316 | 0 | return CL_EUNPACK; |
317 | 0 | } |
318 | 0 | decode_data->right[avail] = decode_data->left[avail] = 0; |
319 | 0 | *p = avail++; |
320 | 0 | } |
321 | 0 | if (*p >= (2 * NC - 1)) { |
322 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
323 | 0 | decode_data->status = CL_EUNPACK; |
324 | 0 | return CL_EUNPACK; |
325 | 0 | } |
326 | 0 | if (k & mask) { |
327 | 0 | p = &decode_data->right[*p]; |
328 | 0 | } else { |
329 | 0 | p = &decode_data->left[*p]; |
330 | 0 | } |
331 | 0 | k <<= 1; |
332 | 0 | i--; |
333 | 0 | } |
334 | 0 | *p = ch; |
335 | 0 | } |
336 | 343k | start[len] = nextcode; |
337 | 343k | } |
338 | 32.1k | return CL_SUCCESS; |
339 | 34.3k | } |
340 | | |
341 | | static cl_error_t read_pt_len(arj_decode_t *decode_data, int nn, int nbit, int i_special) |
342 | 115k | { |
343 | 115k | int i, n; |
344 | 115k | short c; |
345 | 115k | unsigned short mask; |
346 | | |
347 | 115k | n = arj_getbits(decode_data, nbit); |
348 | 115k | if (n == 0) { |
349 | 61.3k | if (nn > NPT) { |
350 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
351 | 0 | decode_data->status = CL_EUNPACK; |
352 | 0 | return CL_EUNPACK; |
353 | 0 | } |
354 | 61.3k | c = arj_getbits(decode_data, nbit); |
355 | 1.22M | for (i = 0; i < nn; i++) { |
356 | 1.16M | decode_data->pt_len[i] = 0; |
357 | 1.16M | } |
358 | 15.7M | for (i = 0; i < 256; i++) { |
359 | 15.7M | decode_data->pt_table[i] = c; |
360 | 15.7M | } |
361 | 61.3k | } else { |
362 | 53.7k | i = 0; |
363 | 545k | while ((i < n) && (i < NPT)) { |
364 | 503k | c = decode_data->bit_buf >> 13; |
365 | 503k | if (c == 7) { |
366 | 29.8k | mask = 1 << 12; |
367 | 204k | while (mask & decode_data->bit_buf) { |
368 | 175k | mask >>= 1; |
369 | 175k | c++; |
370 | 175k | } |
371 | 29.8k | } |
372 | 503k | fill_buf(decode_data, (c < 7) ? 3 : (int)(c - 3)); |
373 | 503k | if (decode_data->status != CL_SUCCESS) { |
374 | 11.4k | return decode_data->status; |
375 | 11.4k | } |
376 | 491k | decode_data->pt_len[i++] = (unsigned char)c; |
377 | 491k | if (i == i_special) { |
378 | 22.8k | c = arj_getbits(decode_data, 2); |
379 | 22.8k | if (decode_data->status != CL_SUCCESS) { |
380 | 0 | return decode_data->status; |
381 | 0 | } |
382 | 53.6k | while ((--c >= 0) && (i < NPT)) { |
383 | 30.7k | decode_data->pt_len[i++] = 0; |
384 | 30.7k | } |
385 | 22.8k | } |
386 | 491k | } |
387 | 323k | while ((i < nn) && (i < NPT)) { |
388 | 281k | decode_data->pt_len[i++] = 0; |
389 | 281k | } |
390 | 42.2k | if (make_table(decode_data, nn, decode_data->pt_len, 8, decode_data->pt_table, PTABLESIZE) != CL_SUCCESS) { |
391 | 24.5k | return CL_EUNPACK; |
392 | 24.5k | } |
393 | 42.2k | } |
394 | 79.0k | return CL_SUCCESS; |
395 | 115k | } |
396 | | |
397 | | static cl_error_t read_c_len(arj_decode_t *decode_data) |
398 | 57.5k | { |
399 | 57.5k | short i, c, n; |
400 | 57.5k | unsigned short mask; |
401 | | |
402 | 57.5k | n = arj_getbits(decode_data, CBIT); |
403 | 57.5k | if (decode_data->status != CL_SUCCESS) { |
404 | 17.9k | return decode_data->status; |
405 | 17.9k | } |
406 | 39.5k | if (n == 0) { |
407 | 21.6k | c = arj_getbits(decode_data, CBIT); |
408 | 21.6k | if (decode_data->status != CL_SUCCESS) { |
409 | 31 | return decode_data->status; |
410 | 31 | } |
411 | 11.0M | for (i = 0; i < NC; i++) { |
412 | 11.0M | decode_data->c_len[i] = 0; |
413 | 11.0M | } |
414 | 88.5M | for (i = 0; i < CTABLESIZE; i++) { |
415 | 88.4M | decode_data->c_table[i] = c; |
416 | 88.4M | } |
417 | 21.6k | } else { |
418 | 17.9k | i = 0; |
419 | 1.01M | while (i < n) { |
420 | 993k | c = decode_data->pt_table[decode_data->bit_buf >> 8]; |
421 | 993k | if (c >= NT) { |
422 | 56.4k | mask = 1 << 7; |
423 | 56.4k | do { |
424 | 56.4k | if (c >= (2 * NC - 1)) { |
425 | 0 | cli_dbgmsg("ERROR: bounds exceeded\n"); |
426 | 0 | decode_data->status = CL_EFORMAT; |
427 | 0 | return CL_EFORMAT; |
428 | 0 | } |
429 | 56.4k | if (decode_data->bit_buf & mask) { |
430 | 7.72k | c = decode_data->right[c]; |
431 | 48.7k | } else { |
432 | 48.7k | c = decode_data->left[c]; |
433 | 48.7k | } |
434 | 56.4k | mask >>= 1; |
435 | 56.4k | } while (c >= NT); |
436 | 56.4k | } |
437 | 993k | if (c >= 19) { |
438 | 0 | cli_dbgmsg("UNARJ: bounds exceeded\n"); |
439 | 0 | decode_data->status = CL_EUNPACK; |
440 | 0 | return CL_EUNPACK; |
441 | 0 | } |
442 | 993k | fill_buf(decode_data, (int)(decode_data->pt_len[c])); |
443 | 993k | if (decode_data->status != CL_SUCCESS) { |
444 | 330 | return decode_data->status; |
445 | 330 | } |
446 | 993k | if (c <= 2) { |
447 | 417k | if (c == 0) { |
448 | 295k | c = 1; |
449 | 295k | } else if (c == 1) { |
450 | 91.4k | c = arj_getbits(decode_data, 4) + 3; |
451 | 91.4k | } else { |
452 | 30.6k | c = arj_getbits(decode_data, CBIT) + 20; |
453 | 30.6k | } |
454 | 417k | if (decode_data->status != CL_SUCCESS) { |
455 | 348 | return decode_data->status; |
456 | 348 | } |
457 | 2.80M | while (--c >= 0) { |
458 | 2.39M | if (i >= NC) { |
459 | 794 | cli_dbgmsg("ERROR: bounds exceeded\n"); |
460 | 794 | decode_data->status = CL_EFORMAT; |
461 | 794 | return CL_EFORMAT; |
462 | 794 | } |
463 | 2.38M | decode_data->c_len[i++] = 0; |
464 | 2.38M | } |
465 | 575k | } else { |
466 | 575k | if (i >= NC) { |
467 | 178 | cli_dbgmsg("ERROR: bounds exceeded\n"); |
468 | 178 | decode_data->status = CL_EFORMAT; |
469 | 178 | return CL_EFORMAT; |
470 | 178 | } |
471 | 575k | decode_data->c_len[i++] = (unsigned char)(c - 2); |
472 | 575k | } |
473 | 993k | } |
474 | 6.06M | while (i < NC) { |
475 | 6.04M | decode_data->c_len[i++] = 0; |
476 | 6.04M | } |
477 | 16.2k | if (make_table(decode_data, NC, decode_data->c_len, 12, decode_data->c_table, CTABLESIZE) != CL_SUCCESS) { |
478 | 1.84k | return CL_EUNPACK; |
479 | 1.84k | } |
480 | 16.2k | } |
481 | 36.0k | return CL_SUCCESS; |
482 | 39.5k | } |
483 | | |
484 | | static uint16_t decode_c(arj_decode_t *decode_data) |
485 | 955M | { |
486 | 955M | uint16_t j, mask; |
487 | | |
488 | 955M | if (decode_data->blocksize == 0) { |
489 | 57.5k | decode_data->blocksize = arj_getbits(decode_data, 16); |
490 | 57.5k | read_pt_len(decode_data, NT, TBIT, 3); |
491 | 57.5k | read_c_len(decode_data); |
492 | 57.5k | read_pt_len(decode_data, NT, PBIT, -1); |
493 | 57.5k | } |
494 | 955M | decode_data->blocksize--; |
495 | 955M | j = decode_data->c_table[decode_data->bit_buf >> 4]; |
496 | 955M | if (j >= NC) { |
497 | 420k | mask = 1 << 3; |
498 | 420k | do { |
499 | 420k | if (j >= (2 * NC - 1)) { |
500 | 0 | cli_dbgmsg("ERROR: bounds exceeded\n"); |
501 | 0 | decode_data->status = CL_EUNPACK; |
502 | 0 | return 0; |
503 | 0 | } |
504 | 420k | if (decode_data->bit_buf & mask) { |
505 | 26.6k | j = decode_data->right[j]; |
506 | 393k | } else { |
507 | 393k | j = decode_data->left[j]; |
508 | 393k | } |
509 | 420k | mask >>= 1; |
510 | 420k | } while (j >= NC); |
511 | 420k | } |
512 | 955M | fill_buf(decode_data, (int)(decode_data->c_len[j])); |
513 | 955M | return j; |
514 | 955M | } |
515 | | |
516 | | static uint16_t decode_p(arj_decode_t *decode_data) |
517 | 14.0M | { |
518 | 14.0M | unsigned short j, mask; |
519 | | |
520 | 14.0M | j = decode_data->pt_table[decode_data->bit_buf >> 8]; |
521 | 14.0M | if (j >= NP) { |
522 | 1.03M | mask = 1 << 7; |
523 | 1.03M | do { |
524 | 1.03M | if (j >= (2 * NC - 1)) { |
525 | 0 | cli_dbgmsg("ERROR: bounds exceeded\n"); |
526 | 0 | decode_data->status = CL_EUNPACK; |
527 | 0 | return 0; |
528 | 0 | } |
529 | 1.03M | if (decode_data->bit_buf & mask) { |
530 | 1.02M | j = decode_data->right[j]; |
531 | 1.02M | } else { |
532 | 8.37k | j = decode_data->left[j]; |
533 | 8.37k | } |
534 | 1.03M | mask >>= 1; |
535 | 1.03M | } while (j >= NP); |
536 | 1.03M | } |
537 | 14.0M | fill_buf(decode_data, (int)(decode_data->pt_len[j])); |
538 | 14.0M | if (j != 0) { |
539 | 3.33M | j--; |
540 | 3.33M | j = (1 << j) + arj_getbits(decode_data, (int)j); |
541 | 3.33M | } |
542 | 14.0M | return j; |
543 | 14.0M | } |
544 | | |
545 | | static cl_error_t decode(arj_metadata_t *metadata) |
546 | 37.8k | { |
547 | 37.8k | cl_error_t ret; |
548 | | |
549 | 37.8k | arj_decode_t decode_data; |
550 | 37.8k | uint32_t count = 0, out_ptr = 0; |
551 | 37.8k | int16_t chr, i, j; |
552 | | |
553 | 37.8k | memset(&decode_data, 0, sizeof(decode_data)); |
554 | 37.8k | decode_data.text = (unsigned char *)cli_calloc(DDICSIZ, 1); |
555 | 37.8k | if (!decode_data.text) { |
556 | 0 | return CL_EMEM; |
557 | 0 | } |
558 | 37.8k | decode_data.map = metadata->map; |
559 | 37.8k | decode_data.offset = metadata->offset; |
560 | 37.8k | decode_data.comp_size = metadata->comp_size; |
561 | 37.8k | ret = decode_start(&decode_data); |
562 | 37.8k | if (ret != CL_SUCCESS) { |
563 | 272 | free(decode_data.text); |
564 | 272 | metadata->offset = decode_data.offset; |
565 | 272 | return ret; |
566 | 272 | } |
567 | 37.5k | decode_data.status = CL_SUCCESS; |
568 | | |
569 | 955M | while (count < metadata->orig_size) { |
570 | 955M | if ((chr = decode_c(&decode_data)) <= UCHAR_MAX) { |
571 | 941M | decode_data.text[out_ptr] = (unsigned char)chr; |
572 | 941M | count++; |
573 | 941M | if (++out_ptr >= DDICSIZ) { |
574 | 33.4k | out_ptr = 0; |
575 | 33.4k | if (write_text(metadata->ofd, decode_data.text, DDICSIZ) != CL_SUCCESS) { |
576 | 0 | free(decode_data.text); |
577 | 0 | metadata->offset = decode_data.offset; |
578 | 0 | return CL_EWRITE; |
579 | 0 | } |
580 | 33.4k | } |
581 | 941M | } else { |
582 | 14.0M | j = chr - (UCHAR_MAX + 1 - THRESHOLD); |
583 | 14.0M | count += j; |
584 | 14.0M | i = decode_p(&decode_data); |
585 | 14.0M | if ((i = out_ptr - i - 1) < 0) { |
586 | 201k | i += DDICSIZ; |
587 | 201k | } |
588 | 14.0M | if ((i >= DDICSIZ) || (i < 0)) { |
589 | 2.71k | cli_dbgmsg("UNARJ: bounds exceeded - probably a corrupted file.\n"); |
590 | 2.71k | break; |
591 | 2.71k | } |
592 | 14.0M | if (out_ptr > (uint32_t)i && out_ptr < DDICSIZ - MAXMATCH - 1) { |
593 | 603M | while ((--j >= 0) && (i < DDICSIZ) && (out_ptr < DDICSIZ)) { |
594 | 590M | decode_data.text[out_ptr++] = decode_data.text[i++]; |
595 | 590M | } |
596 | 13.6M | } else { |
597 | 21.1M | while (--j >= 0) { |
598 | 20.8M | decode_data.text[out_ptr] = decode_data.text[i]; |
599 | 20.8M | if (++out_ptr >= DDICSIZ) { |
600 | 21.1k | out_ptr = 0; |
601 | 21.1k | if (write_text(metadata->ofd, decode_data.text, DDICSIZ) != CL_SUCCESS) { |
602 | 0 | free(decode_data.text); |
603 | 0 | metadata->offset = decode_data.offset; |
604 | 0 | return CL_EWRITE; |
605 | 0 | } |
606 | 21.1k | } |
607 | 20.8M | if (++i >= DDICSIZ) { |
608 | 27.0k | i = 0; |
609 | 27.0k | } |
610 | 20.8M | } |
611 | 327k | } |
612 | 14.0M | } |
613 | 955M | if (decode_data.status != CL_SUCCESS) { |
614 | 29.7k | free(decode_data.text); |
615 | 29.7k | metadata->offset = decode_data.offset; |
616 | 29.7k | return decode_data.status; |
617 | 29.7k | } |
618 | 955M | } |
619 | 7.78k | if (out_ptr != 0) { |
620 | 5.21k | write_text(metadata->ofd, decode_data.text, out_ptr); |
621 | 5.21k | } |
622 | | |
623 | 7.78k | free(decode_data.text); |
624 | 7.78k | metadata->offset = decode_data.offset; |
625 | 7.78k | return CL_SUCCESS; |
626 | 37.5k | } |
627 | | |
628 | | #define ARJ_BFIL(dd) \ |
629 | 386M | { \ |
630 | 386M | dd->getbuf |= dd->bit_buf >> dd->getlen; \ |
631 | 386M | fill_buf(dd, CODE_BIT - dd->getlen); \ |
632 | 386M | dd->getlen = CODE_BIT; \ |
633 | 386M | } |
634 | | #define ARJ_GETBIT(dd, c) \ |
635 | 491M | { \ |
636 | 491M | if (dd->getlen <= 0) ARJ_BFIL(dd) \ |
637 | 491M | c = (dd->getbuf & 0x8000) != 0; \ |
638 | 491M | dd->getbuf *= 2; \ |
639 | 491M | dd->getlen--; \ |
640 | 491M | } |
641 | | #define ARJ_BPUL(dd, l) \ |
642 | 405M | do { \ |
643 | 405M | int i; \ |
644 | 405M | int j = l; \ |
645 | 3.56G | for (i = 0; i < j; i++) { \ |
646 | 3.16G | dd->getbuf *= 2; \ |
647 | 3.16G | } \ |
648 | 405M | dd->getlen -= l; \ |
649 | 405M | } while (0) |
650 | | #define ARJ_GETBITS(dd, c, l) \ |
651 | 405M | { \ |
652 | 405M | if (dd->getlen < l) ARJ_BFIL(dd) \ |
653 | 405M | c = (uint16_t)dd->getbuf >> (CODE_BIT - l); \ |
654 | 405M | ARJ_BPUL(dd, l); \ |
655 | 405M | } |
656 | | |
657 | | static uint16_t decode_ptr(arj_decode_t *decode_data) |
658 | 25.1M | { |
659 | 25.1M | uint16_t c, width, plus, pwr; |
660 | | |
661 | 25.1M | plus = 0; |
662 | 25.1M | pwr = 1 << STRTP; |
663 | 55.6M | for (width = STRTP; width < STOPP; width++) { |
664 | 50.7M | ARJ_GETBIT(decode_data, c); |
665 | 50.7M | if (c == 0) { |
666 | 20.2M | break; |
667 | 20.2M | } |
668 | 30.4M | plus += pwr; |
669 | 30.4M | pwr <<= 1; |
670 | 30.4M | } |
671 | 25.1M | if (width != 0) { |
672 | 25.1M | ARJ_GETBITS(decode_data, c, width); |
673 | 25.1M | } |
674 | 25.1M | c += plus; |
675 | 25.1M | return c; |
676 | 25.1M | } |
677 | | |
678 | | static uint16_t decode_len(arj_decode_t *decode_data) |
679 | 380M | { |
680 | 380M | uint16_t c, width, plus, pwr; |
681 | | |
682 | 380M | plus = 0; |
683 | 380M | pwr = 1 << STRTL; |
684 | 443M | for (width = STRTL; width < STOPL; width++) { |
685 | 440M | ARJ_GETBIT(decode_data, c); |
686 | 440M | if (c == 0) { |
687 | 377M | break; |
688 | 377M | } |
689 | 63.4M | plus += pwr; |
690 | 63.4M | pwr <<= 1; |
691 | 63.4M | } |
692 | 380M | if (width != 0) { |
693 | 25.1M | ARJ_GETBITS(decode_data, c, width); |
694 | 25.1M | } |
695 | 380M | c += plus; |
696 | 380M | return c; |
697 | 380M | } |
698 | | |
699 | | static cl_error_t decode_f(arj_metadata_t *metadata) |
700 | 28.7k | { |
701 | 28.7k | cl_error_t ret; |
702 | | |
703 | 28.7k | arj_decode_t decode_data, *dd; |
704 | 28.7k | uint32_t count = 0, out_ptr = 0; |
705 | 28.7k | int16_t chr, i, j, pos; |
706 | | |
707 | 28.7k | dd = &decode_data; |
708 | 28.7k | memset(&decode_data, 0, sizeof(decode_data)); |
709 | 28.7k | decode_data.text = (unsigned char *)cli_calloc(DDICSIZ, 1); |
710 | 28.7k | if (!decode_data.text) { |
711 | 0 | return CL_EMEM; |
712 | 0 | } |
713 | 28.7k | decode_data.map = metadata->map; |
714 | 28.7k | decode_data.offset = metadata->offset; |
715 | 28.7k | decode_data.comp_size = metadata->comp_size; |
716 | 28.7k | ret = init_getbits(&decode_data); |
717 | 28.7k | if (ret != CL_SUCCESS) { |
718 | 17 | free(decode_data.text); |
719 | 17 | metadata->offset = decode_data.offset; |
720 | 17 | return ret; |
721 | 17 | } |
722 | 28.7k | decode_data.getlen = decode_data.getbuf = 0; |
723 | 28.7k | decode_data.status = CL_SUCCESS; |
724 | | |
725 | 380M | while (count < metadata->orig_size) { |
726 | 380M | chr = decode_len(&decode_data); |
727 | 380M | if (decode_data.status != CL_SUCCESS) { |
728 | 4.71k | free(decode_data.text); |
729 | 4.71k | metadata->offset = decode_data.offset; |
730 | 4.71k | return decode_data.status; |
731 | 4.71k | } |
732 | 380M | if (chr == 0) { |
733 | 355M | ARJ_GETBITS(dd, chr, CHAR_BIT); |
734 | 355M | if (decode_data.status != CL_SUCCESS) { |
735 | 11.2k | free(decode_data.text); |
736 | 11.2k | metadata->offset = decode_data.offset; |
737 | 11.2k | return decode_data.status; |
738 | 11.2k | } |
739 | 355M | decode_data.text[out_ptr] = (unsigned char)chr; |
740 | 355M | count++; |
741 | 355M | if (++out_ptr >= DDICSIZ) { |
742 | 12.1k | out_ptr = 0; |
743 | 12.1k | if (write_text(metadata->ofd, decode_data.text, DDICSIZ) != CL_SUCCESS) { |
744 | 0 | free(decode_data.text); |
745 | 0 | metadata->offset = decode_data.offset; |
746 | 0 | return CL_EWRITE; |
747 | 0 | } |
748 | 12.1k | } |
749 | 355M | } else { |
750 | 25.1M | j = chr - 1 + THRESHOLD; |
751 | 25.1M | count += j; |
752 | 25.1M | pos = decode_ptr(&decode_data); |
753 | 25.1M | if (decode_data.status != CL_SUCCESS) { |
754 | 7.47k | free(decode_data.text); |
755 | 7.47k | metadata->offset = decode_data.offset; |
756 | 7.47k | return decode_data.status; |
757 | 7.47k | } |
758 | 25.1M | if ((i = out_ptr - pos - 1) < 0) { |
759 | 3.21M | i += DDICSIZ; |
760 | 3.21M | } |
761 | 25.1M | if ((i >= DDICSIZ) || (i < 0)) { |
762 | 0 | cli_dbgmsg("UNARJ: bounds exceeded - probably a corrupted file.\n"); |
763 | 0 | break; |
764 | 0 | } |
765 | 888M | while (j-- > 0) { |
766 | 863M | decode_data.text[out_ptr] = decode_data.text[i]; |
767 | 863M | if (++out_ptr >= DDICSIZ) { |
768 | 24.6k | out_ptr = 0; |
769 | 24.6k | if (write_text(metadata->ofd, decode_data.text, DDICSIZ) != CL_SUCCESS) { |
770 | 0 | free(decode_data.text); |
771 | 0 | metadata->offset = decode_data.offset; |
772 | 0 | return CL_EWRITE; |
773 | 0 | } |
774 | 24.6k | } |
775 | 863M | if (++i >= DDICSIZ) { |
776 | 35.0k | i = 0; |
777 | 35.0k | } |
778 | 863M | } |
779 | 25.1M | } |
780 | 380M | } |
781 | 5.29k | if (out_ptr != 0) { |
782 | 2.36k | write_text(metadata->ofd, decode_data.text, out_ptr); |
783 | 2.36k | } |
784 | | |
785 | 5.29k | free(decode_data.text); |
786 | 5.29k | metadata->offset = decode_data.offset; |
787 | 5.29k | return CL_SUCCESS; |
788 | 28.7k | } |
789 | | |
790 | | static cl_error_t arj_unstore(arj_metadata_t *metadata, int ofd, uint32_t len) |
791 | 1.04M | { |
792 | 1.04M | const unsigned char *data; |
793 | 1.04M | uint32_t rem; |
794 | 1.04M | unsigned int todo; |
795 | 1.04M | size_t count; |
796 | | |
797 | 1.04M | cli_dbgmsg("in arj_unstore\n"); |
798 | 1.04M | rem = len; |
799 | | |
800 | 2.34M | while (rem > 0) { |
801 | 2.32M | todo = (unsigned int)MIN(8192, rem); |
802 | 2.32M | data = fmap_need_off_once_len(metadata->map, metadata->offset, todo, &count); |
803 | 2.32M | if (!data || !count) { |
804 | | /* Truncated file, not enough bytes available */ |
805 | 1.02M | return CL_EFORMAT; |
806 | 1.02M | } |
807 | 1.29M | metadata->offset += count; |
808 | 1.29M | if (cli_writen(ofd, data, count) != count) { |
809 | | /* File writing problem */ |
810 | 0 | return CL_EWRITE; |
811 | 0 | } |
812 | 1.29M | rem -= count; |
813 | 1.29M | } |
814 | 21.8k | return CL_SUCCESS; |
815 | 1.04M | } |
816 | | |
817 | | static int is_arj_archive(arj_metadata_t *metadata) |
818 | 5.28M | { |
819 | 5.28M | const char header_id[2] = {0x60, 0xea}; |
820 | 5.28M | const char *mark; |
821 | | |
822 | 5.28M | mark = fmap_need_off_once(metadata->map, metadata->offset, 2); |
823 | 5.28M | if (!mark) |
824 | 1.05M | return FALSE; |
825 | 4.22M | metadata->offset += 2; |
826 | 4.22M | if (memcmp(&mark[0], &header_id[0], 2) == 0) { |
827 | 3.84M | return TRUE; |
828 | 3.84M | } |
829 | 384k | cli_dbgmsg("Not an ARJ archive\n"); |
830 | 384k | return FALSE; |
831 | 4.22M | } |
832 | | |
833 | | static int arj_read_main_header(arj_metadata_t *metadata) |
834 | 2.37M | { |
835 | 2.37M | uint16_t header_size, count; |
836 | 2.37M | arj_main_hdr_t main_hdr; |
837 | 2.37M | const char *filename = NULL; |
838 | 2.37M | const char *comment = NULL; |
839 | 2.37M | struct text_norm_state fnstate, comstate; |
840 | 2.37M | unsigned char *fnnorm = NULL; |
841 | 2.37M | unsigned char *comnorm = NULL; |
842 | 2.37M | uint32_t ret = TRUE; |
843 | | |
844 | 2.37M | size_t filename_max_len = 0; |
845 | 2.37M | size_t filename_len = 0; |
846 | 2.37M | size_t comment_max_len = 0; |
847 | 2.37M | size_t comment_len = 0; |
848 | 2.37M | size_t orig_offset = metadata->offset; |
849 | | |
850 | 2.37M | if (fmap_readn(metadata->map, &header_size, metadata->offset, 2) != 2) |
851 | 0 | return FALSE; |
852 | | |
853 | 2.37M | metadata->offset += 2; |
854 | 2.37M | header_size = le16_to_host(header_size); |
855 | 2.37M | cli_dbgmsg("Header Size: %d\n", header_size); |
856 | 2.37M | if (header_size == 0) { |
857 | | /* End of archive */ |
858 | 2.53k | ret = FALSE; |
859 | 2.53k | goto done; |
860 | 2.53k | } |
861 | 2.37M | if (header_size > HEADERSIZE_MAX) { |
862 | 30.8k | cli_dbgmsg("arj_read_header: invalid header_size: %u\n ", header_size); |
863 | 30.8k | ret = FALSE; |
864 | 30.8k | goto done; |
865 | 30.8k | } |
866 | 2.34M | if ((header_size + sizeof(header_size)) > (metadata->map->len - metadata->offset)) { |
867 | 41.5k | cli_dbgmsg("arj_read_header: invalid header_size: %u, exceeds length of file.\n", header_size); |
868 | 41.5k | ret = FALSE; |
869 | 41.5k | goto done; |
870 | 41.5k | } |
871 | 2.30M | if (fmap_readn(metadata->map, &main_hdr, metadata->offset, 30) != 30) { |
872 | 10 | ret = FALSE; |
873 | 10 | goto done; |
874 | 10 | } |
875 | 2.30M | metadata->offset += 30; |
876 | | |
877 | 2.30M | cli_dbgmsg("ARJ Main File Header\n"); |
878 | 2.30M | cli_dbgmsg("First Header Size: %d\n", main_hdr.first_hdr_size); |
879 | 2.30M | cli_dbgmsg("Version: %d\n", main_hdr.version); |
880 | 2.30M | cli_dbgmsg("Min version: %d\n", main_hdr.min_version); |
881 | 2.30M | cli_dbgmsg("Host OS: %d\n", main_hdr.host_os); |
882 | 2.30M | cli_dbgmsg("Flags: 0x%x\n", main_hdr.flags); |
883 | 2.30M | cli_dbgmsg("Security version: %d\n", main_hdr.security_version); |
884 | 2.30M | cli_dbgmsg("File type: %d\n", main_hdr.file_type); |
885 | | |
886 | 2.30M | if (main_hdr.first_hdr_size < 30) { |
887 | 12.1k | cli_dbgmsg("Format error. First Header Size < 30\n"); |
888 | 12.1k | ret = FALSE; |
889 | 12.1k | goto done; |
890 | 12.1k | } |
891 | 2.29M | if (main_hdr.first_hdr_size > 30) { |
892 | 2.28M | metadata->offset += main_hdr.first_hdr_size - 30; |
893 | 2.28M | } |
894 | | |
895 | 2.29M | filename_max_len = (header_size + sizeof(header_size)) - (metadata->offset - orig_offset); |
896 | 2.29M | if (filename_max_len > header_size) { |
897 | 19.8k | cli_dbgmsg("UNARJ: Format error. First Header Size invalid\n"); |
898 | 19.8k | ret = FALSE; |
899 | 19.8k | goto done; |
900 | 19.8k | } |
901 | 2.27M | if (filename_max_len > 0) { |
902 | 2.26M | fnnorm = cli_calloc(sizeof(unsigned char), filename_max_len + 1); |
903 | 2.26M | filename = fmap_need_offstr(metadata->map, metadata->offset, filename_max_len + 1); |
904 | 2.26M | if (!filename || !fnnorm) { |
905 | 113k | cli_dbgmsg("UNARJ: Unable to allocate memory for filename\n"); |
906 | 113k | ret = FALSE; |
907 | 113k | goto done; |
908 | 113k | } |
909 | 2.15M | filename_len = CLI_STRNLEN(filename, filename_max_len); |
910 | 2.15M | } |
911 | 2.15M | metadata->offset += filename_len + 1; |
912 | | |
913 | 2.15M | comment_max_len = (header_size + sizeof(header_size)) - (metadata->offset - orig_offset); |
914 | 2.15M | if (comment_max_len > header_size) { |
915 | 9.93k | cli_dbgmsg("UNARJ: Format error. First Header Size invalid\n"); |
916 | 9.93k | ret = FALSE; |
917 | 9.93k | goto done; |
918 | 9.93k | } |
919 | 2.14M | if (comment_max_len > 0) { |
920 | 2.11M | comnorm = cli_calloc(sizeof(unsigned char), comment_max_len + 1); |
921 | 2.11M | comment = fmap_need_offstr(metadata->map, metadata->offset, comment_max_len + 1); |
922 | 2.11M | if (!comment || !comnorm) { |
923 | 89.1k | cli_dbgmsg("UNARJ: Unable to allocate memory for comment\n"); |
924 | 89.1k | ret = FALSE; |
925 | 89.1k | goto done; |
926 | 89.1k | } |
927 | 2.03M | comment_len = CLI_STRNLEN(comment, comment_max_len); |
928 | 2.03M | } |
929 | 2.05M | metadata->offset += comment_len + 1; |
930 | | |
931 | 2.05M | text_normalize_init(&fnstate, fnnorm, filename_max_len); |
932 | 2.05M | text_normalize_init(&comstate, comnorm, comment_max_len); |
933 | | |
934 | 2.05M | text_normalize_buffer(&fnstate, (const unsigned char *)filename, filename_len); |
935 | 2.05M | text_normalize_buffer(&comstate, (const unsigned char *)comment, comment_len); |
936 | | |
937 | 2.05M | cli_dbgmsg("Filename: %s\n", fnnorm); |
938 | 2.05M | cli_dbgmsg("Comment: %s\n", comnorm); |
939 | | |
940 | 2.05M | metadata->offset += 4; /* crc */ |
941 | | /* Skip past any extended header data */ |
942 | 2.53M | for (;;) { |
943 | 2.53M | const uint16_t *countp = fmap_need_off_once(metadata->map, metadata->offset, 2); |
944 | 2.53M | if (!countp) { |
945 | 332k | ret = FALSE; |
946 | 332k | goto done; |
947 | 332k | } |
948 | 2.19M | count = cli_readint16(countp); |
949 | 2.19M | metadata->offset += 2; |
950 | 2.19M | cli_dbgmsg("Extended header size: %d\n", count); |
951 | 2.19M | if (count == 0) { |
952 | 1.72M | break; |
953 | 1.72M | } |
954 | | /* Skip extended header + 4byte CRC */ |
955 | 473k | metadata->offset += count + 4; |
956 | 473k | } |
957 | | |
958 | 2.37M | done: |
959 | | |
960 | 2.37M | if (fnnorm) { |
961 | 2.26M | free(fnnorm); |
962 | 2.26M | fnnorm = NULL; |
963 | 2.26M | } |
964 | | |
965 | 2.37M | if (comnorm) { |
966 | 2.11M | free(comnorm); |
967 | 2.11M | comnorm = NULL; |
968 | 2.11M | } |
969 | 2.37M | return ret; |
970 | 2.05M | } |
971 | | |
972 | | static cl_error_t arj_read_file_header(arj_metadata_t *metadata) |
973 | 1.46M | { |
974 | 1.46M | uint16_t header_size, count; |
975 | 1.46M | const char *filename = NULL, *comment = NULL; |
976 | 1.46M | arj_file_hdr_t file_hdr; |
977 | 1.46M | struct text_norm_state fnstate, comstate; |
978 | 1.46M | unsigned char *fnnorm = NULL; |
979 | 1.46M | unsigned char *comnorm = NULL; |
980 | 1.46M | cl_error_t ret = CL_SUCCESS; |
981 | | |
982 | 1.46M | size_t filename_max_len = 0; |
983 | 1.46M | size_t filename_len = 0; |
984 | 1.46M | size_t comment_max_len = 0; |
985 | 1.46M | size_t comment_len = 0; |
986 | 1.46M | size_t orig_offset = metadata->offset; |
987 | | |
988 | 1.46M | if (fmap_readn(metadata->map, &header_size, metadata->offset, 2) != 2) |
989 | 9.02k | return CL_EFORMAT; |
990 | 1.45M | header_size = le16_to_host(header_size); |
991 | 1.45M | metadata->offset += 2; |
992 | | |
993 | 1.45M | cli_dbgmsg("Header Size: %d\n", header_size); |
994 | 1.45M | if (header_size == 0) { |
995 | | /* End of archive */ |
996 | 1.71k | ret = CL_BREAK; |
997 | 1.71k | goto done; |
998 | 1.71k | } |
999 | 1.45M | if (header_size > HEADERSIZE_MAX) { |
1000 | 30.4k | cli_dbgmsg("arj_read_file_header: invalid header_size: %u\n ", header_size); |
1001 | 30.4k | ret = CL_EFORMAT; |
1002 | 30.4k | goto done; |
1003 | 30.4k | } |
1004 | 1.42M | if ((header_size + sizeof(header_size)) > (metadata->map->len - metadata->offset)) { |
1005 | 29.6k | cli_dbgmsg("arj_read_file_header: invalid header_size: %u, exceeds length of file.\n", header_size); |
1006 | 29.6k | ret = CL_EFORMAT; |
1007 | 29.6k | goto done; |
1008 | 29.6k | } |
1009 | 1.39M | if (fmap_readn(metadata->map, &file_hdr, metadata->offset, 30) != 30) { |
1010 | 18 | ret = CL_EFORMAT; |
1011 | 18 | goto done; |
1012 | 18 | } |
1013 | 1.39M | metadata->offset += 30; |
1014 | 1.39M | file_hdr.comp_size = le32_to_host(file_hdr.comp_size); |
1015 | 1.39M | file_hdr.orig_size = le32_to_host(file_hdr.orig_size); |
1016 | | |
1017 | 1.39M | cli_dbgmsg("ARJ File Header\n"); |
1018 | 1.39M | cli_dbgmsg("First Header Size: %d\n", file_hdr.first_hdr_size); |
1019 | 1.39M | cli_dbgmsg("Version: %d\n", file_hdr.version); |
1020 | 1.39M | cli_dbgmsg("Min version: %d\n", file_hdr.min_version); |
1021 | 1.39M | cli_dbgmsg("Host OS: %d\n", file_hdr.host_os); |
1022 | 1.39M | cli_dbgmsg("Flags: 0x%x\n", file_hdr.flags); |
1023 | 1.39M | cli_dbgmsg("Method: %d\n", file_hdr.method); |
1024 | 1.39M | cli_dbgmsg("File type: %d\n", file_hdr.file_type); |
1025 | 1.39M | cli_dbgmsg("File type: %d\n", file_hdr.password_mod); |
1026 | 1.39M | cli_dbgmsg("Compressed size: %u\n", file_hdr.comp_size); |
1027 | 1.39M | cli_dbgmsg("Original size: %u\n", file_hdr.orig_size); |
1028 | | |
1029 | 1.39M | if (file_hdr.first_hdr_size < 30) { |
1030 | 9.18k | cli_dbgmsg("Format error. First Header Size < 30\n"); |
1031 | 9.18k | ret = CL_EFORMAT; |
1032 | 9.18k | goto done; |
1033 | 9.18k | } |
1034 | | |
1035 | | /* Note: this skips past any extended file start position data (multi-volume) */ |
1036 | 1.38M | if (file_hdr.first_hdr_size > 30) { |
1037 | 1.01M | metadata->offset += file_hdr.first_hdr_size - 30; |
1038 | 1.01M | } |
1039 | | |
1040 | 1.38M | filename_max_len = (header_size + sizeof(header_size)) - (metadata->offset - orig_offset); |
1041 | 1.38M | if (filename_max_len > header_size) { |
1042 | 20.8k | cli_dbgmsg("UNARJ: Format error. First Header Size invalid\n"); |
1043 | 20.8k | ret = CL_EFORMAT; |
1044 | 20.8k | goto done; |
1045 | 20.8k | } |
1046 | 1.36M | if (filename_max_len > 0) { |
1047 | 1.36M | fnnorm = cli_calloc(sizeof(unsigned char), filename_max_len + 1); |
1048 | 1.36M | if (!fnnorm) { |
1049 | 0 | cli_dbgmsg("UNARJ: Unable to allocate memory for filename\n"); |
1050 | 0 | ret = CL_EMEM; |
1051 | 0 | goto done; |
1052 | 0 | } |
1053 | 1.36M | filename = fmap_need_offstr(metadata->map, metadata->offset, filename_max_len + 1); |
1054 | 1.36M | if (!filename) { |
1055 | 37.7k | cli_dbgmsg("UNARJ: Filename is out of file\n"); |
1056 | 37.7k | ret = CL_EFORMAT; |
1057 | 37.7k | goto done; |
1058 | 37.7k | } |
1059 | 1.32M | filename_len = CLI_STRNLEN(filename, filename_max_len); |
1060 | 1.32M | } |
1061 | 1.32M | metadata->offset += filename_len + 1; |
1062 | | |
1063 | 1.32M | comment_max_len = (header_size + sizeof(header_size)) - (metadata->offset - orig_offset); |
1064 | 1.32M | if (comment_max_len > header_size) { |
1065 | 2.78k | cli_dbgmsg("UNARJ: Format error. First Header Size invalid\n"); |
1066 | 2.78k | ret = CL_EFORMAT; |
1067 | 2.78k | goto done; |
1068 | 2.78k | } |
1069 | 1.32M | if (comment_max_len > 0) { |
1070 | 1.30M | comnorm = cli_calloc(sizeof(unsigned char), comment_max_len + 1); |
1071 | 1.30M | if (!comnorm) { |
1072 | 0 | cli_dbgmsg("UNARJ: Unable to allocate memory for comment\n"); |
1073 | 0 | ret = CL_EMEM; |
1074 | 0 | goto done; |
1075 | 0 | } |
1076 | 1.30M | comment = fmap_need_offstr(metadata->map, metadata->offset, comment_max_len + 1); |
1077 | 1.30M | if (!comment) { |
1078 | 6.93k | cli_dbgmsg("UNARJ: comment is out of file\n"); |
1079 | 6.93k | ret = CL_EFORMAT; |
1080 | 6.93k | goto done; |
1081 | 6.93k | } |
1082 | 1.30M | comment_len += CLI_STRNLEN(comment, comment_max_len); |
1083 | 1.30M | } |
1084 | 1.31M | metadata->offset += comment_len + 1; |
1085 | | |
1086 | 1.31M | text_normalize_init(&fnstate, fnnorm, filename_max_len); |
1087 | 1.31M | text_normalize_init(&comstate, comnorm, comment_max_len); |
1088 | | |
1089 | 1.31M | text_normalize_buffer(&fnstate, (const unsigned char *)filename, filename_len); |
1090 | 1.31M | text_normalize_buffer(&comstate, (const unsigned char *)comment, comment_len); |
1091 | | |
1092 | 1.31M | cli_dbgmsg("Filename: %s\n", fnnorm); |
1093 | 1.31M | cli_dbgmsg("Comment: %s\n", comnorm); |
1094 | 1.31M | metadata->filename = CLI_STRNDUP(filename, filename_len); |
1095 | | |
1096 | | /* Skip CRC */ |
1097 | 1.31M | metadata->offset += 4; |
1098 | | |
1099 | | /* Skip past any extended header data */ |
1100 | 1.53M | for (;;) { |
1101 | 1.53M | const uint16_t *countp = fmap_need_off_once(metadata->map, metadata->offset, 2); |
1102 | 1.53M | if (!countp) { |
1103 | 128k | if (metadata->filename) |
1104 | 128k | free(metadata->filename); |
1105 | 128k | metadata->filename = NULL; |
1106 | 128k | ret = CL_EFORMAT; |
1107 | 128k | goto done; |
1108 | 128k | } |
1109 | 1.40M | count = cli_readint16(countp); |
1110 | 1.40M | metadata->offset += 2; |
1111 | 1.40M | cli_dbgmsg("Extended header size: %d\n", count); |
1112 | 1.40M | if (count == 0) { |
1113 | 1.18M | break; |
1114 | 1.18M | } |
1115 | | /* Skip extended header + 4byte CRC */ |
1116 | 222k | metadata->offset += count + 4; |
1117 | 222k | } |
1118 | 1.18M | metadata->comp_size = file_hdr.comp_size; |
1119 | 1.18M | metadata->orig_size = file_hdr.orig_size; |
1120 | 1.18M | metadata->method = file_hdr.method; |
1121 | 1.18M | metadata->encrypted = ((file_hdr.flags & GARBLE_FLAG) != 0) ? TRUE : FALSE; |
1122 | 1.18M | metadata->ofd = -1; |
1123 | 1.18M | if (!metadata->filename) { |
1124 | 0 | ret = CL_EMEM; |
1125 | 0 | goto done; |
1126 | 0 | } |
1127 | | |
1128 | 1.45M | done: |
1129 | | |
1130 | 1.45M | if (fnnorm) { |
1131 | 1.36M | free(fnnorm); |
1132 | 1.36M | fnnorm = NULL; |
1133 | 1.36M | } |
1134 | | |
1135 | 1.45M | if (comnorm) { |
1136 | 1.30M | free(comnorm); |
1137 | 1.30M | comnorm = NULL; |
1138 | 1.30M | } |
1139 | 1.45M | return ret; |
1140 | 1.18M | } |
1141 | | |
1142 | | cl_error_t cli_unarj_open(fmap_t *map, const char *dirname, arj_metadata_t *metadata) |
1143 | 2.37M | { |
1144 | 2.37M | UNUSEDPARAM(dirname); |
1145 | 2.37M | cli_dbgmsg("in cli_unarj_open\n"); |
1146 | 2.37M | metadata->map = map; |
1147 | 2.37M | metadata->offset = 0; |
1148 | 2.37M | if (!is_arj_archive(metadata)) { |
1149 | 0 | cli_dbgmsg("Not in ARJ format\n"); |
1150 | 0 | return CL_EFORMAT; |
1151 | 0 | } |
1152 | 2.37M | if (!arj_read_main_header(metadata)) { |
1153 | 652k | cli_dbgmsg("Failed to read main header\n"); |
1154 | 652k | return CL_EFORMAT; |
1155 | 652k | } |
1156 | 1.72M | return CL_SUCCESS; |
1157 | 2.37M | } |
1158 | | |
1159 | | cl_error_t cli_unarj_prepare_file(const char *dirname, arj_metadata_t *metadata) |
1160 | 2.90M | { |
1161 | 2.90M | cli_dbgmsg("in cli_unarj_prepare_file\n"); |
1162 | 2.90M | if (!metadata || !dirname) { |
1163 | 0 | return CL_ENULLARG; |
1164 | 0 | } |
1165 | | /* Each file is preceded by the ARJ file marker */ |
1166 | 2.90M | if (!is_arj_archive(metadata)) { |
1167 | 1.44M | cli_dbgmsg("Not in ARJ format\n"); |
1168 | 1.44M | return CL_EFORMAT; |
1169 | 1.44M | } |
1170 | 1.46M | return arj_read_file_header(metadata); |
1171 | 2.90M | } |
1172 | | |
1173 | | cl_error_t cli_unarj_extract_file(const char *dirname, arj_metadata_t *metadata) |
1174 | 1.13M | { |
1175 | 1.13M | cl_error_t ret = CL_SUCCESS; |
1176 | 1.13M | char filename[1024]; |
1177 | | |
1178 | 1.13M | cli_dbgmsg("in cli_unarj_extract_file\n"); |
1179 | 1.13M | if (!metadata || !dirname) { |
1180 | 0 | return CL_ENULLARG; |
1181 | 0 | } |
1182 | | |
1183 | 1.13M | if (metadata->encrypted) { |
1184 | 10.3k | cli_dbgmsg("PASSWORDed file (skipping)\n"); |
1185 | 10.3k | metadata->offset += metadata->comp_size; |
1186 | 10.3k | cli_dbgmsg("Target offset: %lu\n", (unsigned long int)metadata->offset); |
1187 | 10.3k | return CL_SUCCESS; |
1188 | 10.3k | } |
1189 | | |
1190 | 1.12M | snprintf(filename, 1024, "%s" PATHSEP "file.uar", dirname); |
1191 | 1.12M | cli_dbgmsg("Filename: %s\n", filename); |
1192 | 1.12M | metadata->ofd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600); |
1193 | 1.12M | if (metadata->ofd < 0) { |
1194 | 0 | return CL_EOPEN; |
1195 | 0 | } |
1196 | 1.12M | switch (metadata->method) { |
1197 | 1.04M | case 0: |
1198 | 1.04M | ret = arj_unstore(metadata, metadata->ofd, metadata->comp_size); |
1199 | 1.04M | break; |
1200 | 13.2k | case 1: |
1201 | 22.6k | case 2: |
1202 | 37.8k | case 3: |
1203 | 37.8k | ret = decode(metadata); |
1204 | 37.8k | break; |
1205 | 28.7k | case 4: |
1206 | 28.7k | ret = decode_f(metadata); |
1207 | 28.7k | break; |
1208 | 6.28k | default: |
1209 | 6.28k | ret = CL_EFORMAT; |
1210 | 6.28k | break; |
1211 | 1.12M | } |
1212 | 1.12M | return ret; |
1213 | 1.12M | } |