Coverage Report

Created: 2023-12-08 06:48

/src/clamav/libclamav/dmg.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (C) 2013-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3
 *  Copyright (C) 2013 Sourcefire, Inc.
4
 *
5
 *  Authors: David Raynor <draynor@sourcefire.com>
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License version 2 as
9
 *  published by the Free Software Foundation.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 *  MA 02110-1301, USA.
20
 */
21
22
#if HAVE_CONFIG_H
23
#include "clamav-config.h"
24
#endif
25
26
#include <stdio.h>
27
#include <errno.h>
28
#if HAVE_STRING_H
29
#include <string.h>
30
#endif
31
#include <ctype.h>
32
#include <fcntl.h>
33
#if HAVE_SYS_PARAM_H
34
#include <sys/param.h> /* for NAME_MAX */
35
#endif
36
37
#if HAVE_LIBZ
38
#include <zlib.h>
39
#endif
40
#if HAVE_BZLIB_H
41
#include <bzlib.h>
42
#ifdef NOBZ2PREFIX
43
#define BZ2_bzDecompress bzDecompress
44
#define BZ2_bzDecompressEnd bzDecompressEnd
45
#define BZ2_bzDecompressInit bzDecompressInit
46
#endif
47
#endif
48
49
#if HAVE_LIBXML2
50
#include <libxml/xmlreader.h>
51
#endif
52
53
#include "clamav.h"
54
#include "others.h"
55
#include "dmg.h"
56
#include "scanners.h"
57
#include "sf_base64decode.h"
58
#include "adc.h"
59
60
/* #define DEBUG_DMG_PARSE */
61
/* #define DEBUG_DMG_BZIP */
62
63
#ifdef DEBUG_DMG_PARSE
64
#define dmg_parsemsg(...) cli_dbgmsg(__VA_ARGS__)
65
#else
66
4.27k
#define dmg_parsemsg(...) ;
67
#endif
68
69
#ifdef DEBUG_DMG_BZIP
70
#define dmg_bzipmsg(...) cli_dbgmsg(__VA_ARGS__)
71
#else
72
0
#define dmg_bzipmsg(...) ;
73
#endif
74
75
enum dmgReadState {
76
    DMG_FIND_BASE_PLIST         = 0,
77
    DMG_FIND_BASE_DICT          = 1,
78
    DMG_FIND_KEY_RESOURCE_FORK  = 2,
79
    DMG_FIND_DICT_RESOURCE_FORK = 3,
80
    DMG_FIND_KEY_BLKX           = 4,
81
    DMG_FIND_BLKX_CONTAINER     = 5,
82
    DMG_FIND_KEY_DATA           = 6,
83
    DMG_FIND_DATA_MISH          = 7,
84
    DMG_MAX_STATE               = 8
85
};
86
87
static int dmg_extract_xml(cli_ctx *, char *, struct dmg_koly_block *);
88
#if HAVE_LIBXML2
89
static int dmg_decode_mish(cli_ctx *, unsigned int *, xmlChar *, struct dmg_mish_with_stripes *);
90
#endif
91
static int cmp_mish_stripes(const void *stripe_a, const void *stripe_b);
92
static int dmg_track_sectors(uint64_t *, uint8_t *, uint32_t, uint32_t, uint64_t);
93
static int dmg_handle_mish(cli_ctx *, unsigned int, char *, uint64_t, struct dmg_mish_with_stripes *);
94
95
int cli_scandmg(cli_ctx *ctx)
96
4.21k
{
97
4.21k
    struct dmg_koly_block hdr;
98
4.21k
    int ret;
99
4.21k
    size_t maplen, nread;
100
4.21k
    size_t pos = 0;
101
4.21k
    char *dirname;
102
4.21k
    const char *outdata;
103
4.21k
    unsigned int file                       = 0;
104
4.21k
    struct dmg_mish_with_stripes *mish_list = NULL, *mish_list_tail = NULL;
105
4.21k
    enum dmgReadState state = DMG_FIND_BASE_PLIST;
106
4.21k
    int stateDepth[DMG_MAX_STATE];
107
4.21k
#if HAVE_LIBXML2
108
4.21k
    xmlTextReaderPtr reader;
109
4.21k
#endif
110
111
4.21k
    if (!ctx || !ctx->fmap) {
112
0
        cli_errmsg("cli_scandmg: Invalid context\n");
113
0
        return CL_ENULLARG;
114
0
    }
115
116
4.21k
    maplen = ctx->fmap->len;
117
4.21k
    if (maplen <= 512) {
118
0
        cli_dbgmsg("cli_scandmg: DMG smaller than DMG koly block!\n");
119
0
        return CL_CLEAN;
120
0
    }
121
4.21k
    pos = maplen - 512;
122
123
    /* Grab koly block */
124
4.21k
    if (fmap_readn(ctx->fmap, &hdr, pos, sizeof(hdr)) != sizeof(hdr)) {
125
0
        cli_dbgmsg("cli_scandmg: Invalid DMG trailer block\n");
126
0
        return CL_EFORMAT;
127
0
    }
128
129
    /* Check magic */
130
4.21k
    hdr.magic = be32_to_host(hdr.magic);
131
4.21k
    if (hdr.magic == 0x6b6f6c79) {
132
4.21k
        cli_dbgmsg("cli_scandmg: Found koly block @ %zu\n", pos);
133
4.21k
    } else {
134
0
        cli_dbgmsg("cli_scandmg: No koly magic, %8x\n", hdr.magic);
135
0
        return CL_EFORMAT;
136
0
    }
137
138
4.21k
    hdr.dataForkOffset = be64_to_host(hdr.dataForkOffset);
139
4.21k
    hdr.dataForkLength = be64_to_host(hdr.dataForkLength);
140
4.21k
    cli_dbgmsg("cli_scandmg: data offset %lu len %d\n", (unsigned long)hdr.dataForkOffset, (int)hdr.dataForkLength);
141
142
4.21k
    hdr.xmlOffset = be64_to_host(hdr.xmlOffset);
143
4.21k
    hdr.xmlLength = be64_to_host(hdr.xmlLength);
144
4.21k
    if (hdr.xmlLength > (uint64_t)INT_MAX) {
145
300
        cli_dbgmsg("cli_scandmg: The embedded XML is way larger than necessary, and probably corrupt or tampered with.\n");
146
300
        return CL_EFORMAT;
147
300
    }
148
3.91k
    if ((hdr.xmlOffset > (uint64_t)maplen) || (hdr.xmlLength > (uint64_t)maplen) || (hdr.xmlOffset + hdr.xmlLength) > (uint64_t)maplen) {
149
3.85k
        cli_dbgmsg("cli_scandmg: XML out of range for this file\n");
150
3.85k
        return CL_EFORMAT;
151
3.85k
    }
152
65
    cli_dbgmsg("cli_scandmg: XML offset %lu len %d\n", (unsigned long)hdr.xmlOffset, (int)hdr.xmlLength);
153
65
    if (hdr.xmlLength == 0) {
154
7
        cli_dbgmsg("cli_scandmg: Embedded XML length is zero.\n");
155
7
        return CL_EFORMAT;
156
7
    }
157
158
    /* Create temp folder for contents */
159
58
    if (!(dirname = cli_gentemp_with_prefix(ctx->sub_tmpdir, "dmg-tmp"))) {
160
0
        return CL_ETMPDIR;
161
0
    }
162
58
    if (mkdir(dirname, 0700)) {
163
0
        cli_errmsg("cli_scandmg: Cannot create temporary directory %s\n", dirname);
164
0
        free(dirname);
165
0
        return CL_ETMPDIR;
166
0
    }
167
58
    cli_dbgmsg("cli_scandmg: Extracting into %s\n", dirname);
168
169
    /* Dump XML to tempfile, if needed */
170
58
    if (ctx->engine->keeptmp && !(ctx->engine->engine_options & ENGINE_OPTIONS_FORCE_TO_DISK)) {
171
0
        int xret;
172
0
        xret = dmg_extract_xml(ctx, dirname, &hdr);
173
174
0
        if (xret != CL_SUCCESS) {
175
            /* Printed err detail inside dmg_extract_xml */
176
0
            free(dirname);
177
0
            return xret;
178
0
        }
179
0
    }
180
181
    /* scan XML with cli_magic_scan_nested_fmap_type */
182
58
    ret = cli_magic_scan_nested_fmap_type(ctx->fmap, (size_t)hdr.xmlOffset, (size_t)hdr.xmlLength,
183
58
                                          ctx, CL_TYPE_ANY, NULL, LAYER_ATTRIBUTES_NONE);
184
58
    if (ret != CL_CLEAN) {
185
1
        cli_dbgmsg("cli_scandmg: retcode from scanning TOC xml: %s\n", cl_strerror(ret));
186
1
        if (!ctx->engine->keeptmp)
187
1
            cli_rmdirs(dirname);
188
1
        free(dirname);
189
1
        return ret;
190
1
    }
191
192
    /* page data from map */
193
57
    outdata = fmap_need_off_once_len(ctx->fmap, hdr.xmlOffset, hdr.xmlLength, &nread);
194
57
    if (!outdata || (nread != hdr.xmlLength)) {
195
0
        cli_errmsg("cli_scandmg: Failed getting XML from map, len %d\n", (int)hdr.xmlLength);
196
0
        if (!ctx->engine->keeptmp)
197
0
            cli_rmdirs(dirname);
198
0
        free(dirname);
199
0
        return CL_EMAP;
200
0
    }
201
202
    /* time to walk the tree */
203
    /* plist -> dict -> (key:resource_fork) dict -> (key:blkx) array -> dict */
204
    /* each of those bottom level dict should have 4 parts */
205
    /* [ Attributes, Data, ID, Name ], where Data is Base64 mish block */
206
207
/* This is the block where we require libxml2 */
208
57
#if HAVE_LIBXML2
209
210
57
#define DMG_XML_PARSE_OPTS ((XML_PARSE_NONET | XML_PARSE_COMPACT) | CLAMAV_MIN_XMLREADER_FLAGS)
211
212
57
    reader = xmlReaderForMemory(outdata, (int)hdr.xmlLength, "toc.xml", NULL, DMG_XML_PARSE_OPTS);
213
57
    if (!reader) {
214
0
        cli_dbgmsg("cli_scandmg: Failed parsing XML!\n");
215
0
        if (!ctx->engine->keeptmp)
216
0
            cli_rmdirs(dirname);
217
0
        free(dirname);
218
0
        return CL_EFORMAT;
219
0
    }
220
221
57
    stateDepth[DMG_FIND_BASE_PLIST] = -1;
222
223
    // May need to check for (xmlTextReaderIsEmptyElement(reader) == 0)
224
225
    /* Break loop if have return code or reader can't read any more */
226
5.07k
    while ((ret == CL_CLEAN) && (xmlTextReaderRead(reader) == 1)) {
227
5.02k
        xmlReaderTypes nodeType;
228
5.02k
        nodeType = xmlTextReaderNodeType(reader);
229
230
5.02k
        if (nodeType == XML_READER_TYPE_ELEMENT) {
231
            // New element, do name check
232
1.50k
            xmlChar *nodeName;
233
1.50k
            int depth;
234
235
1.50k
            depth = xmlTextReaderDepth(reader);
236
1.50k
            if (depth < 0) {
237
0
                break;
238
0
            }
239
1.50k
            if ((depth > 50) && SCAN_HEURISTICS) {
240
                // Possible heuristic, should limit runaway
241
0
                cli_dbgmsg("cli_scandmg: Excessive nesting in DMG TOC.\n");
242
0
                break;
243
0
            }
244
1.50k
            nodeName = xmlTextReaderLocalName(reader);
245
1.50k
            if (!nodeName)
246
0
                continue;
247
1.50k
            dmg_parsemsg("read: name %s depth %d\n", nodeName, depth);
248
249
1.50k
            if ((state == DMG_FIND_DATA_MISH) && (depth == stateDepth[state - 1])) {
250
91
                xmlChar *textValue;
251
91
                struct dmg_mish_with_stripes *mish_set;
252
                /* Reset state early, for continue cases */
253
91
                stateDepth[DMG_FIND_KEY_DATA] = -1;
254
91
                state--;
255
91
                if (xmlStrcmp(nodeName, (const xmlChar *)"data") != 0) {
256
1
                    cli_dbgmsg("cli_scandmg: Not blkx data element\n");
257
1
                    xmlFree(nodeName);
258
1
                    continue;
259
1
                }
260
90
                dmg_parsemsg("read: Found blkx data element\n");
261
                /* Pull out data content from text */
262
90
                if (xmlTextReaderIsEmptyElement(reader)) {
263
0
                    cli_dbgmsg("cli_scandmg: blkx data element is empty\n");
264
0
                    xmlFree(nodeName);
265
0
                    continue;
266
0
                }
267
90
                if (xmlTextReaderRead(reader) != 1) {
268
5
                    xmlFree(nodeName);
269
5
                    break;
270
5
                }
271
85
                if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) {
272
0
                    cli_dbgmsg("cli_scandmg: Next node not text\n");
273
0
                    xmlFree(nodeName);
274
0
                    continue;
275
0
                }
276
85
                textValue = xmlTextReaderValue(reader);
277
85
                if (textValue == NULL) {
278
0
                    xmlFree(nodeName);
279
0
                    continue;
280
0
                }
281
                /* Have encoded mish block */
282
85
                mish_set = cli_malloc(sizeof(struct dmg_mish_with_stripes));
283
85
                if (mish_set == NULL) {
284
0
                    ret = CL_EMEM;
285
0
                    xmlFree(textValue);
286
0
                    xmlFree(nodeName);
287
0
                    break;
288
0
                }
289
85
                ret = dmg_decode_mish(ctx, &file, textValue, mish_set);
290
85
                xmlFree(textValue);
291
85
                if (ret == CL_EFORMAT) {
292
                    /* Didn't decode, or not a mish block */
293
35
                    ret = CL_CLEAN;
294
35
                    free(mish_set);
295
35
                    xmlFree(nodeName);
296
35
                    continue;
297
50
                } else if (ret != CL_CLEAN) {
298
0
                    xmlFree(nodeName);
299
0
                    free(mish_set);
300
0
                    continue;
301
0
                }
302
                /* Add mish block to list */
303
50
                if (mish_list_tail != NULL) {
304
30
                    mish_list_tail->next = mish_set;
305
30
                    mish_list_tail       = mish_set;
306
30
                } else {
307
20
                    mish_list      = mish_set;
308
20
                    mish_list_tail = mish_set;
309
20
                }
310
50
                mish_list_tail->next = NULL;
311
50
            }
312
1.46k
            if ((state == DMG_FIND_KEY_DATA) && (depth > stateDepth[state - 1]) && (xmlStrcmp(nodeName, (const xmlChar *)"key") == 0)) {
313
468
                xmlChar *textValue;
314
468
                dmg_parsemsg("read: Found key - checking for Data\n");
315
468
                if (xmlTextReaderRead(reader) != 1) {
316
1
                    xmlFree(nodeName);
317
1
                    break;
318
1
                }
319
467
                if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) {
320
0
                    cli_dbgmsg("cli_scandmg: Key node no text\n");
321
0
                    xmlFree(nodeName);
322
0
                    continue;
323
0
                }
324
467
                textValue = xmlTextReaderValue(reader);
325
467
                if (textValue == NULL) {
326
0
                    cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n");
327
0
                    xmlFree(nodeName);
328
0
                    continue;
329
0
                }
330
467
                if (xmlStrcmp(textValue, (const xmlChar *)"Data") == 0) {
331
92
                    dmg_parsemsg("read: Matched data\n");
332
92
                    stateDepth[DMG_FIND_KEY_DATA] = depth;
333
92
                    state++;
334
375
                } else {
335
375
                    dmg_parsemsg("read: text value is %s\n", textValue);
336
375
                }
337
467
                xmlFree(textValue);
338
467
            }
339
1.45k
            if ((state == DMG_FIND_BLKX_CONTAINER) && (depth == stateDepth[state - 1])) {
340
26
                if (xmlStrcmp(nodeName, (const xmlChar *)"array") == 0) {
341
26
                    dmg_parsemsg("read: Found array blkx\n");
342
26
                    stateDepth[DMG_FIND_BLKX_CONTAINER] = depth;
343
26
                    state++;
344
26
                } else if (xmlStrcmp(nodeName, (const xmlChar *)"dict") == 0) {
345
0
                    dmg_parsemsg("read: Found dict blkx\n");
346
0
                    stateDepth[DMG_FIND_BLKX_CONTAINER] = depth;
347
0
                    state++;
348
0
                } else {
349
0
                    cli_dbgmsg("cli_scandmg: Bad blkx, not container\n");
350
0
                    stateDepth[DMG_FIND_KEY_BLKX] = -1;
351
0
                    state--;
352
0
                }
353
26
            }
354
1.45k
            if ((state == DMG_FIND_KEY_BLKX) && (depth == stateDepth[state - 1] + 1) && (xmlStrcmp(nodeName, (const xmlChar *)"key") == 0)) {
355
40
                xmlChar *textValue;
356
40
                dmg_parsemsg("read: Found key - checking for blkx\n");
357
40
                if (xmlTextReaderRead(reader) != 1) {
358
0
                    xmlFree(nodeName);
359
0
                    break;
360
0
                }
361
40
                if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) {
362
0
                    cli_dbgmsg("cli_scandmg: Key node no text\n");
363
0
                    xmlFree(nodeName);
364
0
                    continue;
365
0
                }
366
40
                textValue = xmlTextReaderValue(reader);
367
40
                if (textValue == NULL) {
368
0
                    cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n");
369
0
                    xmlFree(nodeName);
370
0
                    continue;
371
0
                }
372
40
                if (xmlStrcmp(textValue, (const xmlChar *)"blkx") == 0) {
373
26
                    cli_dbgmsg("cli_scandmg: Matched blkx\n");
374
26
                    stateDepth[DMG_FIND_KEY_BLKX] = depth;
375
26
                    state++;
376
26
                } else {
377
14
                    cli_dbgmsg("cli_scandmg: wanted blkx, text value is %s\n", textValue);
378
14
                }
379
40
                xmlFree(textValue);
380
40
            }
