~ubuntu-branches/ubuntu/saucy/clamav/saucy-backports

« back to all changes in this revision

Viewing changes to libclamav/gpt.c

  • Committer: Package Import Robot
  • Author(s): Scott Kitterman
  • Date: 2014-07-15 01:08:10 UTC
  • mfrom: (0.35.47 sid)
  • Revision ID: package-import@ubuntu.com-20140715010810-ru66ek4fun2iseba
Tags: 0.98.4+dfsg-2~ubuntu13.10.1
No-change backport to saucy (LP: #1341962)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2014 Sourcefire, Inc.
 
3
 *
 
4
 *  Authors: Kevin Lin <klin@sourcefire.com>
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License version 2 as
 
8
 *  published by the Free Software Foundation.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
18
 *  MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
#if HAVE_CONFIG_H
 
22
#include "clamav-config.h"
 
23
#endif
 
24
 
 
25
#include <stdio.h>
 
26
#include <errno.h>
 
27
#if HAVE_STRING_H
 
28
#include <string.h>
 
29
#endif
 
30
#include <ctype.h>
 
31
#include <fcntl.h>
 
32
#include <zlib.h>
 
33
 
 
34
#include <openssl/ssl.h>
 
35
#include <openssl/err.h>
 
36
#include "libclamav/crypto.h"
 
37
 
 
38
#include "cltypes.h"
 
39
#include "others.h"
 
40
#include "gpt.h"
 
41
#include "mbr.h"
 
42
#include "str.h"
 
43
#include "prtn_intxn.h"
 
44
#include "scanners.h"
 
45
#include "dconf.h"
 
46
 
 
47
//#define DEBUG_GPT_PARSE
 
48
//#define DEBUG_GPT_PRINT
 
49
 
 
50
#ifdef DEBUG_GPT_PARSE
 
51
#  define gpt_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
 
52
#else
 
53
#  define gpt_parsemsg(...) ;
 
54
#endif
 
55
 
 
56
#ifdef DEBUG_GPT_PRINT
 
57
#  define gpt_printmsg(...) cli_dbgmsg( __VA_ARGS__)
 
58
#else
 
59
#  define gpt_printmsg(...) ;
 
60
#endif
 
61
 
 
62
enum GPT_SCANSTATE {
 
63
    INVALID,
 
64
    PRIMARY_ONLY,
 
65
    SECONDARY_ONLY,
 
66
    BOTH
 
67
};
 
68
 
 
69
static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
 
70
static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
 
71
static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize);
 
72
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize);
 
73
static void gpt_printName(uint16_t name[], const char* msg);
 
74
static void gpt_printGUID(uint8_t GUID[], const char* msg);
 
75
static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
 
76
 
 
77
/* returns 0 on failing to detect sectorsize */
 
78
size_t gpt_detect_size(fmap_t *map)
 
79
{
 
80
    unsigned char *buff;
 
81
 
 
82
    buff = (unsigned char*)fmap_need_off_once(map, 512, 8);
 
83
    if (!buff) return 0;
 
84
    if (0 == strncmp(buff, GPT_SIGNATURE_STR, 8))
 
85
        return 512;
 
86
 
 
87
    buff = (unsigned char*)fmap_need_off_once(map, 1024, 8);
 
88
    if (!buff) return 0;
 
89
    if (0 == strncmp(buff, GPT_SIGNATURE_STR, 8))
 
90
        return 1024;
 
91
 
 
92
    buff = (unsigned char*)fmap_need_off_once(map, 2048, 8);
 
93
    if (!buff) return 0;
 
94
    if (0 == strncmp(buff, GPT_SIGNATURE_STR, 8))
 
95
        return 2048;
 
96
 
 
97
    buff = (unsigned char*)fmap_need_off_once(map, 4096, 8);
 
98
    if (!buff) return 0;
 
99
    if (0 == strncmp(buff, GPT_SIGNATURE_STR, 8))
 
100
        return 4096;
 
101
 
 
102
    return 0;
 
103
}
 
104
 
 
105
/* attempts to detect sector size is input as 0 */
 
106
int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
 
