libclamav/mbr.c
039e798c
 /*
c442ca9c
  *  Copyright (C) 2014-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
039e798c
  *
  *  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"
039e798c
 #include "others.h"
 #include "mbr.h"
 #include "prtn_intxn.h"
 #include "scanners.h"
558c673e
 #include "dconf.h"
039e798c
 
 //#define DEBUG_MBR_PARSE
 //#define DEBUG_EBR_PARSE
 
0e8b5a59
 #ifndef PRTN_INTXN_DETECTION
 #  define PRTN_INTXN_DETECTION "heuristic.mbrprtnintersect"
 #endif
 
039e798c
 #ifdef DEBUG_MBR_PARSE
 #  define mbr_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
 #else
 #  define mbr_parsemsg(...) ;
 #endif
 
 #ifdef DEBUG_EBR_PARSE
 #  define ebr_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
 #else
 #  define ebr_parsemsg(...) ;
 #endif
 
 enum MBR_STATE {
     SEEN_NOTHING,
     SEEN_PARTITION,
     SEEN_EXTENDED,
     SEEN_EMPTY
 };
 
0e8b5a59
 static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, 
                            size_t extlbasize, size_t sectorsize);
 static int mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize);
039e798c
 static int mbr_check_ebr(struct mbr_boot_record *record);
 static int mbr_primary_prtn_intxn(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize);
0e8b5a59
 static int mbr_extended_prtn_intxn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t sectorsize);
039e798c
 
 int cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen) {
     struct mbr_boot_record mbr;
00acb79c
     off_t mbr_base = 0;
     size_t sectorsize = 512;
039e798c
 
00acb79c
     if (len < sectorsize) {
039e798c
         return CL_EFORMAT;
     }
 
00acb79c
     mbr_base = sectorsize - sizeof(struct mbr_boot_record);
     memcpy(&mbr, buff+mbr_base, sizeof(mbr));
039e798c
     mbr_convert_to_host(&mbr);
 
e5d13808
     if ((mbr.entries[0].type == MBR_PROTECTIVE) || (mbr.entries[0].type == MBR_HYBRID))
         return CL_TYPE_GPT;
039e798c
 
00acb79c
     return mbr_check_mbr(&mbr, maplen, sectorsize);
039e798c
 }
 
e5d13808
 int cli_mbr_check2(cli_ctx *ctx, size_t sectorsize) {
76a58f01
     struct mbr_boot_record mbr;
e5d13808
     off_t pos = 0, mbr_base = 0;
     size_t maplen;
76a58f01
 
e5d13808
     if (!ctx || !ctx->fmap) {
         cli_errmsg("cli_scanmbr: Invalid context\n");
         return CL_ENULLARG;
76a58f01
     }
 
e5d13808
     /* sector size calculation, actual value is OS dependent */
     if (sectorsize == 0)
         sectorsize = MBR_SECTOR_SIZE;
 
76a58f01
     mbr_base = sectorsize - sizeof(struct mbr_boot_record);
 
e5d13808
     /* size of total file must be a multiple of the sector size */
     maplen = (*ctx->fmap)->real_len;
     if ((maplen % sectorsize) != 0) {
811fb62c
         cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n",
                    (unsigned long)maplen, (unsigned long)sectorsize);
e5d13808
         return CL_EFORMAT;
     }
 
     /* sector 0 (first sector) is the master boot record */
     pos = (MBR_SECTOR * sectorsize) + mbr_base;
 
     /* read the master boot record */
     if (fmap_readn(*ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) {
         cli_dbgmsg("cli_scanmbr: Invalid master boot record\n");
         return CL_EFORMAT;
     }
 
31545680
     /* convert the little endian to host, include the internal  */
     mbr_convert_to_host(&mbr);
 
e5d13808
     if ((mbr.entries[0].type == MBR_PROTECTIVE) || (mbr.entries[0].type == MBR_HYBRID))
         return CL_TYPE_GPT;
 
     return mbr_check_mbr(&mbr, maplen, sectorsize);
76a58f01
 }
 