381
1.45k
            if ((state == DMG_FIND_DICT_RESOURCE_FORK) && (depth == stateDepth[state - 1])) {
382
31
                if (xmlStrcmp(nodeName, (const xmlChar *)"dict") == 0) {
383
27
                    dmg_parsemsg("read: Found resource-fork dict\n");
384
27
                    stateDepth[DMG_FIND_DICT_RESOURCE_FORK] = depth;
385
27
                    state++;
386
27
                } else {
387
4
                    dmg_parsemsg("read: Not resource-fork dict\n");
388
4
                    stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = -1;
389
4
                    state--;
390
4
                }
391
31
            }
392
1.45k
            if ((state == DMG_FIND_KEY_RESOURCE_FORK) && (depth == stateDepth[state - 1] + 1) && (xmlStrcmp(nodeName, (const xmlChar *)"key") == 0)) {
393
32
                dmg_parsemsg("read: Found resource-fork key\n");
394
32
                stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = depth;
395
32
                state++;
396
32
            }
397
1.45k
            if ((state == DMG_FIND_BASE_DICT) && (depth == stateDepth[state - 1] + 1) && (xmlStrcmp(nodeName, (const xmlChar *)"dict") == 0)) {
398
31
                dmg_parsemsg("read: Found dict start\n");
399
31
                stateDepth[DMG_FIND_BASE_DICT] = depth;
400
31
                state++;
401
31
            }