107
{
 
108
    struct gpt_header phdr, shdr;
 
109
    enum GPT_SCANSTATE state = INVALID;
 
110
    int ret = CL_CLEAN, detection = CL_CLEAN;
 
111
    size_t maplen;
 
112
    off_t pos = 0;
 
113
 
 
114
    gpt_parsemsg("The beginning of something big: GPT parsing\n");
 
115
 
 
116
    if (!ctx || !ctx->fmap) {
 
117
        cli_errmsg("cli_scangpt: Invalid context\n");
 
118
        return CL_ENULLARG;
 
119
    }
 
120
 
 
121
    /* sector size calculatation */
 
122
    if (sectorsize == 0) {
 
123
        sectorsize = gpt_detect_size((*ctx->fmap));
 
124
        cli_dbgmsg("cli_scangpt: detected %lu sector size\n", (unsigned long)sectorsize);
 
125
    }
 
126
    if (sectorsize == 0) {
 
127
        cli_errmsg("cli_scangpt: could not detemine sector size\n");
 
128
        return CL_EFORMAT;
 
129
    }
 
130
 
 
131
    /* size of total file must be a multiple of the sector size */
 
132
    maplen = (*ctx->fmap)->real_len;
 
133
    if ((maplen % sectorsize) != 0) {
 
134
        cli_dbgmsg("cli_scangpt: File sized %lu is not a multiple of sector size %lu\n",
 
135
                   (unsigned long)maplen, (unsigned long)sectorsize);
 
136
        return CL_EFORMAT;
 
137
    }
 
138
 
 
139
    /* check the protective mbr */
 
140
    ret = gpt_check_mbr(ctx, sectorsize);
 
141
    if (ret != CL_CLEAN) {
 
142
        if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
143
            detection = CL_VIRUS;
 
144
        else
 
145
            return ret;
 
146
    }
 
147
 
 
148
    pos = GPT_PRIMARY_HDR_LBA * sectorsize; /* sector 1 (second sector) is the primary gpt header */
 
149
 
 
150
    /* read primary gpt header */
 
151
    cli_dbgmsg("cli_scangpt: Using primary GPT header\n");
 
152
    if (fmap_readn(*ctx->fmap, &phdr, pos, sizeof(phdr)) != sizeof(phdr)) {
 
153
        cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
 
154
        return CL_EFORMAT;
 
155
    }
 
156
 
 
157
    pos = maplen - sectorsize; /* last sector is the secondary gpt header */
 
158
 
 
159
    if (gpt_validate_header(ctx, phdr, sectorsize)) {
 
160
        cli_dbgmsg("cli_scangpt: Primary GPT header is invalid\n");
 
161
        cli_dbgmsg("cli_scangpt: Using secondary GPT header\n");
 
162
 
 
163
        state = SECONDARY_ONLY;
 
164
 
 
165
        /* read secondary gpt header */
 
166
        if (fmap_readn(*ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
 
167
            cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
 
168
            return CL_EFORMAT;
 
169
        }
 
170
 
 
171
        if (gpt_validate_header(ctx, shdr, sectorsize)) {
 
172
            cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
 
173
            cli_dbgmsg("cli_scangpt: Disk is unusable\n");
 
174
            return CL_EFORMAT;
 
175
        }
 
176
    }
 
177
    else {
 
178
        cli_dbgmsg("cli_scangpt: Checking secondary GPT header\n");
 
179
 
 
180
        state = PRIMARY_ONLY;
 
181
 
 
182
        /* check validity of secondary header; still using the primary */
 
183
        if (fmap_readn(*ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
 
184
            cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
 
185
        }
 
186
        else if (gpt_validate_header(ctx, shdr, sectorsize)) {
 
187
            cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
 
188
        }
 
189
        /* check that the two partition table crc32 checksum match, 
 
190
         * may want a different hashing function */
 
191
        else if (phdr.tableCRC32 != shdr.tableCRC32){
 
192
            cli_dbgmsg("cli_scangpt: Primary and secondary GPT header table CRC32 differ\n");
 
193
            cli_dbgmsg("cli_scangpt: Set to scan primary and secondary partition tables\n");
 
194
 
 
195
            state = BOTH;
 
196
        }
 
197
        else {
 
198
            cli_dbgmsg("cli_scangpt: Secondary GPT header check OK\n");
 
199
        }
 
200
    }
 
201
 
 
202
    /* check that the partition table has no intersections - HEURISTICS */
 
203
    if ((ctx->options & CL_SCAN_PARTITION_INTXN) && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
 
204
        ret = gpt_prtn_intxn(ctx, phdr, sectorsize);
 
205
        if (ret != CL_CLEAN) {
 
206
            if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
207
                detection = CL_VIRUS;
 
208
            else
 
209
                return ret;
 
210
        }
 
211
        ret = gpt_prtn_intxn(ctx, shdr, sectorsize);
 
212
        if (ret != CL_CLEAN) {
 
213
            if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
214
                detection = CL_VIRUS;
 
215
            else
 
216
                return ret;
 
217
        }
 
218
    }
 
219
 
 
220
    /* scanning partitions */
 
221
    switch (state) {
 
222
    case PRIMARY_ONLY:
 
223
        cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions only\n");
 
224
        ret = gpt_scan_partitions(ctx, phdr, sectorsize);
 
225
        if (ret != CL_CLEAN) {
 
226
            if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
227
                detection = CL_VIRUS;
 
228
            else
 
229
                return ret;
 
230
        }
 
231
        break;
 
232
    case SECONDARY_ONLY:
 
233
        cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions only\n");
 
234
        ret = gpt_scan_partitions(ctx, shdr, sectorsize);
 
235
        if (ret != CL_CLEAN) {
 
236
            if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
237
                detection = CL_VIRUS;
 
238
            else
 
239
                return ret;
 
240
        }
 
241
        break;
 
242
    case BOTH:
 
243
        cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions\n");
 
244
        ret = gpt_scan_partitions(ctx, phdr, sectorsize);
 
245
        if (ret != CL_CLEAN) {
 
246
            if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
247
                detection = CL_VIRUS;
 
248
            else
 
249
                return ret;
 
250
        }
 
251
        cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions\n");
 
252
        ret = gpt_scan_partitions(ctx, shdr, sectorsize);
 
253
        if (ret != CL_CLEAN) {
 
254
            if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
255
                detection = CL_VIRUS;
 
256
            else
 
257
                return ret;
 
258
        }
 
259
        break;
 
260
    default:
 
261
        cli_dbgmsg("cli_scangpt: State is invalid\n");
 
262
    }
 
263
 
 
264
    return detection;
 
265
}
 
266
 
 
267
static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
 
268
{
 
269
    struct gpt_partition_entry gpe;
 
270
    int ret = CL_CLEAN, detection = CL_CLEAN;
 
271
    size_t maplen, part_size = 0;
 
272
    off_t pos = 0, part_off = 0;
 
273
    unsigned i = 0, j = 0;
 
274
    uint32_t max_prtns = 0;
 
275
 
 
276
    /* convert endian to host */
 
277
    hdr.signature = be64_to_host(hdr.signature);
 
278
    hdr.revision = be32_to_host(hdr.revision);
 
279
    hdr.headerSize = le32_to_host(hdr.headerSize);
 
280
    hdr.headerCRC32 = le32_to_host(hdr.headerCRC32);
 
281
    hdr.reserved = le32_to_host(hdr.reserved);
 
282
    hdr.currentLBA = le64_to_host(hdr.currentLBA);
 
283
    hdr.backupLBA = le64_to_host(hdr.backupLBA);
 
284
    hdr.firstUsableLBA = le64_to_host(hdr.firstUsableLBA);
 
285
    hdr.lastUsableLBA = le64_to_host(hdr.lastUsableLBA);
 
286
    hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA);
 
287
    hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
 
288
    hdr.tableEntrySize = le32_to_host(hdr.tableEntrySize);
 
289
    hdr.tableCRC32 = le32_to_host(hdr.tableCRC32);
 
290
 
 
291
    /* print header info for the debug */
 
292
    cli_dbgmsg("GPT Header:\n");
 
293
    cli_dbgmsg("Signature: 0x%llx\n", hdr.signature);
 
294
    cli_dbgmsg("Revision: %x\n", hdr.revision);
 
295
    gpt_printGUID(hdr.DiskGUID, "DISK GUID");
 
296
    cli_dbgmsg("Partition Entry Count: %u\n", hdr.tableNumEntries);
 
297
    cli_dbgmsg("Partition Entry Size: %u\n", hdr.tableEntrySize);
 
298
 
 
299
    maplen = (*ctx->fmap)->real_len;
 
300
 
 
301
    /* check engine maxpartitions limit */
 
302
    if (hdr.tableNumEntries < ctx->engine->maxpartitions) {
 
303
        max_prtns = hdr.tableNumEntries;
 
304
    }
 
305
    else {
 
306
        max_prtns = ctx->engine->maxpartitions;
 
307
    }
 
308
 
 
309
    /* use the partition tables to pass partitions to cli_map_scan */
 
310
    pos = hdr.tableStartLBA * sectorsize;
 
311
    for (i = 0; i < max_prtns; ++i) {
 
312
        /* read in partition entry */
 
313
        if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
 
314
            cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
 
315
            return CL_EFORMAT;
 
316
        }
 
317
 
 
318
        /* convert the endian to host */
 
319
        gpe.firstLBA = le64_to_host(gpe.firstLBA);
 
320
        gpe.lastLBA = le64_to_host(gpe.lastLBA);
 
321
        gpe.attributes = le64_to_host(gpe.attributes);
 
322
        for (j = 0; j < 36; ++j) {
 
323
            gpe.name[i] = le16_to_host(gpe.name[i]);
 
324
        }
 
325
 
 
326
        /* check that partition is not empty and within a valid location */
 
327
        if (gpe.firstLBA == 0) {
 
328
            /* empty partition, invalid */
 
329
        }
 
330
        else if ((gpe.firstLBA > gpe.lastLBA) ||
 
331
                 (gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) {
 
332
            cli_dbgmsg("cli_scangpt: GPT partition exists outside specified bounds\n");
 
333
            gpt_parsemsg("%llu < %llu, %llu > %llu\n", gpe.firstLBA, hdr.firstUsableLBA,
 
334
                         gpe.lastLBA, hdr.lastUsableLBA);
 
335
            /* partition exists outside bounds specified by header or invalid */
 
336
        }
 
337
        else if (((gpe.lastLBA+1) * sectorsize) > maplen) {
 
338
            /* partition exists outside bounds of the file map */
 
339
        }
 
340
        else {
 
341
            /* print partition entry data for debug */
 
342
            cli_dbgmsg("GPT Partition Entry %u:\n", i);
 
343
            gpt_printName(gpe.name, "Name");
 
344
            gpt_printGUID(gpe.typeGUID, "Type GUID");
 
345
            gpt_printGUID(gpe.uniqueGUID, "Unique GUID");
 
346
            cli_dbgmsg("Attributes: %llx\n", gpe.attributes);
 
347
            cli_dbgmsg("Blocks: [%llu(%llu) -> %llu(%llu)]\n",
 
348
                       gpe.firstLBA, (gpe.firstLBA * sectorsize), 
 
349
                       gpe.lastLBA, ((gpe.lastLBA+1) * sectorsize));
 
350
 
 
351
            /* send the partition to cli_map_scan */
 
352
            part_off = gpe.firstLBA * sectorsize;
 
353
            part_size = (gpe.lastLBA - gpe.firstLBA + 1) * sectorsize;
 
354
            ret = cli_map_scan(*ctx->fmap, part_off, part_size, ctx, CL_TYPE_PART_ANY);
 
355
            if (ret != CL_CLEAN) {
 
356
                if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
 
357
                    detection = CL_VIRUS;
 
358
                else
 
359
                    return ret;
 
360
            }
 
361
        }
 
362
 
 
363
        /* increment the offsets to next partition entry */
 
364
        pos += hdr.tableEntrySize;
 
365
    }
 
366
 
 
367
    if (i >= ctx->engine->maxpartitions) {
 
368
        cli_dbgmsg("cli_scangpt: max partitions reached\n");
 
369
    }
 
370
 
 
371
    return detection;
 
372
}
 
373
 
 
374
static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
 
375
{
 
376
    uint32_t crc32_calc, crc32_ref;
 
377
    uint64_t tableLastLBA, lastLBA;
 
378
    size_t maplen, ptable_start, ptable_len;
 
379
    unsigned char *ptable;
 
380
 
 
381
    maplen = (*ctx->fmap)->real_len;
 
382
 
 
383
    /* checking header crc32 checksum */
 
384
    crc32_ref = le32_to_host(hdr.headerCRC32);
 
385
    hdr.headerCRC32 = 0; /* checksum is calculated with field = 0 */
 
386
    crc32_calc = crc32(0, (unsigned char*)&hdr, sizeof(hdr));
 
387
    if (crc32_calc != crc32_ref) {
 
388
        cli_dbgmsg("cli_scangpt: GPT header checksum mismatch\n");
 
389
        gpt_parsemsg("%x != %x\n", crc32_calc, crc32_ref);
 
390
        return CL_EFORMAT;
 
391
    }
 
392
 
 
393
    /* convert endian to host to check partition table */
 
394
    hdr.signature = be64_to_host(hdr.signature);
 
395
    hdr.revision = be32_to_host(hdr.revision);
 
396
    hdr.headerSize = le32_to_host(hdr.headerSize);
 
397
    hdr.headerCRC32 = crc32_ref;
 
398
    hdr.reserved = le32_to_host(hdr.reserved);
 
399
    hdr.currentLBA = le64_to_host(hdr.currentLBA);
 
400
    hdr.backupLBA = le64_to_host(hdr.backupLBA);
 
401
    hdr.firstUsableLBA = le64_to_host(hdr.firstUsableLBA);
 
402
    hdr.lastUsableLBA = le64_to_host(hdr.lastUsableLBA);
 
403
    hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA);
 
404
    hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
 
405
    hdr.tableEntrySize = le32_to_host(hdr.tableEntrySize);
 
406
    hdr.tableCRC32 = le32_to_host(hdr.tableCRC32);;
 
407
 
 
408
    ptable_start = hdr.tableStartLBA * sectorsize;
 
409
    ptable_len = hdr.tableNumEntries * hdr.tableEntrySize;
 
410
    tableLastLBA = (hdr.tableStartLBA + (ptable_len / sectorsize)) - 1;
 
411
    lastLBA = (maplen / sectorsize) - 1;
 
412
 
 
413
    /** HEADER CHECKS **/
 
414
    gpt_printSectors(ctx, sectorsize);
 
415
 
 
416
    /* check signature */
 
417
    if (hdr.signature != GPT_SIGNATURE) {
 
418
        cli_dbgmsg("cli_scangpt: Invalid GPT header signature %llx\n",
 
419
                   hdr.signature);
 
420
        return CL_EFORMAT;
 
421
    }
 
422
 
 
423
    /* check header size */
 
424
    if (hdr.headerSize != sizeof(hdr)) {
 
425
        cli_dbgmsg("cli_scangpt: GPT header size does not match stated size\n");
 
426
        return CL_EFORMAT;
 
427
    }
 
428
 
 
429
    /* check reserved value == 0 */
 
430
    if (hdr.reserved != GPT_HDR_RESERVED) {
 
431
        cli_dbgmsg("cli_scangpt: GPT header reserved is not expected value\n");
 
432
        return CL_EFORMAT;
 
433
    }
 
434
 
 
435
    /* check that sectors are in a valid configuration */
 
436
    if (!((hdr.currentLBA == GPT_PRIMARY_HDR_LBA && hdr.backupLBA == lastLBA) ||
 
437
          (hdr.currentLBA == lastLBA && hdr.backupLBA == GPT_PRIMARY_HDR_LBA))) {
 
438
        cli_dbgmsg("cli_scangpt: GPT secondary header is not last LBA\n");
 
439
        return CL_EFORMAT;
 
440
    }
 
441
    if (hdr.firstUsableLBA > hdr.lastUsableLBA) {
 
442
        cli_dbgmsg("cli_scangpt: GPT first usable sectors is after last usable sector\n");
 
443
        return CL_EFORMAT;
 
444
    }
 
445
    if (hdr.firstUsableLBA <= GPT_PRIMARY_HDR_LBA || hdr.lastUsableLBA >= lastLBA) {
 
446
        cli_dbgmsg("cli_scangpt: GPT usable sectors intersects header sector\n");
 
447
        return CL_EFORMAT;
 
448
    }
 
449
    if ((hdr.tableStartLBA <= hdr.firstUsableLBA && tableLastLBA >= hdr.firstUsableLBA) ||
 
450
        (hdr.tableStartLBA >= hdr.firstUsableLBA && hdr.tableStartLBA <= hdr.lastUsableLBA)) {
 
451
        cli_dbgmsg("cli_scangpt: GPT usable sectors intersects partition table\n");
 
452
        return CL_EFORMAT;
 
453
    }
 
454
    if (hdr.tableStartLBA <= GPT_PRIMARY_HDR_LBA || tableLastLBA >= lastLBA) {
 
455
        cli_dbgmsg("cli_scangpt: GPT partition table intersects header sector\n");
 
456
        return CL_EFORMAT;
 
457
    }
 
458
 
 
459
    /* check that valid table entry size */
 
460
    if (hdr.tableEntrySize != sizeof(struct gpt_partition_entry)) {
 
461
        cli_dbgmsg("cli_scangpt: cannot parse gpt with partition entry sized %u\n",
 
462
                   hdr.tableEntrySize);
 
463
        return CL_EFORMAT;
 
464
    }
 
465
 
 
466
 
 
467
    /* check valid table */
 
468
    if ((ptable_start + ptable_len) > maplen) {
 
469
        cli_dbgmsg("cli_scangpt: GPT partition table extends over fmap limit\n");
 
470
        return CL_EFORMAT;
 
471
    }
 
472
 
 
473
    /** END HEADER CHECKS **/
 
474
 
 
475
    /* checking partition table crc32 checksum */
 
476
    ptable = (unsigned char*)fmap_need_off_once((*ctx->fmap), ptable_start, ptable_len);
 
477
    crc32_calc = crc32(0, ptable, ptable_len);
 
478
    if (crc32_calc != hdr.tableCRC32) {
 
479
        cli_dbgmsg("cli_scangpt: GPT partition table checksum mismatch\n");
 
480
        gpt_parsemsg("%x != %x\n", crc32_calc, hdr.tableCRC32);
 
481
        return CL_EFORMAT;
 
482
    }
 
483
 
 
484
    return CL_SUCCESS;
 
485
}
 