22cb38ed
 /* sets sectorsize to default value if specified to be 0 */
00acb79c
 int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
039e798c
 {
     struct mbr_boot_record mbr;
     enum MBR_STATE state = SEEN_NOTHING;
a343bc81
     int ret = CL_CLEAN, detection = CL_CLEAN;
00acb79c
     off_t pos = 0, mbr_base = 0, partoff = 0;
0e8b5a59
     unsigned i = 0, prtncount = 0;
00acb79c
     size_t maplen, partsize;
039e798c
 
     mbr_parsemsg("The start of something magnificant: MBR parsing\n");
 
     if (!ctx || !ctx->fmap) {
         cli_errmsg("cli_scanmbr: Invalid context\n");
         return CL_ENULLARG;
     }
 
0b3aa068
     /* sector size calculation, actual value is OS dependent */
00acb79c
     if (sectorsize == 0)
         sectorsize = MBR_SECTOR_SIZE;
 
     mbr_base = sectorsize - sizeof(struct mbr_boot_record);
039e798c
 
     /* size of total file must be a multiple of the sector size */
     maplen = (*ctx->fmap)->real_len;
     if ((maplen % sectorsize) != 0) {
811fb62c
         cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n",
                    (unsigned long)maplen, (unsigned long)sectorsize);
039e798c
         return CL_EFORMAT;
     }
 
     /* sector 0 (first sector) is the master boot record */
00acb79c
     pos = (MBR_SECTOR * sectorsize) + mbr_base;
039e798c
 
     /* read the master boot record */
     if (fmap_readn(*ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) {
         cli_dbgmsg("cli_scanmbr: Invalid master boot record\n");
         return CL_EFORMAT;
     }
 
     /* convert the little endian to host, include the internal  */
     mbr_convert_to_host(&mbr);
 
     /* MBR checks */
0e8b5a59
     ret = mbr_check_mbr(&mbr, maplen, sectorsize);
039e798c
     if (ret != CL_CLEAN) {
         return ret;
     }
 
0b3aa068
     /* MBR is valid, examine bootstrap code */
00acb79c
     ret = cli_map_scan(*ctx->fmap, 0, sectorsize, ctx, CL_TYPE_ANY);
a343bc81
     if (ret != CL_CLEAN) {
d7979d4f
         if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
             detection = CL_VIRUS;
         else
             return ret;
0b3aa068
     }
 
039e798c
     /* check that the partition table has no intersections - HEURISTICS */
d7979d4f
     if (SCAN_HEURISTIC_PARTITION_INTXN && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
039e798c
         ret = mbr_primary_prtn_intxn(ctx, mbr, sectorsize);
a343bc81
         if (ret != CL_CLEAN) {
d7979d4f
             if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                 detection = CL_VIRUS;
             else
                 return ret;
039e798c
         }
     }
 
     /* MBR is valid, examine partitions */
     prtncount = 0;
     cli_dbgmsg("MBR Signature: %x\n", mbr.signature);
0e8b5a59
     for (i = 0; i < MBR_MAX_PARTITION_ENTRIES && prtncount < ctx->engine->maxpartitions; ++i) {
039e798c
         cli_dbgmsg("MBR Partition Entry %u:\n", i);
         cli_dbgmsg("Status: %u\n", mbr.entries[i].status);
         cli_dbgmsg("Type: %x\n", mbr.entries[i].type);
811fb62c
         cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n",
0e8b5a59
                    mbr.entries[i].firstLBA, mbr.entries[i].numLBA,
811fb62c
                    (unsigned long)(mbr.entries[i].firstLBA * sectorsize),
                    (unsigned long)(mbr.entries[i].numLBA * sectorsize));
039e798c
 
         /* Handle MBR entry based on type */
         if (mbr.entries[i].type == MBR_EMPTY) {
7cd9337a
             /* empty partition entry */
039e798c
             prtncount++;
         }
         else if (mbr.entries[i].type == MBR_EXTENDED) {
             if (state == SEEN_EXTENDED) {
                 cli_dbgmsg("cli_scanmbr: detected a master boot record "
                            "with multiple extended partitions\n");
             }
22cb38ed
             state = SEEN_EXTENDED; /* used only to detect multiple extended partitions */
039e798c
 
0e8b5a59
             ret = mbr_scanextprtn(ctx, &prtncount, mbr.entries[i].firstLBA, 
                                   mbr.entries[i].numLBA, sectorsize);
a343bc81
             if (ret != CL_CLEAN) {
d7979d4f
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                     detection = CL_VIRUS;
                 else
                     return ret;
0e8b5a59
             }
039e798c
         }
         else {
             prtncount++;
 
             partoff = mbr.entries[i].firstLBA * sectorsize;
0e8b5a59
             partsize = mbr.entries[i].numLBA * sectorsize;
039e798c
             mbr_parsemsg("cli_map_scan: [%u, +%u)\n", partoff, partsize);
             ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY);
a343bc81
             if (ret != CL_CLEAN) {
d7979d4f
                 if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                     detection = CL_VIRUS;
                 else
039e798c
                     return ret;
             }
         }
     }
 