402
1.45k
            if ((state == DMG_FIND_BASE_PLIST) && (xmlStrcmp(nodeName, (const xmlChar *)"plist") == 0)) {
403
34
                dmg_parsemsg("read: Found plist start\n");
404
34
                stateDepth[DMG_FIND_BASE_PLIST] = depth;
405
34
                state++;
406
34
            }
407
1.45k
            xmlFree(nodeName);
408
3.52k
        } else if ((nodeType == XML_READER_TYPE_END_ELEMENT) && (state > DMG_FIND_BASE_PLIST)) {
409
1.30k
            int significantEnd = 0;
410
1.30k
            int depth          = xmlTextReaderDepth(reader);
411
1.30k
            if (depth < 0) {
412
0
                break;
413
1.30k
            } else if (depth < stateDepth[state - 1]) {
414
4
                significantEnd = 1;
415
1.30k
            } else if ((depth == stateDepth[state - 1]) && (state - 1 == DMG_FIND_BLKX_CONTAINER)) {
416
                /* Special case, ending blkx container */
417
13
                significantEnd = 1;
418
13
            }
419
1.30k
            if (significantEnd) {
420
17
                dmg_parsemsg("read: significant end tag, state %d\n", state);
421
17
                stateDepth[state - 1] = -1;
422
17
                state--;
423
17
                if ((state - 1 == DMG_FIND_KEY_RESOURCE_FORK) || (state - 1 == DMG_FIND_KEY_BLKX)) {
424
                    /* Keys end their own tag (validly) and the next state depends on the following tag */
425
                    // cli_dbgmsg("read: significant end tag ending prior key state\n");
426
15
                    stateDepth[state - 1] = -1;
427
15
                    state--;
428
15
                }
429
1.29k
            } else {
430
1.29k
                dmg_parsemsg("read: not significant end tag, state %d depth %d prior depth %d\n", state, depth, stateDepth[state - 1]);
431
1.29k
            }
432
1.30k
        }