486
 
 
487
static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
 
488
{
 
489
    struct mbr_boot_record pmbr;
 
490
    off_t pos = 0, mbr_base = 0;
 
491
    int ret = CL_CLEAN;
 
492
    unsigned i = 0;
 
493
 
 
494
    /* read the mbr */
 
495
    mbr_base = sectorsize - sizeof(struct mbr_boot_record);
 
496
    pos = (MBR_SECTOR * sectorsize) + mbr_base;
 
497
 
 
498
    if (fmap_readn(*ctx->fmap, &pmbr, pos, sizeof(pmbr)) != sizeof(pmbr)) {
 
499
        cli_dbgmsg("cli_scangpt: Invalid primary MBR header\n");
 
500
        return CL_EFORMAT;
 
501
    }
 
502
 
 
503
    /* convert mbr */
 
504
    mbr_convert_to_host(&pmbr);
 
505
 
 
506
    /* check the protective mbr - warning */
 
507
    if (pmbr.entries[0].type == MBR_PROTECTIVE) {
 
508
        /* check the efi partition matches the gpt spec */
 
509
        if (pmbr.entries[0].firstLBA != GPT_PRIMARY_HDR_LBA) {
 
510
            cli_warnmsg("cli_scangpt: protective MBR first LBA is incorrect %u\n",
 
511
                        pmbr.entries[0].firstLBA);
 
512
        }
 
513
 
 
514
        /* other entries are empty */
 
515
        for (i = 1; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
 
516
            if (pmbr.entries[i].type != MBR_EMPTY) {
 
517
                cli_warnmsg("cli_scangpt: protective MBR has non-empty partition\n");
 
518
                break;
 
519
            }
 
520
        }
 
521
    }
 
522
    else if (pmbr.entries[0].type == MBR_HYBRID) {
 
523
        /* hybrid mbr detected */
 
524
        cli_warnmsg("cli_scangpt: detected a hybrid MBR\n");
 
525
    }
 
526
    else {
 
527
        /* non-protective mbr detected */
 
528
        cli_warnmsg("cli_scangpt: detected a non-protective MBR\n");
 
529
    }
 
530
 
 
531
    /* scan the bootloader segment - pushed to scanning mbr */
 
532
    /* check if MBR size matches GPT size */
 
533
    /* check if the MBR and GPT partitions align - heuristic */
 
534
    /* scan the MBR partitions - additional scans */
 
535
 
 
536
    return ret;
 
537
}
 