0e8b5a59
     if (prtncount >= ctx->engine->maxpartitions) {
         cli_dbgmsg("cli_scanmbr: maximum partitions reached\n");
     }
a343bc81
 
     return detection;
039e798c
 }
 
0e8b5a59
 static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t extlbasize, size_t sectorsize)
 {
039e798c
     struct mbr_boot_record ebr;
     enum MBR_STATE state = SEEN_NOTHING;
a343bc81
     int ret = CL_CLEAN, detection = CL_CLEAN;
00acb79c
     off_t pos = 0, mbr_base = 0, logiclba = 0, extoff = 0, partoff = 0;
0e8b5a59
     size_t partsize, extsize;
     unsigned i = 0, j = 0;
 
     ebr_parsemsg("The start of something exhausting: EBR parsing\n");
 
00acb79c
     mbr_base = sectorsize - sizeof(struct mbr_boot_record);
 
0e8b5a59
     logiclba = 0;
     extoff = extlba * sectorsize;
     extsize = extlbasize * sectorsize;
     do {
         pos = extlba * sectorsize; /* start of extended partition */
 
         /* read the extended boot record */
00acb79c
         pos += (logiclba * sectorsize) + mbr_base;
0e8b5a59
         if (fmap_readn(*ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) {
             cli_dbgmsg("cli_scanebr: Invalid extended boot record\n");
             return CL_EFORMAT;
         }
039e798c
 
0e8b5a59
         /* convert the little endian to host */
         mbr_convert_to_host(&ebr);
039e798c
 
0e8b5a59
         /* EBR checks */
         ret = mbr_check_ebr(&ebr);
         if (ret != CL_CLEAN) {
             return ret;
         }
039e798c
 
0e8b5a59
         /* update state */
         state = SEEN_NOTHING;
         (*prtncount)++;
 
         /* EBR is valid, examine partitions */
         cli_dbgmsg("EBR Partition Entry %u:\n", i++);
         cli_dbgmsg("EBR Signature: %x\n", ebr.signature);
         for (j = 0; j < MBR_MAX_PARTITION_ENTRIES; ++j) {
             if (j < 2) {
                 cli_dbgmsg("Logical Partition Entry %u:\n", j);
                 cli_dbgmsg("Status: %u\n", ebr.entries[j].status);
                 cli_dbgmsg("Type: %x\n", ebr.entries[j].type);
811fb62c
                 cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n",
0e8b5a59
                            ebr.entries[j].firstLBA, ebr.entries[j].numLBA,
811fb62c
                            (unsigned long)(ebr.entries[j].firstLBA * sectorsize),
                            (unsigned long)(ebr.entries[j].numLBA * sectorsize));
0e8b5a59
 
                 if (ebr.entries[j].type == MBR_EMPTY) {
7cd9337a
                     /* empty partition entry */
0e8b5a59
                     switch(state) {
                     case SEEN_NOTHING:
                         state = SEEN_EMPTY;
                         break;
                     case SEEN_PARTITION:
                         logiclba = 0;
                         break;
                     case SEEN_EMPTY:
                         logiclba = 0;
                         /* fall-through */
                     case SEEN_EXTENDED:
b374521a
                         cli_warnmsg("cli_scanebr: detected a logical boot record "
                                     "without a partition record\n");
0e8b5a59
                         break;
                     default:
b374521a
                         cli_warnmsg("cli_scanebr: undefined state for EBR parsing\n");
0e8b5a59
                         return CL_EPARSE;
                     }
039e798c
                 }
0e8b5a59
                 else if (ebr.entries[j].type == MBR_EXTENDED) {
                     switch(state) {
                     case SEEN_NOTHING:
                         state = SEEN_EXTENDED;
                         break;
                     case SEEN_PARTITION:
                         break;
                     case SEEN_EMPTY:
b374521a
                         cli_warnmsg("cli_scanebr: detected a logical boot record "
                                     "without a partition record\n");
0e8b5a59
                         break;
                     case SEEN_EXTENDED:
b374521a
                         cli_warnmsg("cli_scanebr: detected a logical boot record "
                                     "with multiple extended partition records\n");
0e8b5a59
                         return CL_EFORMAT;
                     default:
                         cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n");
                         return CL_EPARSE;
                     }
039e798c
 
0e8b5a59
                     logiclba = ebr.entries[j].firstLBA;
039e798c
                 }
0e8b5a59
                 else {
                     switch(state) {
                     case SEEN_NOTHING:
                         state = SEEN_PARTITION;
                         break;
                     case SEEN_PARTITION:
b374521a
                         cli_warnmsg("cli_scanebr: detected a logical boot record "
                                     "with multiple partition records\n");
0e8b5a59
                         logiclba = 0; /* no extended partitions are possible */
                         break;
                     case SEEN_EXTENDED:
b374521a
                         cli_warnmsg("cli_scanebr: detected a logical boot record "
                                     "with extended partition record first\n");
0e8b5a59
                         break;
                     case SEEN_EMPTY:
b374521a
                         cli_warnmsg("cli_scanebr: detected a logical boot record "
                                     "with empty partition record first\n");
0e8b5a59
                         logiclba = 0; /* no extended partitions are possible */
                         break;
                     default:
                         cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n");
                         return CL_EPARSE;
                     }
039e798c
 
0e8b5a59
                     partoff = (extlba + logiclba + ebr.entries[j].firstLBA) * sectorsize;
                     partsize = ebr.entries[j].numLBA * sectorsize;
31545680
                     if (partoff + partsize > extoff + extsize) {
                         cli_dbgmsg("cli_scanebr: Invalid extended partition entry\n");
0e8b5a59
                         return CL_EFORMAT;
                     }
 
                     ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY);
a343bc81
                     if (ret != CL_CLEAN) {
d7979d4f
                         if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
a343bc81
                             detection = CL_VIRUS;
                         else
                             return ret;
0e8b5a59
                     }
039e798c
                 }
             }
0e8b5a59
             else {
                 /* check the last two entries to be empty */
                 if (ebr.entries[j].type != MBR_EMPTY) {
                     cli_dbgmsg("cli_scanebr: detected a non-empty partition "
                                "entry at index %u\n", j);
                     /* should we attempt to use these entries? */
                     return CL_EFORMAT;
                 }
039e798c
             }
         }
0e8b5a59
     } while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions);