433
5.02k
    }
434
435
57
    xmlFreeTextReader(reader);
436
437
#else
438
439
    cli_dbgmsg("cli_scandmg: libxml2 support is compiled out. It is required for full DMG support.\n");
440
441
#endif
442
443
    /* Loop over mish array */
444
57
    file = 0;
445
93
    while ((ret == CL_CLEAN) && (mish_list != NULL)) {
446
        /* Handle & scan mish block */
447
36
        ret = dmg_handle_mish(ctx, file++, dirname, hdr.xmlOffset, mish_list);
448
36
        free(mish_list->mish);
449
36
        mish_list_tail = mish_list;
450
36
        mish_list      = mish_list->next;
451
36
        free(mish_list_tail);
452
36
    }
453
454
    /* Cleanup */
455
    /* If error occurred, need to free mish items and mish blocks */
456
71
    while (mish_list != NULL) {
457
14
        free(mish_list->mish);
458
14
        mish_list_tail = mish_list;
459
14
        mish_list      = mish_list->next;
460
14
        free(mish_list_tail);
461
14
    }
462
57
    if (!ctx->engine->keeptmp)
463
57
        cli_rmdirs(dirname);
464
57
    free(dirname);
465
57
    return ret;
466
57
}
467
468
#if HAVE_LIBXML2
469
/* Transform the base64-encoded string into the binary structure
470
 * After this, the base64 string (from xml) can be released
471
 * If mish_set->mish is set by this function, it must be freed by the caller */
472
static int dmg_decode_mish(cli_ctx *ctx, unsigned int *mishblocknum, xmlChar *mish_base64,
473
                           struct dmg_mish_with_stripes *mish_set)
474
85
{
475
85
    size_t base64_len, buff_size, decoded_len;
476
85
    uint8_t *decoded;
477
85
    const uint8_t mish_magic[4] = {0x6d, 0x69, 0x73, 0x68};
478
479
85
    UNUSEDPARAM(ctx);
480
481
85
    (*mishblocknum)++;
482
85
    base64_len = strlen((const char *)mish_base64);
483
85
    dmg_parsemsg("dmg_decode_mish: len of encoded block %u is %lu\n", *mishblocknum, base64_len);
484
485
    /* speed vs memory, could walk the encoded data and skip whitespace in calculation */
486
85
    buff_size = 3 * base64_len / 4 + 4;
487
85
    dmg_parsemsg("dmg_decode_mish: buffer for mish block %u is %lu\n", *mishblocknum, (unsigned long)buff_size);
488
85
    decoded = cli_malloc(buff_size);
489
85
    if (!decoded)
490
0
        return CL_EMEM;
491
492
85
    if (sf_base64decode((uint8_t *)mish_base64, base64_len, decoded, buff_size - 1, &decoded_len)) {
493
8
        cli_dbgmsg("dmg_decode_mish: failed base64 decoding on mish block %u\n", *mishblocknum);
494
8
        free(decoded);
495
8
        return CL_EFORMAT;
496
8
    }
497
77
    dmg_parsemsg("dmg_decode_mish: len of decoded mish block %u is %lu\n", *mishblocknum, (unsigned long)decoded_len);
498
499
77
    if (decoded_len < sizeof(struct dmg_mish_block)) {
500
5
        cli_dbgmsg("dmg_decode_mish: block %u too short for valid mish block\n", *mishblocknum);
501
5
        free(decoded);
502
5
        return CL_EFORMAT;
503
5
    }
504
    /* mish check: magic is mish, have to check after conversion from base64
505
     * mish base64 is bWlzaA [but last character can change last two bytes]
506
     * won't see that in practice much (affects value of version field) */
507
72
    if (memcmp(decoded, mish_magic, 4)) {
508
5
        cli_dbgmsg("dmg_decode_mish: block %u does not have mish magic\n", *mishblocknum);
509
5
        free(decoded);
510
5
        return CL_EFORMAT;
511
5
    }
512
513
67
    mish_set->mish              = (struct dmg_mish_block *)decoded;
514
67
    mish_set->mish->startSector = be64_to_host(mish_set->mish->startSector);
515
67
    mish_set->mish->sectorCount = be64_to_host(mish_set->mish->sectorCount);
516
67
    mish_set->mish->dataOffset  = be64_to_host(mish_set->mish->dataOffset);
517
    // mish_set->mish->bufferCount = be32_to_host(mish_set->mish->bufferCount);
518
67
    mish_set->mish->blockDataCount = be32_to_host(mish_set->mish->blockDataCount);
519
520
67
    cli_dbgmsg("dmg_decode_mish: startSector = " STDu64 " sectorCount = " STDu64
521
67
               " dataOffset = " STDu64 " stripeCount = " STDu32 "\n",
522
67
               mish_set->mish->startSector, mish_set->mish->sectorCount,
523
67
               mish_set->mish->dataOffset, mish_set->mish->blockDataCount);
524
525
    /* decoded length should be mish block + blockDataCount * 40 */
526
67
    if (decoded_len < (sizeof(struct dmg_mish_block) + mish_set->mish->blockDataCount * sizeof(struct dmg_block_data))) {
527
17
        cli_dbgmsg("dmg_decode_mish: mish block %u too small\n", *mishblocknum);
528
17
        free(decoded);
529
17
        mish_set->mish = NULL;
530
17
        return CL_EFORMAT;
531
50
    } else if (decoded_len > (sizeof(struct dmg_mish_block) + mish_set->mish->blockDataCount * sizeof(struct dmg_block_data))) {
532
13
        cli_dbgmsg("dmg_decode_mish: mish block %u bigger than needed, continuing\n", *mishblocknum);
533
13
    }
534
535
50
    mish_set->stripes = (struct dmg_block_data *)(decoded + sizeof(struct dmg_mish_block));
536
50
    return CL_CLEAN;
537
67
}
538
#endif
539
540
/* Comparator for stripe sorting */
541
static int cmp_mish_stripes(const void *stripe_a, const void *stripe_b)
542
2
{
543
2
    const struct dmg_block_data *a = stripe_a, *b = stripe_b;
544
2
    return a->startSector - b->startSector;
545
2
}
546
547
/* Safely track sector sizes for output estimate */
548
static int dmg_track_sectors(uint64_t *total, uint8_t *data_to_write,
549
                             uint32_t stripeNum, uint32_t stripeType, uint64_t stripeCount)