538
 
 
539
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize)
 
540
{
 
541
#ifdef DEBUG_GPT_PARSE
 
542
    struct gpt_header phdr, shdr;
 
543
    off_t ppos = 0, spos = 0;
 
544
    size_t pptable_len, sptable_len, maplen;
 
545
    uint64_t ptableLastLBA, stableLastLBA;
 
546
 
 
547
    /* sector size calculation */
 
548
    sectorsize = GPT_DEFAULT_SECTOR_SIZE;
 
549
 
 
550
    maplen = (*ctx->fmap)->real_len;
 
551
 
 
552
    ppos = 1 * sectorsize; /* sector 1 (second sector) is the primary gpt header */
 
553
    spos = maplen - sectorsize; /* last sector is the secondary gpt header */
 
554
 
 
555
    /* read in the primary and secondary gpt headers */
 
556
    if (fmap_readn(*ctx->fmap, &phdr, ppos, sizeof(phdr)) != sizeof(phdr)) {
 
557
        cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
 
558
        return;
 
559
    }
 
560
    if (fmap_readn(*ctx->fmap, &shdr, spos, sizeof(shdr)) != sizeof(shdr)) {
 
561
        cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
 
562
        return;
 
563
    }
 
564
 
 
565
    pptable_len = phdr.tableNumEntries * phdr.tableEntrySize;
 
566
    sptable_len = shdr.tableNumEntries * shdr.tableEntrySize;
 
567
    ptableLastLBA = (phdr.tableStartLBA + (pptable_len / sectorsize)) - 1;
 
568
    stableLastLBA = (shdr.tableStartLBA + (sptable_len / sectorsize)) - 1;
 
569
 
 
570
    gpt_parsemsg("0: MBR\n");
 
571
    gpt_parsemsg("%llu: Primary GPT Header\n", phdr.currentLBA);
 
572
    gpt_parsemsg("%llu-%llu: Primary GPT Partition Table\n", phdr.tableStartLBA, ptableLastLBA);
 
573
    gpt_parsemsg("%llu-%llu: Usuable LBAs\n", phdr.firstUsableLBA, phdr.lastUsableLBA);
 
574
    gpt_parsemsg("%llu-%llu: Secondary GPT Partition Table\n", shdr.tableStartLBA, stableLastLBA);
 
575
    gpt_parsemsg("%llu: Secondary GPT Header\n", phdr.backupLBA);
 
576
#else
 
577
    return;
 
578
#endif
 
579
}
 