039e798c
 
0e8b5a59
     cli_dbgmsg("cli_scanmbr: examined %u logical partitions\n", i);
a343bc81
 
     return detection;
039e798c
 }
 
b374521a
 void mbr_convert_to_host(struct mbr_boot_record *record)
 {
     struct mbr_partition_entry *entry;
     unsigned i;
  
     for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
         entry = &record->entries[i];
  
         entry->firstLBA = le32_to_host(entry->firstLBA);
         entry->numLBA = le32_to_host(entry->numLBA);
     }
     record->signature = be16_to_host(record->signature);
 }
 
0e8b5a59
 static int mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize)
039e798c
 {
     unsigned i = 0;
     off_t partoff = 0;
0e8b5a59
     size_t partsize = 0;
039e798c
 
     for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
         /* check status */
         if ((record->entries[i].status != MBR_STATUS_INACTIVE) && 
             (record->entries[i].status != MBR_STATUS_ACTIVE)) {
             cli_dbgmsg("cli_scanmbr: Invalid boot record status\n");
             return CL_EFORMAT;
         }
 
         partoff = record->entries[i].firstLBA * sectorsize;
0e8b5a59
         partsize = record->entries[i].numLBA * sectorsize;
039e798c
         if (partoff + partsize > maplen) {
             cli_dbgmsg("cli_scanmbr: Invalid partition entry\n");
             return CL_EFORMAT;
         }
     }
 
     /* check the signature */
     if (record->signature != MBR_SIGNATURE) {
         cli_dbgmsg("cli_scanmbr: Invalid boot record signature\n");
         return CL_EFORMAT;
     }
 