550
47
{
551
47
    int ret = CL_CLEAN, usable = 0;
552
553
47
    switch (stripeType) {
554
0
        case DMG_STRIPE_STORED:
555
0
            *data_to_write = 1;
556
0
            usable         = 1;
557
0
            break;
558
2
        case DMG_STRIPE_ADC:
559
2
            *data_to_write = 1;
560
2
            usable         = 1;
561
2
            break;
562
14
        case DMG_STRIPE_DEFLATE:
563
14
#if HAVE_LIBZ
564
14
            *data_to_write = 1;
565
14
            usable         = 1;
566
#else
567
            cli_warnmsg("dmg_track_sectors: Need zlib decompression to properly scan this file.\n");
568
            return CL_EFORMAT;
569
#endif
570
14
            break;
571
0
        case DMG_STRIPE_BZ:
572
0
#if HAVE_BZLIB_H
573
0
            *data_to_write = 1;
574
0
            usable         = 1;
575
#else
576
            cli_warnmsg("dmg_track_sectors: Need bzip2 decompression to properly scan this file.\n");
577
            return CL_EFORMAT;
578
#endif
579
0
            break;
580
0
        case DMG_STRIPE_EMPTY:
581
5
        case DMG_STRIPE_ZEROES:
582
            /* Usable, but only zeroes is not worth scanning on its own */
583
5
            usable = 1;
584
5
            break;
585
0
        case DMG_STRIPE_SKIP:
586
23
        case DMG_STRIPE_END:
587
            /* These should be sectorCount 0 */
588
23
            break;
589
3
        default:
590
3
            if (stripeCount) {
591
                /* Continue for now */
592
3
                cli_dbgmsg("dmg_track_sectors: unknown type on stripe " STDu32 ", will skip\n", stripeNum);
593
3
            } else {
594
                /* Continue, no sectors missed  */
595
0
                cli_dbgmsg("dmg_track_sectors: unknown type on empty stripe " STDu32 "\n", stripeNum);
596
0
            }
597
3
            break;
598
47
    }
599
600
47
    if (usable) {
601
        /* Check for wrap */
602
21
        if (*total < (*total + stripeCount)) {
603
21
            *total = *total + stripeCount;
604
21
        } else if (stripeCount) {
605
0
            cli_dbgmsg("dmg_track_sectors: *total would wrap uint64, suspicious\n");
606
0
            ret = CL_EFORMAT;
607
0
        } else {
608
            /* Can continue */
609
0
            cli_dbgmsg("dmg_track_sectors: unexpected zero sectorCount on stripe " STDu32 "\n", stripeNum);
610
0
        }
611
21
    }
612
613
47
    return ret;
614
47
}
615
616
/* Stripe handling: zero block (type 0x0 or 0x2) */
617
static int dmg_stripe_zeroes(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set)
618
0
{
619
0
    int ret    = CL_CLEAN;
620
0
    size_t len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE;
621
0
    size_t written;
622
0
    uint8_t obuf[BUFSIZ];
623
624
0
    UNUSEDPARAM(ctx);
625
626
0
    cli_dbgmsg("dmg_stripe_zeroes: stripe " STDu32 "\n", index);
627
0
    if (len == 0)
628
0
        return CL_CLEAN;
629
630
0
    memset(obuf, 0, sizeof(obuf));
631
0
    while (len > sizeof(obuf)) {
632
0
        written = cli_writen(fd, obuf, sizeof(obuf));
633
0
        if (written != sizeof(obuf)) {
634
0
            ret = CL_EWRITE;
635
0
            break;
636
0
        }
637
0
        len -= sizeof(obuf);
638
0
    }
639
640
0
    if ((ret == CL_CLEAN) && (len > 0)) {
641
0
        written = cli_writen(fd, obuf, len);
642
0
        if (written != len) {
643
0
            ret = CL_EWRITE;
644
0
        }
645
0
    }
646
647
0
    if (ret != CL_CLEAN) {
648
0
        cli_errmsg("dmg_stripe_zeroes: error writing bytes to file (out of disk space?)\n");
649
0
        return CL_EWRITE;
650
0
    }
651
0
    return CL_CLEAN;
652
0
}
653
654
/* Stripe handling: stored block (type 0x1) */
655
static int dmg_stripe_store(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set)
656
0
{
657
0
    const void *obuf;
658
0
    size_t off = mish_set->stripes[index].dataOffset;
659
0
    size_t len = mish_set->stripes[index].dataLength;
660
0
    size_t written;
661
662
0
    cli_dbgmsg("dmg_stripe_store: stripe " STDu32 "\n", index);
663
0
    if (len == 0)
664
0
        return CL_CLEAN;
665
666
0
    obuf = (void *)fmap_need_off_once(ctx->fmap, off, len);
667
0
    if (!obuf) {
668
0
        cli_warnmsg("dmg_stripe_store: fmap need failed on stripe " STDu32 "\n", index);
669
0
        return CL_EMAP;
670
0
    }
671
0
    written = cli_writen(fd, obuf, len);
672
0
    if (written == (size_t)-1) {
673
0
        cli_errmsg("dmg_stripe_store: error writing bytes to file (out of disk space?)\n");
674
0
        return CL_EWRITE;
675
0
    } else if (written != len) {
676
0
        cli_errmsg("dmg_stripe_store: error writing bytes to file (out of disk space?)\n");
677
0
        return CL_EWRITE;
678
0
    }
679
0
    return CL_CLEAN;
680
0
}
681
682
/* Stripe handling: ADC block (type 0x80000004) */
683
static int dmg_stripe_adc(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set)
684
1
{
685
1
    int adcret;
686
1
    adc_stream strm;
687
1
    size_t off            = mish_set->stripes[index].dataOffset;
688
1
    size_t len            = mish_set->stripes[index].dataLength;
689
1
    uint64_t size_so_far  = 0;
690
1
    uint64_t expected_len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE;
691
1
    uint8_t obuf[BUFSIZ];
692
693
1
    cli_dbgmsg("dmg_stripe_adc: stripe " STDu32 " initial len " STDu64 " expected len " STDu64 "\n",
694
1
               index, (uint64_t)len, (uint64_t)expected_len);
695
1
    if (len == 0)
696
0
        return CL_CLEAN;
697
698
1
    memset(&strm, 0, sizeof(strm));
699
1
    strm.next_in = (uint8_t *)fmap_need_off_once(ctx->fmap, off, len);
700
1
    if (!strm.next_in) {
701
0
        cli_warnmsg("dmg_stripe_adc: fmap need failed on stripe " STDu32 "\n", index);
702
0
        return CL_EMAP;
703
0
    }
704
1
    strm.avail_in  = len;
705
1
    strm.next_out  = obuf;
706
1
    strm.avail_out = sizeof(obuf);
707
708
1
    adcret = adc_decompressInit(&strm);
709
1
    if (adcret != ADC_OK) {
710
0
        cli_warnmsg("dmg_stripe_adc: adc_decompressInit failed\n");
711
0
        return CL_EMEM;
712
0
    }
713
714
1
    while (adcret == ADC_OK) {
715
1
        size_t written;
716
1
        if (size_so_far > expected_len) {
717
0
            cli_warnmsg("dmg_stripe_adc: expected size exceeded!\n");
718
0
            adc_decompressEnd(&strm);
719
0
            return CL_EFORMAT;
720
0
        }
721
1
        adcret = adc_decompress(&strm);
722
1
        switch (adcret) {
723
0
            case ADC_OK:
724
0
                if (strm.avail_out == 0) {
725
0
                    if ((written = cli_writen(fd, obuf, sizeof(obuf))) != sizeof(obuf)) {
726
0
                        cli_errmsg("dmg_stripe_adc: failed write to output file\n");
727
0
                        adc_decompressEnd(&strm);
728
0
                        return CL_EWRITE;
729
0
                    }
730
0
                    size_so_far += written;
731
0
                    strm.next_out  = obuf;
732
0
                    strm.avail_out = sizeof(obuf);
733
0
                }
734
0
                continue;
735
0
            case ADC_STREAM_END:
736
1
            default:
737
1
                written = sizeof(obuf) - strm.avail_out;
738
1
                if (written) {
739
1
                    if ((cli_writen(fd, obuf, written)) != written) {
740
0
                        cli_errmsg("dmg_stripe_adc: failed write to output file\n");
741
0
                        adc_decompressEnd(&strm);
742
0
                        return CL_EWRITE;
743
0
                    }
744
1
                    size_so_far += written;
745
1
                    strm.next_out  = obuf;
746
1
                    strm.avail_out = sizeof(obuf);
747
1
                }
748
1
                if (adcret == ADC_STREAM_END)
749
0
                    break;
750
1
                cli_dbgmsg("dmg_stripe_adc: after writing " STDu64 " bytes, "
751
1
                           "got error %d decompressing stripe " STDu32 "\n",
752
1
                           size_so_far, adcret, index);
753
1
                adc_decompressEnd(&strm);
754
1
                return CL_EFORMAT;
755
1
        }
756
0
        break;
757
1
    }
758
759
0
    adc_decompressEnd(&strm);
760
0
    cli_dbgmsg("dmg_stripe_adc: stripe " STDu32 " actual len " STDu64 " expected len " STDu64 "\n",
761
0
               index, size_so_far, expected_len);
762
0
    return CL_CLEAN;
763
1
}
764
765
/* Stripe handling: deflate block (type 0x80000005) */
766
static int dmg_stripe_inflate(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set)
767
13
{
768
13
    int zstat;
769
13
    z_stream strm;
770
13
    size_t off            = mish_set->stripes[index].dataOffset;
771
13
    size_t len            = mish_set->stripes[index].dataLength;
772
13
    uint64_t size_so_far  = 0;
773
13
    uint64_t expected_len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE;
774
13
    uint8_t obuf[BUFSIZ];
775
776
13
    cli_dbgmsg("dmg_stripe_inflate: stripe " STDu32 "\n", index);
777
13
    if (len == 0)
778
0
        return CL_CLEAN;
779
780
13
    memset(&strm, 0, sizeof(strm));
781
13
    strm.next_in = (void *)fmap_need_off_once(ctx->fmap, off, len);
782
13
    if (!strm.next_in) {
783
0
        cli_warnmsg("dmg_stripe_inflate: fmap need failed on stripe " STDu32 "\n", index);
784
0
        return CL_EMAP;
785
0
    }
786
13
    strm.avail_in  = len;
787
13
    strm.next_out  = obuf;
788
13
    strm.avail_out = sizeof(obuf);
789
790
13
    zstat = inflateInit(&strm);
791
13
    if (zstat != Z_OK) {
792
0
        cli_warnmsg("dmg_stripe_inflate: inflateInit failed\n");
793
0
        return CL_EMEM;
794
0
    }
795
796
26
    while (strm.avail_in) {
797
24
        size_t written;
798
24
        if (size_so_far > expected_len) {
799
0
            cli_warnmsg("dmg_stripe_inflate: expected size exceeded!\n");
800
0
            inflateEnd(&strm);
801
0
            return CL_EFORMAT;
802
0
        }
803
24
        zstat = inflate(&strm, Z_NO_FLUSH); /* zlib */
804
24
        switch (zstat) {
805
13
            case Z_OK:
806
13
                if (strm.avail_out == 0) {
807
11
                    if ((written = cli_writen(fd, obuf, sizeof(obuf))) != sizeof(obuf)) {
808
0
                        cli_errmsg("dmg_stripe_inflate: failed write to output file\n");
809
0
                        inflateEnd(&strm);
810
0
                        return CL_EWRITE;
811
0
                    }
812
11
                    size_so_far += written;
813
11
                    strm.next_out  = (Bytef *)obuf;
814
11
                    strm.avail_out = sizeof(obuf);
815
11
                }
816
13
                continue;
817
13
            case Z_STREAM_END:
818
11
            default:
819
11
                written = sizeof(obuf) - strm.avail_out;
820
11
                if (written) {
821
9
                    if ((cli_writen(fd, obuf, written)) != written) {
822
0
                        cli_errmsg("dmg_stripe_inflate: failed write to output file\n");
823
0
                        inflateEnd(&strm);
824
0
                        return CL_EWRITE;
825
0
                    }
826
9
                    size_so_far += written;
827
9
                    strm.next_out  = (Bytef *)obuf;
828
9
                    strm.avail_out = sizeof(obuf);
829
9
                    if (zstat == Z_STREAM_END)
830
7
                        break;
831
9
                }
832
4
                if (strm.msg)
833
4
                    cli_dbgmsg("dmg_stripe_inflate: after writing " STDu64 " bytes, "
834
4
                               "got error \"%s\" inflating stripe " STDu32 "\n",
835
4
                               size_so_far, strm.msg, index);
836
0
                else
837
0
                    cli_dbgmsg("dmg_stripe_inflate: after writing " STDu64 " bytes, "
838
0
                               "got error %d inflating stripe " STDu32 "\n",
839
0
                               size_so_far, zstat, index);
840
4
                inflateEnd(&strm);
841
4
                return CL_EFORMAT;
842
24
        }
843
7
        break;
844
24
    }
845
846
9
    if (strm.avail_out != sizeof(obuf)) {
847
2
        if (cli_writen(fd, obuf, sizeof(obuf) - strm.avail_out) == (size_t)-1) {
848
0
            cli_errmsg("dmg_stripe_inflate: failed write to output file\n");
849
0
            inflateEnd(&strm);
850
0
            return CL_EWRITE;
851
0
        }
852
2
    }
853
854
9
    inflateEnd(&strm);
855
9
    return CL_CLEAN;
856
9
}
857
858
/* Stripe handling: bzip block (type 0x80000006) */
859
static int dmg_stripe_bzip(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set)
860
0
{
861
0
    int ret               = CL_CLEAN;
862
0
    size_t off            = mish_set->stripes[index].dataOffset;
863
0
    size_t len            = mish_set->stripes[index].dataLength;
864
0
    uint64_t size_so_far  = 0;
865
0
    uint64_t expected_len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE;
866
0
#if HAVE_BZLIB_H
867
0
    int rc;
868
0
    bz_stream strm;
869
0
    uint8_t obuf[BUFSIZ];
870
0
#endif
871
872
0
    cli_dbgmsg("dmg_stripe_bzip: stripe " STDu32 " initial len " STDu64 " expected len " STDu64 "\n",
873
0
               index, (uint64_t)len, (uint64_t)expected_len);
874
875
0
#if HAVE_BZLIB_H
876
0
    memset(&strm, 0, sizeof(strm));
877
0
    strm.next_out  = (char *)obuf;
878
0
    strm.avail_out = sizeof(obuf);
879
0
    if (BZ2_bzDecompressInit(&strm, 0, 0) != BZ_OK) {
880
0
        cli_dbgmsg("dmg_stripe_bzip: bzDecompressInit failed\n");
881
0
        return CL_EOPEN;
882
0
    }
883
884
0
    do {
885
0
        if (size_so_far > expected_len) {
886
0
            cli_warnmsg("dmg_stripe_bzip: expected size exceeded!\n");
887
0
            ret = CL_EFORMAT;
888
0
            break;
889
0
        }
890
0
        if (strm.avail_in == 0) {
891
0
            size_t next_len = (len > sizeof(obuf)) ? sizeof(obuf) : len;
892
0
            dmg_bzipmsg("dmg_stripe_bzip: off %lu len %lu next_len %lu\n", off, len, next_len);
893
0
            strm.next_in = (void *)fmap_need_off_once(ctx->fmap, off, next_len);
894
0
            if (strm.next_in == NULL) {
895
0
                cli_dbgmsg("dmg_stripe_bzip: expected more stream\n");
896
0
                ret = CL_EMAP;
897
0
                break;
898
0
            }
899
0
            strm.avail_in = next_len;
900
0
            len -= next_len;
901
0
            off += next_len;
902
0
        }
903
904
0
        dmg_bzipmsg("dmg_stripe_bzip: before = strm.avail_in %lu strm.avail_out: %lu\n", strm.avail_in, strm.avail_out);
905
0
        rc = BZ2_bzDecompress(&strm);
906
0
        if ((rc != BZ_OK) && (rc != BZ_STREAM_END)) {
907
0
            cli_dbgmsg("dmg_stripe_bzip: decompress error: %d\n", rc);
908
0
            ret = CL_EFORMAT;
909
0
            break;
910
0
        }
911
912
0
        dmg_bzipmsg("dmg_stripe_bzip: after = strm.avail_in %lu strm.avail_out: %lu rc: %d %d\n",
913
0
                    strm.avail_in, strm.avail_out, rc, BZ_STREAM_END);
914
        /* Drain output buffer */
915
0
        if (!strm.avail_out) {
916
0
            size_t next_write = sizeof(obuf);
917
0
            do {
918
0
                size_so_far += next_write;
919
0
                dmg_bzipmsg("dmg_stripe_bzip: size_so_far: " STDu64 " next_write: %zu\n", size_so_far, next_write);
920
0
                if (size_so_far > expected_len) {
921
0
                    cli_warnmsg("dmg_stripe_bzip: expected size exceeded!\n");
922
0
                    ret = CL_EFORMAT;
923
0
                    rc  = BZ_DATA_ERROR; /* prevent stream end block */
924
0
                    break;
925
0
                }
926
927
0
                ret = cli_checklimits("dmg_stripe_bzip", ctx, (unsigned long)(size_so_far + sizeof(obuf)), 0, 0);
928
0
                if (ret != CL_CLEAN) {
929
0
                    break;
930
0
                }
931
932
0
                if (cli_writen(fd, obuf, next_write) != next_write) {
933
0
                    cli_dbgmsg("dmg_stripe_bzip: error writing to tmpfile\n");
934
0
                    ret = CL_EWRITE;
935
0
                    break;
936
0
                }
937
938
0
                strm.next_out  = (char *)obuf;
939
0
                strm.avail_out = sizeof(obuf);
940
941
0
                if (rc == BZ_OK)
942
0
                    rc = BZ2_bzDecompress(&strm);
943
0
                if ((rc != BZ_OK) && (rc != BZ_STREAM_END)) {
944
0
                    cli_dbgmsg("dmg_stripe_bzip: decompress error: %d\n", rc);
945
0
                    ret = CL_EFORMAT;
946
0
                    break;
947
0
                }
948
0
            } while (!strm.avail_out);
949
0
        }
950
        /* Stream end, so write data if any remains in buffer */
951
0
        if (rc == BZ_STREAM_END) {
952
0
            size_t next_write = sizeof(obuf) - strm.avail_out;
953
0
            size_so_far += next_write;
954
0
            dmg_bzipmsg("dmg_stripe_bzip: size_so_far: " STDu64 " next_write: %zu\n", size_so_far, next_write);
955
956
0
            ret = cli_checklimits("dmg_stripe_bzip", ctx, (unsigned long)(size_so_far + sizeof(obuf)), 0, 0);
957
0
            if (ret != CL_CLEAN) {
958
0
                break;
959
0
            }
960
961
0
            if (cli_writen(fd, obuf, next_write) != next_write) {
962
0
                cli_dbgmsg("dmg_stripe_bzip: error writing to tmpfile\n");
963
0
                ret = CL_EWRITE;
964
0
                break;
965
0
            }
966
967
0
            strm.next_out  = (char *)obuf;
968
0
            strm.avail_out = sizeof(obuf);
969
0
        }
970
0
    } while ((rc == BZ_OK) && (len > 0));
971
972
0
    BZ2_bzDecompressEnd(&strm);
973
0
#endif
974
975
0
    if (ret == CL_CLEAN) {
976
0
        if (size_so_far != expected_len) {
977
0
            cli_dbgmsg("dmg_stripe_bzip: output does not match expected size!\n");
978
0
        }
979
0
    }
980
0
    return ret;
981
0
}
982
983
/* Given mish data, reconstruct the partition details */
984
static int dmg_handle_mish(cli_ctx *ctx, unsigned int mishblocknum, char *dir,
985
                           uint64_t xmlOffset, struct dmg_mish_with_stripes *mish_set)