580
 
 
581
static void gpt_printName(uint16_t name[], const char* msg)
 
582
{
 
583
    char *namestr;
 
584
 
 
585
    namestr = (char*)cli_utf16toascii((char*)name, 72);
 
586
    cli_dbgmsg("%s: %s\n", msg, namestr);
 
587
 
 
588
    free(namestr);
 
589
}
 
590
 
 
591
static void gpt_printGUID(uint8_t GUID[], const char* msg)
 
592
{
 
593
    unsigned i;
 
594
    char hexstr[64], tmpstr[64];
 
595
 
 
596
    hexstr[0] = '\0';
 
597
    tmpstr[0] = '\0';
 
598
    for (i = 0; i < 16; ++i) {
 
599
        gpt_printmsg("%x\n", GUID[i]);
 
600
        if (i == 3 || i == 5 || i == 7 || i == 9) {
 
601
            snprintf(hexstr, 64, "%s%02x-", tmpstr, GUID[i]);
 
602
            gpt_printmsg("%s\n", hexstr);
 
603
        }
 
604
        else {
 
605
            snprintf(hexstr, 64, "%s%02x", tmpstr, GUID[i]);
 
606
            gpt_printmsg("%s\n", hexstr);
 
607
        }
 
608
        strncpy(tmpstr, hexstr, 64);
 
609
    }
 
610
    cli_dbgmsg("%s: %s\n", msg, hexstr);
 
611
}
 
