libclamav/gpt.c
fce85dd7
 /*
e1cbc270
  *  Copyright (C) 2014-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
fce85dd7
  *
  *  Authors: Kevin Lin <klin@sourcefire.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <errno.h>
 #if HAVE_STRING_H
 #include <string.h>
 #endif
 #include <ctype.h>
 #include <fcntl.h>
 #include <zlib.h>
 
60d8d2c3
 #include "clamav.h"
fce85dd7
 #include "others.h"
 #include "gpt.h"
b374521a
 #include "mbr.h"
e731850d
 #include "str.h"
e3646995
 #include "prtn_intxn.h"
fce85dd7
 #include "scanners.h"
558c673e
 #include "dconf.h"
fce85dd7
 
 //#define DEBUG_GPT_PARSE
 //#define DEBUG_GPT_PRINT
 
 #ifdef DEBUG_GPT_PARSE
288057e9
 #define gpt_parsemsg(...) cli_dbgmsg(__VA_ARGS__)
fce85dd7
 #else
288057e9
 #define gpt_parsemsg(...) ;
fce85dd7
 #endif
 
 #ifdef DEBUG_GPT_PRINT
288057e9
 #define gpt_printmsg(...) cli_dbgmsg(__VA_ARGS__)
fce85dd7
 #else
288057e9
 #define gpt_printmsg(...) ;
fce85dd7
 #endif
 
e3646995
 enum GPT_SCANSTATE {
     INVALID,
     PRIMARY_ONLY,
     SECONDARY_ONLY,
     BOTH
 };
 
 static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
 static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
b374521a
 static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize);
e3646995
 static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize);
288057e9
 static void gpt_printName(uint16_t name[], const char *msg);
 static void gpt_printGUID(uint8_t GUID[], const char *msg);
e3646995
 static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
 
00acb79c
 /* returns 0 on failing to detect sectorsize */
 size_t gpt_detect_size(fmap_t *map)
 {
     unsigned char *buff;
 
288057e9
     buff = (unsigned char *)fmap_need_off_once(map, 512, 8);
76a58f01
     if (!buff) return 0;
cd94be7a
     if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
00acb79c
         return 512;
 
288057e9
     buff = (unsigned char *)fmap_need_off_once(map, 1024, 8);
76a58f01
     if (!buff) return 0;
cd94be7a
     if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
00acb79c
         return 1024;
 
288057e9
     buff = (unsigned char *)fmap_need_off_once(map, 2048, 8);
76a58f01
     if (!buff) return 0;
cd94be7a
     if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
00acb79c
         return 2048;
 
288057e9
     buff = (unsigned char *)fmap_need_off_once(map, 4096, 8);
76a58f01
     if (!buff) return 0;
cd94be7a
     if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
00acb79c
         return 4096;
 
     return 0;
 }
 
 /* attempts to detect sector size is input as 0 */
 int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