986
36
{
987
36
    struct dmg_block_data *blocklist = mish_set->stripes;
988
36
    uint64_t totalSectors            = 0;
989
36
    uint32_t i;
990
36
    unsigned long projected_size;
991
36
    int ret        = CL_CLEAN, ofd;
992
36
    uint8_t sorted = 1, writeable_data = 0;
993
36
    char outfile[PATH_MAX + 1];
994
995
    /* First loop, fix endian-ness and check if already sorted */
996
83
    for (i = 0; i < mish_set->mish->blockDataCount; i++) {
997
51
        blocklist[i].type = be32_to_host(blocklist[i].type);
998
        // blocklist[i].reserved = be32_to_host(blocklist[i].reserved);
999
51
        blocklist[i].startSector = be64_to_host(blocklist[i].startSector);
1000
51
        blocklist[i].sectorCount = be64_to_host(blocklist[i].sectorCount);
1001
51
        blocklist[i].dataOffset  = be64_to_host(blocklist[i].dataOffset);
1002
51
        blocklist[i].dataLength  = be64_to_host(blocklist[i].dataLength);
1003
51
        cli_dbgmsg("mish %u stripe " STDu32 " type " STDx32 " start " STDu64
1004
51
                   " count " STDu64 " source " STDu64 " length " STDu64 "\n",
1005
51
                   mishblocknum, i, blocklist[i].type, blocklist[i].startSector, blocklist[i].sectorCount,
1006
51
                   blocklist[i].dataOffset, blocklist[i].dataLength);
1007
51
        if ((blocklist[i].dataOffset > xmlOffset) ||
1008
51
            (blocklist[i].dataOffset + blocklist[i].dataLength > xmlOffset)) {
1009
4
            cli_dbgmsg("dmg_handle_mish: invalid stripe offset and/or length\n");
1010
4
            return CL_EFORMAT;
1011
4
        }
1012
47
        if ((i > 0) && sorted && (blocklist[i].startSector < blocklist[i - 1].startSector)) {
1013
2
            cli_dbgmsg("dmg_handle_mish: stripes not in order, will have to sort\n");
1014
2
            sorted = 0;
1015
2
        }
1016
47
        if (dmg_track_sectors(&totalSectors, &writeable_data, i, blocklist[i].type, blocklist[i].sectorCount)) {
1017
            /* reason was logged from dmg_track_sector_count */
1018
0
            return CL_EFORMAT;
1019
0
        }
1020
47
    }
1021
1022
32
    if (!sorted) {
1023
2
        cli_qsort(blocklist, mish_set->mish->blockDataCount, sizeof(struct dmg_block_data), cmp_mish_stripes);
1024
2
    }
1025
32
    cli_dbgmsg("dmg_handle_mish: stripes in order!\n");
1026
1027
    /* Size checks */
1028
32
    if ((writeable_data == 0) || (totalSectors == 0)) {
1029
17
        cli_dbgmsg("dmg_handle_mish: no data to output\n");
1030
17
        return CL_CLEAN;
1031
17
    } else if (totalSectors > (ULONG_MAX / DMG_SECTOR_SIZE)) {
1032
        /* cli_checklimits only takes unsigned long for now */
1033
0
        cli_warnmsg("dmg_handle_mish: mish block %u too big to handle (for now)", mishblocknum);
1034
0
        return CL_CLEAN;
1035
0
    }
1036
15
    projected_size = (unsigned long)(totalSectors * DMG_SECTOR_SIZE);
1037
15
    ret            = cli_checklimits("cli_scandmg", ctx, projected_size, 0, 0);
1038
15
    if (ret != CL_CLEAN) {
1039
        /* limits exceeded */
1040
1
        cli_dbgmsg("dmg_handle_mish: skipping block %u, limits exceeded\n", mishblocknum);
1041
1
        return ret;
1042
1
    }
1043
1044
    /* Prepare for file */
1045
14
    snprintf(outfile, sizeof(outfile) - 1, "%s" PATHSEP "dmg%02u", dir, mishblocknum);
1046
14
    outfile[sizeof(outfile) - 1] = '\0';
1047
14
    ofd                          = open(outfile, O_RDWR | O_CREAT | O_EXCL | O_TRUNC | O_BINARY, 0600);
1048
14
    if (ofd < 0) {
1049
0
        char err[128];
1050
0
        cli_errmsg("cli_scandmg: Can't create temporary file %s: %s\n",
1051
0
                   outfile, cli_strerror(errno, err, sizeof(err)));
1052
0
        return CL_ETMPFILE;
1053
0
    }
1054
14
    cli_dbgmsg("dmg_handle_mish: extracting block %u to %s\n", mishblocknum, outfile);
1055
1056
    /* Push data, stripe by stripe */
1057
37
    for (i = 0; i < mish_set->mish->blockDataCount && ret == CL_CLEAN; i++) {
1058
23
        switch (blocklist[i].type) {
1059
0
            case DMG_STRIPE_EMPTY:
1060
0
            case DMG_STRIPE_ZEROES:
1061
0
                ret = dmg_stripe_zeroes(ctx, ofd, i, mish_set);
1062
0
                break;
1063
0
            case DMG_STRIPE_STORED:
1064
0
                ret = dmg_stripe_store(ctx, ofd, i, mish_set);
1065
0
                break;
1066
1
            case DMG_STRIPE_ADC:
1067
1
                ret = dmg_stripe_adc(ctx, ofd, i, mish_set);
1068
1
                break;
1069
13
            case DMG_STRIPE_DEFLATE:
1070
13
                ret = dmg_stripe_inflate(ctx, ofd, i, mish_set);
1071
13
                break;
1072
0
            case DMG_STRIPE_BZ:
1073
0
                ret = dmg_stripe_bzip(ctx, ofd, i, mish_set);
1074
0
                break;
1075
0
            case DMG_STRIPE_SKIP:
1076
9
            case DMG_STRIPE_END:
1077
9
            default:
1078
9
                cli_dbgmsg("dmg_handle_mish: stripe " STDu32 ", skipped\n", i);
1079
9
                break;
1080
23
        }
1081
23
    }
1082
1083
    /* If okay so far, scan rebuilt partition */
1084
14
    if (ret == CL_CLEAN) {
1085
        /* Have to keep partition typing separate */
1086
9
        ret = cli_magic_scan_desc_type(ofd, outfile, ctx, CL_TYPE_PART_ANY, NULL, LAYER_ATTRIBUTES_NONE);
1087
9
    }
1088
1089
14
    close(ofd);
1090
14
    if (!ctx->engine->keeptmp)
1091
14
        if (cli_unlink(outfile)) return CL_EUNLINK;
1092
1093
14
    return ret;
1094
14
}
1095
1096
static int dmg_extract_xml(cli_ctx *ctx, char *dir, struct dmg_koly_block *hdr)
1097
0
{
1098
0
    char *xmlfile;
1099
0
    const char *outdata;
1100
0
    size_t namelen, nread;
1101
0
    int ofd;
1102
1103
    /* Prep TOC XML for output */
1104
0
    outdata = fmap_need_off_once_len(ctx->fmap, hdr->xmlOffset, hdr->xmlLength, &nread);
1105
0
    if (!outdata || (nread != hdr->xmlLength)) {
1106
0
        cli_errmsg("cli_scandmg: Failed getting XML from map, len " STDu64 "\n", hdr->xmlLength);
1107
0
        return CL_EMAP;
1108
0
    }
1109
1110
0
    namelen = strlen(dir) + 1 + 7 + 1;
1111
0
    if (!(xmlfile = cli_malloc(namelen))) {
1112
0
        return CL_EMEM;
1113
0
    }
1114
0
    snprintf(xmlfile, namelen, "%s" PATHSEP "toc.xml", dir);
1115
0
    cli_dbgmsg("cli_scandmg: Extracting XML as %s\n", xmlfile);
1116
1117
    /* Write out TOC XML */
1118
0
    if ((ofd = open(xmlfile, O_CREAT | O_RDWR | O_EXCL | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
1119
0
        char err[128];
1120
0
        cli_errmsg("cli_scandmg: Can't create temporary file %s: %s\n",
1121
0
                   xmlfile, cli_strerror(errno, err, sizeof(err)));
1122
0
        free(xmlfile);
1123
0
        return CL_ETMPFILE;
1124
0
    }
1125
1126
0
    if ((uint64_t)cli_writen(ofd, outdata, (size_t)hdr->xmlLength) != hdr->xmlLength) {
1127
0
        cli_errmsg("cli_scandmg: Not all bytes written!\n");
1128
0
        close(ofd);
1129
0
        free(xmlfile);
1130
0
        return CL_EWRITE;
1131
0
    }
1132
1133
0
    close(ofd);
1134
0
    free(xmlfile);
1135
0
    return CL_SUCCESS;
1136
0
}