612
 
 
613
static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
 
614
{
 
615
    prtn_intxn_list_t prtncheck;
 
616
    struct gpt_partition_entry gpe;
 
617
    unsigned i, pitxn;
 
618
    int ret = CL_CLEAN, tmp = CL_CLEAN;
 
619
    off_t pos;
 
620
    size_t maplen;
 
621
    uint32_t max_prtns = 0;
 
622
 
 
623
    maplen = (*ctx->fmap)->real_len;
 
624
 
 
625
    /* convert endian to host to check partition table */
 
626
    hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA);
 
627
    hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
 
628
 
 
629
    prtn_intxn_list_init(&prtncheck);    
 
630
 
 
631
    /* check engine maxpartitions limit */
 
632
    if (hdr.tableNumEntries < ctx->engine->maxpartitions) {
 
633
        max_prtns = hdr.tableNumEntries;
 
634
    }
 
635
    else {
 
636
        max_prtns = ctx->engine->maxpartitions;
 
637
    }
 
638
 
 
639
    pos = hdr.tableStartLBA * sectorsize;
 
640
    for (i = 0; i < max_prtns; ++i) {
 
641
        /* read in partition entry */
 
642
        if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
 
643
            cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
 
644
            prtn_intxn_list_free(&prtncheck);
 
645
            return CL_EFORMAT;
 
646
        }
 