fce85dd7
 {
     struct gpt_header phdr, shdr;
e3646995
     enum GPT_SCANSTATE state = INVALID;
a343bc81
     int ret = CL_CLEAN, detection = CL_CLEAN;
00acb79c
     size_t maplen;
e3646995
     off_t pos = 0;
fce85dd7
 
e3646995
     gpt_parsemsg("The beginning of something big: GPT parsing\n");
fce85dd7
 
e3646995
     if (!ctx || !ctx->fmap) {
         cli_errmsg("cli_scangpt: Invalid context\n");
         return CL_ENULLARG;
     }
 
7cd9337a
     /* sector size calculation */
00acb79c
     if (sectorsize == 0) {
         sectorsize = gpt_detect_size((*ctx->fmap));
811fb62c
         cli_dbgmsg("cli_scangpt: detected %lu sector size\n", (unsigned long)sectorsize);
00acb79c
     }
     if (sectorsize == 0) {
7cd9337a
         cli_errmsg("cli_scangpt: could not determine sector size\n");
00acb79c
         return CL_EFORMAT;
     }
e3646995
 
     /* size of total file must be a multiple of the sector size */
fce85dd7
     maplen = (*ctx->fmap)->real_len;
e3646995
     if ((maplen % sectorsize) != 0) {
811fb62c
         cli_dbgmsg("cli_scangpt: File sized %lu is not a multiple of sector size %lu\n",
                    (unsigned long)maplen, (unsigned long)sectorsize);
e3646995
         return CL_EFORMAT;
     }
fce85dd7
 
b374521a
     /* check the protective mbr */
     ret = gpt_check_mbr(ctx, sectorsize);
a343bc81
     if (ret != CL_CLEAN) {
048a88e6
         if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
             detection = CL_VIRUS;
         else
             return ret;
b374521a
     }
 
e3646995
     pos = GPT_PRIMARY_HDR_LBA * sectorsize; /* sector 1 (second sector) is the primary gpt header */
fce85dd7
 
e3646995
     /* read primary gpt header */
     cli_dbgmsg("cli_scangpt: Using primary GPT header\n");
     if (fmap_readn(*ctx->fmap, &phdr, pos, sizeof(phdr)) != sizeof(phdr)) {
fce85dd7
         cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
e3646995
 
     pos = maplen - sectorsize; /* last sector is the secondary gpt header */
 
     if (gpt_validate_header(ctx, phdr, sectorsize)) {
         cli_dbgmsg("cli_scangpt: Primary GPT header is invalid\n");
         cli_dbgmsg("cli_scangpt: Using secondary GPT header\n");
 
         state = SECONDARY_ONLY;
 
         /* read secondary gpt header */
         if (fmap_readn(*ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
             cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
             return CL_EFORMAT;
         }
 
         if (gpt_validate_header(ctx, shdr, sectorsize)) {
             cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
             cli_dbgmsg("cli_scangpt: Disk is unusable\n");
             return CL_EFORMAT;
         }
288057e9
     } else {
e3646995
         cli_dbgmsg("cli_scangpt: Checking secondary GPT header\n");
fce85dd7
 
e3646995
         state = PRIMARY_ONLY;
fce85dd7
 
e3646995
         /* check validity of secondary header; still using the primary */
         if (fmap_readn(*ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
             cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
288057e9
         } else if (gpt_validate_header(ctx, shdr, sectorsize)) {
e3646995
             cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
         }
         /* check that the two partition table crc32 checksum match, 
          * may want a different hashing function */
288057e9
         else if (phdr.tableCRC32 != shdr.tableCRC32) {
e3646995
             cli_dbgmsg("cli_scangpt: Primary and secondary GPT header table CRC32 differ\n");
             cli_dbgmsg("cli_scangpt: Set to scan primary and secondary partition tables\n");
 
             state = BOTH;
288057e9
         } else {
e3646995
             cli_dbgmsg("cli_scangpt: Secondary GPT header check OK\n");
         }
     }
 
     /* check that the partition table has no intersections - HEURISTICS */
048a88e6
     if (SCAN_HEURISTIC_PARTITION_INTXN && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
e3646995
         ret = gpt_prtn_intxn(ctx, phdr, sectorsize);
a343bc81
         if (ret != CL_CLEAN) {
048a88e6
             if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                 detection = CL_VIRUS;
             else
                 return ret;
e3646995
         }
         ret = gpt_prtn_intxn(ctx, shdr, sectorsize);
a343bc81
         if (ret != CL_CLEAN) {
048a88e6
             if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                 detection = CL_VIRUS;
             else
                 return ret;
e3646995
         }
     }
 
     /* scanning partitions */
     switch (state) {
288057e9
         case PRIMARY_ONLY:
             cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions only\n");
             ret = gpt_scan_partitions(ctx, phdr, sectorsize);
             if (ret != CL_CLEAN) {
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
                     detection = CL_VIRUS;
                 else
                     return ret;
             }
             break;
         case SECONDARY_ONLY:
             cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions only\n");
             ret = gpt_scan_partitions(ctx, shdr, sectorsize);
             if (ret != CL_CLEAN) {
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
                     detection = CL_VIRUS;
                 else
                     return ret;
             }
             break;
         case BOTH:
             cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions\n");
             ret = gpt_scan_partitions(ctx, phdr, sectorsize);
             if (ret != CL_CLEAN) {
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
                     detection = CL_VIRUS;
                 else
                     return ret;
             }
             cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions\n");
             ret = gpt_scan_partitions(ctx, shdr, sectorsize);
             if (ret != CL_CLEAN) {
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
                     detection = CL_VIRUS;
                 else
                     return ret;
             }
             break;
         default:
             cli_dbgmsg("cli_scangpt: State is invalid\n");
e3646995
     }
 
a343bc81
     return detection;
fce85dd7
 }
 
e3646995
 static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
fce85dd7
 {
e3646995
     struct gpt_partition_entry gpe;
a343bc81
     int ret = CL_CLEAN, detection = CL_CLEAN;
288057e9
     size_t maplen, part_size      = 0;
e3646995
     off_t pos = 0, part_off = 0;
     unsigned i = 0, j = 0;
     uint32_t max_prtns = 0;
fce85dd7
 
e3646995
     /* convert endian to host */
288057e9
     hdr.signature       = be64_to_host(hdr.signature);
     hdr.revision        = be32_to_host(hdr.revision);
     hdr.headerSize      = le32_to_host(hdr.headerSize);
     hdr.headerCRC32     = le32_to_host(hdr.headerCRC32);
     hdr.reserved        = le32_to_host(hdr.reserved);
     hdr.currentLBA      = le64_to_host(hdr.currentLBA);
     hdr.backupLBA       = le64_to_host(hdr.backupLBA);
     hdr.firstUsableLBA  = le64_to_host(hdr.firstUsableLBA);
     hdr.lastUsableLBA   = le64_to_host(hdr.lastUsableLBA);
     hdr.tableStartLBA   = le64_to_host(hdr.tableStartLBA);
e3646995
     hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
288057e9
     hdr.tableEntrySize  = le32_to_host(hdr.tableEntrySize);
     hdr.tableCRC32      = le32_to_host(hdr.tableCRC32);
e3646995
 
     /* print header info for the debug */
     cli_dbgmsg("GPT Header:\n");
ce2dcb53
     cli_dbgmsg("Signature: 0x%llx\n", (long long unsigned)hdr.signature);
e3646995
     cli_dbgmsg("Revision: %x\n", hdr.revision);
     gpt_printGUID(hdr.DiskGUID, "DISK GUID");
     cli_dbgmsg("Partition Entry Count: %u\n", hdr.tableNumEntries);
     cli_dbgmsg("Partition Entry Size: %u\n", hdr.tableEntrySize);
 
     maplen = (*ctx->fmap)->real_len;
 
     /* check engine maxpartitions limit */
     if (hdr.tableNumEntries < ctx->engine->maxpartitions) {
         max_prtns = hdr.tableNumEntries;
288057e9
     } else {
e3646995
         max_prtns = ctx->engine->maxpartitions;
     }
 
     /* use the partition tables to pass partitions to cli_map_scan */
     pos = hdr.tableStartLBA * sectorsize;
     for (i = 0; i < max_prtns; ++i) {
         /* read in partition entry */
         if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
             cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
             return CL_EFORMAT;
         }
 
         /* convert the endian to host */
288057e9
         gpe.firstLBA   = le64_to_host(gpe.firstLBA);
         gpe.lastLBA    = le64_to_host(gpe.lastLBA);
e3646995
         gpe.attributes = le64_to_host(gpe.attributes);
         for (j = 0; j < 36; ++j) {
             gpe.name[i] = le16_to_host(gpe.name[i]);
         }
 
         /* check that partition is not empty and within a valid location */
         if (gpe.firstLBA == 0) {
             /* empty partition, invalid */
288057e9
         } else if ((gpe.firstLBA > gpe.lastLBA) ||
                    (gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) {
e3646995
             cli_dbgmsg("cli_scangpt: GPT partition exists outside specified bounds\n");
             gpt_parsemsg("%llu < %llu, %llu > %llu\n", gpe.firstLBA, hdr.firstUsableLBA,
                          gpe.lastLBA, hdr.lastUsableLBA);
             /* partition exists outside bounds specified by header or invalid */
288057e9
         } else if (((gpe.lastLBA + 1) * sectorsize) > maplen) {
e3646995
             /* partition exists outside bounds of the file map */
288057e9
         } else {
e3646995
             /* print partition entry data for debug */
             cli_dbgmsg("GPT Partition Entry %u:\n", i);
             gpt_printName(gpe.name, "Name");
             gpt_printGUID(gpe.typeGUID, "Type GUID");
             gpt_printGUID(gpe.uniqueGUID, "Unique GUID");
ce2dcb53
             cli_dbgmsg("Attributes: %llx\n", (long long unsigned)gpe.attributes);
e3646995
             cli_dbgmsg("Blocks: [%llu(%llu) -> %llu(%llu)]\n",
288057e9
                        (long long unsigned)gpe.firstLBA, (long long unsigned)(gpe.firstLBA * sectorsize),
                        (long long unsigned)gpe.lastLBA, (long long unsigned)((gpe.lastLBA + 1) * sectorsize));
e3646995
 
             /* send the partition to cli_map_scan */
288057e9
             part_off  = gpe.firstLBA * sectorsize;
e3646995
             part_size = (gpe.lastLBA - gpe.firstLBA + 1) * sectorsize;
288057e9
             ret       = cli_map_scan(*ctx->fmap, part_off, part_size, ctx, CL_TYPE_PART_ANY);
a343bc81
             if (ret != CL_CLEAN) {
048a88e6
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                     detection = CL_VIRUS;
                 else
e3646995
                     return ret;
             }
fce85dd7
         }
e3646995
 
         /* increment the offsets to next partition entry */
         pos += hdr.tableEntrySize;
fce85dd7
     }
e3646995
 
d3130136
     if (i >= ctx->engine->maxpartitions) {
de3b931f
         cli_dbgmsg("cli_scangpt: max partitions reached\n");
e3646995
     }
 
a343bc81
     return detection;
fce85dd7
 }
 
 static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
 {
     uint32_t crc32_calc, crc32_ref;
     uint64_t tableLastLBA, lastLBA;
     size_t maplen, ptable_start, ptable_len;
     unsigned char *ptable;
 
     maplen = (*ctx->fmap)->real_len;
 
     /* checking header crc32 checksum */
288057e9
     crc32_ref       = le32_to_host(hdr.headerCRC32);
fce85dd7
     hdr.headerCRC32 = 0; /* checksum is calculated with field = 0 */
288057e9
     crc32_calc      = crc32(0, (unsigned char *)&hdr, sizeof(hdr));
fce85dd7
     if (crc32_calc != crc32_ref) {
         cli_dbgmsg("cli_scangpt: GPT header checksum mismatch\n");
         gpt_parsemsg("%x != %x\n", crc32_calc, crc32_ref);
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
     /* convert endian to host to check partition table */
288057e9
     hdr.signature       = be64_to_host(hdr.signature);
     hdr.revision        = be32_to_host(hdr.revision);
     hdr.headerSize      = le32_to_host(hdr.headerSize);
     hdr.headerCRC32     = crc32_ref;
     hdr.reserved        = le32_to_host(hdr.reserved);
     hdr.currentLBA      = le64_to_host(hdr.currentLBA);
     hdr.backupLBA       = le64_to_host(hdr.backupLBA);
     hdr.firstUsableLBA  = le64_to_host(hdr.firstUsableLBA);
     hdr.lastUsableLBA   = le64_to_host(hdr.lastUsableLBA);
     hdr.tableStartLBA   = le64_to_host(hdr.tableStartLBA);
fce85dd7
     hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
288057e9
     hdr.tableEntrySize  = le32_to_host(hdr.tableEntrySize);
     hdr.tableCRC32      = le32_to_host(hdr.tableCRC32);
fce85dd7
 
     ptable_start = hdr.tableStartLBA * sectorsize;
288057e9
     ptable_len   = hdr.tableNumEntries * hdr.tableEntrySize;
fce85dd7
     tableLastLBA = (hdr.tableStartLBA + (ptable_len / sectorsize)) - 1;
288057e9
     lastLBA      = (maplen / sectorsize) - 1;
fce85dd7
 
     /** HEADER CHECKS **/
     gpt_printSectors(ctx, sectorsize);
 
     /* check signature */
     if (hdr.signature != GPT_SIGNATURE) {
         cli_dbgmsg("cli_scangpt: Invalid GPT header signature %llx\n",
288057e9
                    (long long unsigned)hdr.signature);
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
     /* check header size */
     if (hdr.headerSize != sizeof(hdr)) {
         cli_dbgmsg("cli_scangpt: GPT header size does not match stated size\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
     /* check reserved value == 0 */
     if (hdr.reserved != GPT_HDR_RESERVED) {
         cli_dbgmsg("cli_scangpt: GPT header reserved is not expected value\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
     /* check that sectors are in a valid configuration */
     if (!((hdr.currentLBA == GPT_PRIMARY_HDR_LBA && hdr.backupLBA == lastLBA) ||
           (hdr.currentLBA == lastLBA && hdr.backupLBA == GPT_PRIMARY_HDR_LBA))) {
         cli_dbgmsg("cli_scangpt: GPT secondary header is not last LBA\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
     if (hdr.firstUsableLBA > hdr.lastUsableLBA) {
         cli_dbgmsg("cli_scangpt: GPT first usable sectors is after last usable sector\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
     if (hdr.firstUsableLBA <= GPT_PRIMARY_HDR_LBA || hdr.lastUsableLBA >= lastLBA) {
         cli_dbgmsg("cli_scangpt: GPT usable sectors intersects header sector\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
     if ((hdr.tableStartLBA <= hdr.firstUsableLBA && tableLastLBA >= hdr.firstUsableLBA) ||
         (hdr.tableStartLBA >= hdr.firstUsableLBA && hdr.tableStartLBA <= hdr.lastUsableLBA)) {
         cli_dbgmsg("cli_scangpt: GPT usable sectors intersects partition table\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
     if (hdr.tableStartLBA <= GPT_PRIMARY_HDR_LBA || tableLastLBA >= lastLBA) {
         cli_dbgmsg("cli_scangpt: GPT partition table intersects header sector\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
e3646995
     /* check that valid table entry size */
00acb79c
     if (hdr.tableEntrySize != sizeof(struct gpt_partition_entry)) {
e3646995
         cli_dbgmsg("cli_scangpt: cannot parse gpt with partition entry sized %u\n",
                    hdr.tableEntrySize);
         return CL_EFORMAT;
     }
 
fce85dd7
     /* check valid table */
     if ((ptable_start + ptable_len) > maplen) {
         cli_dbgmsg("cli_scangpt: GPT partition table extends over fmap limit\n");
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
     /** END HEADER CHECKS **/
 
     /* checking partition table crc32 checksum */
288057e9
     ptable     = (unsigned char *)fmap_need_off_once((*ctx->fmap), ptable_start, ptable_len);
fce85dd7
     crc32_calc = crc32(0, ptable, ptable_len);
     if (crc32_calc != hdr.tableCRC32) {
         cli_dbgmsg("cli_scangpt: GPT partition table checksum mismatch\n");
         gpt_parsemsg("%x != %x\n", crc32_calc, hdr.tableCRC32);
e3646995
         return CL_EFORMAT;
fce85dd7
     }
 
e3646995
     return CL_SUCCESS;
fce85dd7
 }
 
b374521a
 static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
 {
     struct mbr_boot_record pmbr;
     off_t pos = 0, mbr_base = 0;
288057e9
     int ret    = CL_CLEAN;
b374521a
     unsigned i = 0;
 
     /* read the mbr */
     mbr_base = sectorsize - sizeof(struct mbr_boot_record);
288057e9
     pos      = (MBR_SECTOR * sectorsize) + mbr_base;
b374521a
 
     if (fmap_readn(*ctx->fmap, &pmbr, pos, sizeof(pmbr)) != sizeof(pmbr)) {
         cli_dbgmsg("cli_scangpt: Invalid primary MBR header\n");
         return CL_EFORMAT;
     }
 
     /* convert mbr */
     mbr_convert_to_host(&pmbr);
 
     /* check the protective mbr - warning */
     if (pmbr.entries[0].type == MBR_PROTECTIVE) {
         /* check the efi partition matches the gpt spec */
         if (pmbr.entries[0].firstLBA != GPT_PRIMARY_HDR_LBA) {
             cli_warnmsg("cli_scangpt: protective MBR first LBA is incorrect %u\n",
                         pmbr.entries[0].firstLBA);
         }
 
         /* other entries are empty */
         for (i = 1; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
             if (pmbr.entries[i].type != MBR_EMPTY) {
                 cli_warnmsg("cli_scangpt: protective MBR has non-empty partition\n");
                 break;
             }
         }
288057e9
     } else if (pmbr.entries[0].type == MBR_HYBRID) {
b374521a
         /* hybrid mbr detected */
         cli_warnmsg("cli_scangpt: detected a hybrid MBR\n");
288057e9
     } else {
b374521a
         /* non-protective mbr detected */
         cli_warnmsg("cli_scangpt: detected a non-protective MBR\n");
     }
 
     /* scan the bootloader segment - pushed to scanning mbr */
     /* check if MBR size matches GPT size */
     /* check if the MBR and GPT partitions align - heuristic */
     /* scan the MBR partitions - additional scans */
 
     return ret;
 }
 
e3646995
 static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize)
fce85dd7
 {
e3646995
 #ifdef DEBUG_GPT_PARSE
     struct gpt_header phdr, shdr;
     off_t ppos = 0, spos = 0;
     size_t pptable_len, sptable_len, maplen;
     uint64_t ptableLastLBA, stableLastLBA;
fce85dd7
 
e3646995
     /* sector size calculation */
     sectorsize = GPT_DEFAULT_SECTOR_SIZE;
fce85dd7
 
     maplen = (*ctx->fmap)->real_len;
 
288057e9
     ppos = 1 * sectorsize;      /* sector 1 (second sector) is the primary gpt header */
e3646995
     spos = maplen - sectorsize; /* last sector is the secondary gpt header */
 
     /* read in the primary and secondary gpt headers */
     if (fmap_readn(*ctx->fmap, &phdr, ppos, sizeof(phdr)) != sizeof(phdr)) {
fce85dd7
         cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
e3646995
         return;
     }
     if (fmap_readn(*ctx->fmap, &shdr, spos, sizeof(shdr)) != sizeof(shdr)) {
         cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
         return;
fce85dd7
     }
 
288057e9
     pptable_len   = phdr.tableNumEntries * phdr.tableEntrySize;
     sptable_len   = shdr.tableNumEntries * shdr.tableEntrySize;
e3646995
     ptableLastLBA = (phdr.tableStartLBA + (pptable_len / sectorsize)) - 1;
     stableLastLBA = (shdr.tableStartLBA + (sptable_len / sectorsize)) - 1;
fce85dd7
 
e3646995
     gpt_parsemsg("0: MBR\n");
     gpt_parsemsg("%llu: Primary GPT Header\n", phdr.currentLBA);
     gpt_parsemsg("%llu-%llu: Primary GPT Partition Table\n", phdr.tableStartLBA, ptableLastLBA);
7cd9337a
     gpt_parsemsg("%llu-%llu: Usable LBAs\n", phdr.firstUsableLBA, phdr.lastUsableLBA);
e3646995
     gpt_parsemsg("%llu-%llu: Secondary GPT Partition Table\n", shdr.tableStartLBA, stableLastLBA);
     gpt_parsemsg("%llu: Secondary GPT Header\n", phdr.backupLBA);
 #else
cd94be7a
     UNUSEDPARAM(ctx);
     UNUSEDPARAM(sectorsize);
e3646995
     return;
 #endif
 }
fce85dd7
 
288057e9
 static void gpt_printName(uint16_t name[], const char *msg)
e3646995
 {
     char *namestr;
fce85dd7
 
288057e9
     namestr = (char *)cli_utf16toascii((char *)name, 72);
e3646995
     cli_dbgmsg("%s: %s\n", msg, namestr);
 
     free(namestr);
 }
 
288057e9
 static void gpt_printGUID(uint8_t GUID[], const char *msg)
e3646995
 {
4bdb1820
     cli_dbgmsg("%s: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
                msg, GUID[0], GUID[1], GUID[2], GUID[3], GUID[4], GUID[5], GUID[6], GUID[7],
                GUID[8], GUID[9], GUID[10], GUID[11], GUID[12], GUID[13], GUID[14], GUID[15]);
e3646995
 }
fce85dd7
 
e3646995
 static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
 {
     prtn_intxn_list_t prtncheck;
     struct gpt_partition_entry gpe;
     unsigned i, pitxn;
a343bc81
     int ret = CL_CLEAN, tmp = CL_CLEAN;
e3646995
     off_t pos;
     size_t maplen;
     uint32_t max_prtns = 0;
288057e9
     int virus_found    = 0;
e3646995
 
     maplen = (*ctx->fmap)->real_len;
 
     /* convert endian to host to check partition table */
288057e9
     hdr.tableStartLBA   = le64_to_host(hdr.tableStartLBA);
fce85dd7
     hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
 
288057e9
     prtn_intxn_list_init(&prtncheck);
fce85dd7
 
e3646995
     /* check engine maxpartitions limit */
     if (hdr.tableNumEntries < ctx->engine->maxpartitions) {
         max_prtns = hdr.tableNumEntries;
288057e9
     } else {
e3646995
         max_prtns = ctx->engine->maxpartitions;
fce85dd7
     }
 
     pos = hdr.tableStartLBA * sectorsize;
e3646995
     for (i = 0; i < max_prtns; ++i) {
fce85dd7
         /* read in partition entry */
         if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
e3646995
             cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
             prtn_intxn_list_free(&prtncheck);
fce85dd7
             return CL_EFORMAT;
         }
 
         /* convert the endian to host */
         gpe.firstLBA = le64_to_host(gpe.firstLBA);
288057e9
         gpe.lastLBA  = le64_to_host(gpe.lastLBA);
fce85dd7
 
         if (gpe.firstLBA == 0) {
e3646995
             /* empty partition, invalid */
288057e9
         } else if ((gpe.firstLBA > gpe.lastLBA) ||
                    (gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) {
fce85dd7
             /* partition exists outside bounds specified by header or invalid */
288057e9
         } else if (((gpe.lastLBA + 1) * sectorsize) > maplen) {
e3646995
             /* partition exists outside bounds of the file map */
288057e9
         } else {
e3646995
             tmp = prtn_intxn_list_check(&prtncheck, &pitxn, gpe.firstLBA, gpe.lastLBA - gpe.firstLBA + 1);
             if (tmp != CL_CLEAN) {
cbf5017a
                 if (tmp == CL_VIRUS) {
e3646995
                     cli_dbgmsg("cli_scangpt: detected intersection with partitions "
288057e9
                                "[%u, %u]\n",
                                pitxn, i);
cbf5017a
                     ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
                     if (ret == CL_VIRUS)
                         virus_found = 1;
048a88e6
                     if (SCAN_ALLMATCHES || ret == CL_CLEAN)
cbf5017a
                         tmp = 0;
                     else
                         goto leave;
                 } else {
e3646995
                     ret = tmp;
cbf5017a
                     goto leave;
fce85dd7
                 }
             }
         }
 
         /* increment the offsets to next partition entry */
         pos += hdr.tableEntrySize;
     }
 
288057e9
 leave:
e3646995
     prtn_intxn_list_free(&prtncheck);
cbf5017a
     if (virus_found)
         return CL_VIRUS;
e3646995
     return ret;
fce85dd7
 }