e5d13808
     /* check the maplen */
     if ((maplen / sectorsize) < 2) {
f290ffd3
         cli_dbgmsg("cli_scanmbr: bootstrap code or file is too small to hold disk image\n");
e5d13808
         return CL_EFORMAT;
     }
 
039e798c
     return CL_CLEAN;
 }
 
 static int mbr_check_ebr(struct mbr_boot_record *record)
 {
     unsigned i = 0;
 
0e8b5a59
     for (i = 0; i < MBR_MAX_PARTITION_ENTRIES-2; ++i) {
039e798c
         /* check status */
         if ((record->entries[i].status != MBR_STATUS_INACTIVE) && 
             (record->entries[i].status != MBR_STATUS_ACTIVE)) {
             cli_dbgmsg("cli_scanmbr: Invalid boot record status\n");
             return CL_EFORMAT;
         }
     }
 
     /* check the signature */
     if (record->signature != MBR_SIGNATURE) {
         cli_dbgmsg("cli_scanmbr: Invalid boot record signature\n");
         return CL_EFORMAT;
     }
 
     return CL_CLEAN;
 }
 
 /* this includes the overall bounds of extended partitions */
 static int mbr_primary_prtn_intxn(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize)
 {
     prtn_intxn_list_t prtncheck;
0e8b5a59
     unsigned i = 0, pitxn = 0, prtncount = 0;
a343bc81
     int ret = CL_CLEAN, tmp = CL_CLEAN;
039e798c
 
     prtn_intxn_list_init(&prtncheck);
 
0e8b5a59
     for (i = 0; i < MBR_MAX_PARTITION_ENTRIES && prtncount < ctx->engine->maxpartitions; ++i) {
         if (mbr.entries[i].type == MBR_EMPTY) {
7cd9337a
             /* empty partition entry */
0e8b5a59
             prtncount++;
         }
         else {
             tmp = prtn_intxn_list_check(&prtncheck, &pitxn, mbr.entries[i].firstLBA,
                                         mbr.entries[i].numLBA);
039e798c
             if (tmp != CL_CLEAN) {
cbf5017a
                 if (tmp == CL_VIRUS) {
039e798c
                     cli_dbgmsg("cli_scanmbr: detected intersection with partitions "
                                "[%u, %u]\n", pitxn, i);
cbf5017a
                     ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
d7979d4f
                     if (SCAN_ALLMATCHES || ret == CL_CLEAN)
cbf5017a
                         tmp = 0;
                     else
                         goto leave;
                 } else {
039e798c
                     ret = tmp;
cbf5017a
                     goto leave;
039e798c
                 }
             }
 
0e8b5a59
             if (mbr.entries[i].type == MBR_EXTENDED) {
                 /* check the logical partitions */
                 tmp = mbr_extended_prtn_intxn(ctx, &prtncount, 
                                   mbr.entries[i].firstLBA, sectorsize);
039e798c
                 if (tmp != CL_CLEAN) {
d7979d4f
                     if (SCAN_ALLMATCHES && (tmp == CL_VIRUS)) {
039e798c
                         ret = tmp;
                         tmp = 0;
                     }
                     else if (tmp == CL_VIRUS) {
                         prtn_intxn_list_free(&prtncheck);
                         return CL_VIRUS;
                     }
                     else {
                         prtn_intxn_list_free(&prtncheck);
                         return tmp;
                     }
                 }
0e8b5a59
             }
             else {
                 prtncount++;
             }
         }
     }
039e798c
 
cbf5017a
 leave:
0e8b5a59
     prtn_intxn_list_free(&prtncheck);
     return ret;
 }
 
 /* checks internal logical partitions */
 static int mbr_extended_prtn_intxn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t sectorsize)
 {
     struct mbr_boot_record ebr;
     prtn_intxn_list_t prtncheck;
     unsigned i, pitxn;
a343bc81
     int ret = CL_CLEAN, tmp = CL_CLEAN, mbr_base = 0;
0e8b5a59
     off_t pos = 0, logiclba = 0;
cbf5017a
     int virus_found = 0;
0e8b5a59
 
00acb79c
     mbr_base = sectorsize - sizeof(struct mbr_boot_record);
 
0e8b5a59
     prtn_intxn_list_init(&prtncheck);
039e798c
 
0e8b5a59
     logiclba = 0; i = 0;
     do {
         pos = extlba * sectorsize; /* start of extended partition */
039e798c
 
0e8b5a59
         /* read the extended boot record */
00acb79c
         pos += (logiclba * sectorsize) + mbr_base;
0e8b5a59
         if (fmap_readn(*ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) {
             cli_dbgmsg("cli_scanebr: Invalid extended boot record\n");
             prtn_intxn_list_free(&prtncheck);
             return CL_EFORMAT;
         }
039e798c
 
0e8b5a59
         /* convert the little endian to host */
         mbr_convert_to_host(&ebr);
 
         /* update state */
         (*prtncount)++;
 
         /* assume that logical record is first and extended is second */
         tmp = prtn_intxn_list_check(&prtncheck, &pitxn, logiclba, ebr.entries[0].numLBA);
         if (tmp != CL_CLEAN) {
cbf5017a
             if (tmp == CL_VIRUS) {
0e8b5a59
                 cli_dbgmsg("cli_scanebr: detected intersection with partitions "
                            "[%u, %u]\n", pitxn, i);
cbf5017a
                 ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
                 if (ret == CL_VIRUS)
                     virus_found = 1;
d7979d4f
                 if (SCAN_ALLMATCHES || ret == CL_CLEAN)
cbf5017a
                     tmp = 0;
                 else
                     goto leave;
             } else {
0e8b5a59
                 ret = tmp;
cbf5017a
                 goto leave;
0e8b5a59
             }
         }
039e798c
 
0e8b5a59
         /* assume extended is second entry */
         if (ebr.entries[1].type != MBR_EXTENDED) {
             cli_dbgmsg("cli_scanebr: second entry for EBR is not an extended partition\n");
             break;
039e798c
         }
 
0e8b5a59
         logiclba = ebr.entries[1].firstLBA;
039e798c
 
0e8b5a59
         ++i;
     } while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions);
039e798c
 
cbf5017a
  leave:
     prtn_intxn_list_free(&prtncheck);
     if (virus_found)
         return CL_VIRUS;
0e8b5a59
     return ret;
039e798c
 }