647
 
 
648
        /* convert the endian to host */
 
649
        gpe.firstLBA = le64_to_host(gpe.firstLBA);
 
650
        gpe.lastLBA = le64_to_host(gpe.lastLBA);
 
651
 
 
652
        if (gpe.firstLBA == 0) {
 
653
            /* empty partition, invalid */
 
654
        }
 
655
        else if ((gpe.firstLBA > gpe.lastLBA) ||
 
656
                 (gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) {
 
657
            /* partition exists outside bounds specified by header or invalid */
 
658
        }
 
659
        else if (((gpe.lastLBA+1) * sectorsize) > maplen) {
 
660
            /* partition exists outside bounds of the file map */
 
661
        }
 
662
        else {
 
663
            tmp = prtn_intxn_list_check(&prtncheck, &pitxn, gpe.firstLBA, gpe.lastLBA - gpe.firstLBA + 1);
 
664
            if (tmp != CL_CLEAN) {
 
665
                if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) {
 
666
                    cli_dbgmsg("cli_scangpt: detected intersection with partitions "
 
667
                               "[%u, %u]\n", pitxn, i);
 
668
                    cli_append_virus(ctx, PRTN_INTXN_DETECTION);
 
669
                    ret = tmp;
 
670
                    tmp = 0;
 
671
                }
 
672
                else if (tmp == CL_VIRUS) {
 
673
                    cli_dbgmsg("cli_scangpt: detected intersection with partitions "
 
674
                               "[%u, %u]\n", pitxn, i);
 
675
                    cli_append_virus(ctx, PRTN_INTXN_DETECTION);
 
676
                    prtn_intxn_list_free(&prtncheck);
 
677
                    return CL_VIRUS;
 
678
                }
 
679
                else {
 
680
                    prtn_intxn_list_free(&prtncheck);
 
681
                    return tmp;
 
682
                }
 
683
            }
 
684
        }
 
685
 
 
686
        /* increment the offsets to next partition entry */
 
687
        pos += hdr.tableEntrySize;
 
688
    }
 
689
 
 
690
    prtn_intxn_list_free(&prtncheck);
 
691
    return ret;
 
692
}