/* returns 0 on failing to detect sectorsize */ size_t gpt_detect_size(fmap_t *map) { unsigned char *buff; buff = (unsigned char*)fmap_need_off_once(map, 512, 8); if (!buff) return 0; if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8)) return 512; buff = (unsigned char*)fmap_need_off_once(map, 1024, 8); if (!buff) return 0; if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8)) return 1024; buff = (unsigned char*)fmap_need_off_once(map, 2048, 8); if (!buff) return 0; if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8)) return 2048; buff = (unsigned char*)fmap_need_off_once(map, 4096, 8); if (!buff) return 0; if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8)) return 4096; return 0; }
int cli_check_jpeg_exploit(cli_ctx *ctx, off_t offset) { const unsigned char *buffer; int retval; fmap_t *map = *ctx->fmap; cli_dbgmsg("in cli_check_jpeg_exploit()\n"); if(ctx->recursion > ctx->engine->maxreclevel) return CL_EMAXREC; if(!(buffer = fmap_need_off_once(map, offset, 2))) return 0; if ((buffer[0] != 0xff) || (buffer[1] != 0xd8)) { return 0; } offset += 2; for (;;) { off_t new_off; if(!(buffer = fmap_need_off_once(map, offset, 4))) { return 0; } /* Check for multiple 0xFF values, we need to skip them */ if ((buffer[0] == 0xff) && (buffer[1] == 0xff)) { offset++; continue; } offset += 4; if ((buffer[0] == 0xff) && (buffer[1] == 0xfe)) { if (buffer[2] == 0x00) { if ((buffer[3] == 0x00) || (buffer[3] == 0x01)) { return 1; } } } if (buffer[0] != 0xff) { return -1; } if (buffer[1] == 0xda) { /* End of Image marker */ return 0; } new_off = ((unsigned int) buffer[2] << 8) + buffer[3]; if (new_off < 2) { return -1; } new_off -= 2; new_off += offset; if (buffer[1] == 0xed) { /* Possible Photoshop file */ ctx->recursion++; retval=jpeg_check_photoshop(ctx, offset); ctx->recursion--; if (retval != 0) return retval; } offset = new_off; } }
/* Hashes a file onto the provided buffer and looks it up the cache. Returns CL_VIRUS if found, CL_CLEAN if not FIXME or a recoverable error, and returns CL_EREAD if unrecoverable */ int cache_check(unsigned char *hash, cli_ctx *ctx) { fmap_t *map; size_t todo, at = 0; cli_md5_ctx md5; int ret; if(!ctx || !ctx->engine || !ctx->engine->cache) return CL_VIRUS; map = *ctx->fmap; todo = map->len; cli_md5_init(&md5); while(todo) { const void *buf; size_t readme = todo < FILEBUFF ? todo : FILEBUFF; if(!(buf = fmap_need_off_once(map, at, readme))) return CL_EREAD; todo -= readme; at += readme; if (cli_md5_update(&md5, buf, readme)) { cli_errmsg("cache_check: error reading while generating hash!\n"); return CL_EREAD; } } cli_md5_final(hash, &md5); ret = cache_lookup_hash(hash, map->len, ctx->engine->cache, ctx->recursion); cli_dbgmsg("cache_check: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x is %s\n", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], (ret == CL_VIRUS) ? "negative" : "positive"); return ret; }
/* Stripe handling: stored block (type 0x1) */ static int dmg_stripe_store(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set) { const void *obuf; int ret; size_t off = mish_set->stripes[index].dataOffset; size_t len = mish_set->stripes[index].dataLength; ssize_t written; cli_dbgmsg("dmg_stripe_store: stripe " STDu32 "\n", index); if (len == 0) return CL_CLEAN; obuf = (void *)fmap_need_off_once(*ctx->fmap, off, len); if (!obuf) { cli_warnmsg("dmg_stripe_store: fmap need failed on stripe " STDu32 "\n", index); return CL_EMAP; } written = cli_writen(fd, obuf, len); if (written < 0) { cli_errmsg("dmg_stripe_store: error writing bytes to file (out of disk space?)\n"); return CL_EWRITE; } else if (written != len) { cli_errmsg("dmg_stripe_store: error writing bytes to file (out of disk space?)\n"); return CL_EWRITE; } return CL_CLEAN; }
static int jpeg_check_photoshop(cli_ctx *ctx, off_t offset) { int retval; const unsigned char *buffer; off_t old; fmap_t *map = *ctx->fmap; if(!(buffer = fmap_need_off_once(map, offset, 14))) { return 0; } if (memcmp(buffer, "Photoshop 3.0", 14) != 0) { return 0; } offset += 14; cli_dbgmsg("Found Photoshop segment\n"); do { old = offset; retval = jpeg_check_photoshop_8bim(ctx, &offset); if(offset <= old) break; } while (retval == 0); if (retval == -1) { retval = 0; } return retval; }
static int read_chunk(chm_metadata_t *metadata) { cli_dbgmsg("in read_chunk\n"); if (metadata->itsp_hdr.block_len < 8 || metadata->itsp_hdr.block_len > 33554432) { return CL_EFORMAT; } if (metadata->chunk_offset > metadata->m_length) { return CL_EFORMAT; } if ((metadata->chunk_offset + metadata->itsp_hdr.block_len) > metadata->m_length) { return CL_EFORMAT; } metadata->chunk_data = fmap_need_off_once(metadata->map, metadata->chunk_offset, metadata->itsp_hdr.block_len); if(!metadata->chunk_data) return CL_EFORMAT; metadata->chunk_current = metadata->chunk_data + CHM_CHUNK_HDR_LEN; metadata->chunk_end = metadata->chunk_data + metadata->itsp_hdr.block_len; if (memcmp(metadata->chunk_data, "PMGL", 4) == 0) { metadata->chunk_entries = (uint16_t)((((uint8_t const *)(metadata->chunk_data))[metadata->itsp_hdr.block_len-2] << 0) | (((uint8_t const *)(metadata->chunk_data))[metadata->itsp_hdr.block_len-1] << 8)); } else if (memcmp(metadata->chunk_data, "PMGI", 4) != 0) { return CL_BREAK; } return CL_SUCCESS; }
/* Normalizes the text in @fmap and stores the result in @state's buffer. * Returns number of characters written to buffer. */ size_t text_normalize_map(struct text_norm_state *state, fmap_t *map, size_t offset) { const unsigned char *map_loc; unsigned int map_pgsz; uint64_t map_len; size_t buff_len; size_t acc; size_t acc_total; size_t acc_len; map_len = map->len; map_pgsz = map->pgsz; buff_len = state->out_len; acc_total = 0; acc = 0; while (1) { /* Break out if we've reached the end of the map or our buffer. */ if(!(acc_len = MIN_3(map_pgsz, map_len - offset, buff_len - acc_total))) break; /* If map_loc is NULL, then there's nothing left to do but recover. */ if(!(map_loc = fmap_need_off_once(map, offset, acc_len))) break; offset += acc_len; /* If we didn't normalize anything, no need to update values, just break out. */ if(!(acc = text_normalize_buffer(state, map_loc, acc_len))) break; acc_total += acc; } return acc_total; }
int cli_check_riff_exploit(cli_ctx *ctx) { const uint32_t *buf; int big_endian, retval; off_t offset; fmap_t *map = *ctx->fmap; cli_dbgmsg("in cli_check_riff_exploit()\n"); if(!(buf = fmap_need_off_once(map, 0, 4*3))) return 0; if (memcmp(buf, "RIFF", 4) == 0) { big_endian = FALSE; } else if (memcmp(buf, "RIFX", 4) == 0) { big_endian = TRUE; } else { /* Not a RIFF file */ return 0; } if (memcmp(&buf[2], "ACON", 4) != 0) { /* Only scan MS animated icon files */ /* There is a *lot* of broken software out there that produces bad RIFF files */ return 0; } offset = 4*3; do { retval = riff_read_chunk(map, &offset, big_endian, 1); } while (retval == 1); return retval; }
int32_t cli_bcapi_read_number(struct cli_bc_ctx *ctx, uint32_t radix) { unsigned i; const char *p; int32_t result; if ((radix != 10 && radix != 16) || !ctx->fmap) return -1; cli_event_int(EV, BCEV_OFFSET, ctx->off); while ((p = fmap_need_off_once(ctx->fmap, ctx->off, BUF))) { for (i=0;i<BUF;i++) { if ((p[i] >= '0' && p[i] <= '9') || (radix == 16 && ((p[i] >= 'a' && p[i] <= 'f') || (p[i] >= 'A' && p[i] <= 'F')))) { char *endptr; p = fmap_need_ptr_once(ctx->fmap, p+i, 16); if (!p) return -1; result = strtoul(p, &endptr, radix); ctx->off += i + (endptr - p); return result; } } ctx->off += BUF; } return -1; }
/* Read in a block of data from either the mmap area or the given fd */ static int chm_read_data(fmap_t *map, char *dest, off_t offset, off_t len) { void *src = fmap_need_off_once(map, offset, len); if(!src) return FALSE; memcpy(dest, src, len); return TRUE; }
static unsigned int chdr(fmap_t *map, uint32_t coff, uint32_t zsize, unsigned int *fu, unsigned int fc, int *ret, cli_ctx *ctx, char *tmpd, zip_request_t *request) { char name[256]; int last = 0; const uint8_t *ch; if(!(ch = fmap_need_off(map, coff, SIZEOF_CH)) || CH_magic != 0x02014b50) { if(ch) fmap_unneed_ptr(map, ch, SIZEOF_CH); cli_dbgmsg("cli_unzip: ch - wrkcomplete\n"); return 0; } coff+=SIZEOF_CH; cli_dbgmsg("cli_unzip: ch - flags %x - method %x - csize %x - usize %x - flen %x - elen %x - clen %x - disk %x - off %x\n", CH_flags, CH_method, CH_csize, CH_usize, CH_flen, CH_elen, CH_clen, CH_dsk, CH_off); if(zsize-coff<=CH_flen) { cli_dbgmsg("cli_unzip: ch - fname out of file\n"); last=1; } name[0]='\0'; if((cli_debug_flag && !last) || request) { unsigned int size = (CH_flen>=sizeof(name))?sizeof(name)-1:CH_flen; const char *src = fmap_need_off_once(map, coff, size); if(src) { memcpy(name, src, size); name[size]='\0'; cli_dbgmsg("cli_unzip: ch - fname: %s\n", name); } } coff+=CH_flen; if(zsize-coff<=CH_elen && !last) { cli_dbgmsg("cli_unzip: ch - extra out of file\n"); last=1; } coff+=CH_elen; if(zsize-coff<CH_clen && !last) { cli_dbgmsg("cli_unzip: ch - comment out of file\n"); last = 1; } coff+=CH_clen; if (!request) { if(CH_off<zsize-SIZEOF_LH) { lhdr(map, CH_off, zsize-CH_off, fu, fc, ch, ret, ctx, tmpd, 1, zip_scan_cb); } else cli_dbgmsg("cli_unzip: ch - local hdr out of file\n"); } else { size_t len = MIN(sizeof(name)-1, request->namelen); if (!last && !strncmp(request->name, name, len)) { request->found = 1; request->loff = CH_off; } } fmap_unneed_ptr(map, ch, SIZEOF_CH); return (last?0:coff); }
static int jpeg_check_photoshop_8bim(cli_ctx *ctx, off_t *off) { const unsigned char *buf; uint16_t ntmp; uint8_t nlength, id[2]; uint32_t size; off_t offset = *off; int retval; fmap_t *map = *ctx->fmap; if(!(buf = fmap_need_off_once(map, offset, 4 + 2 + 1))) { cli_dbgmsg("read bim failed\n"); return -1; } if (memcmp(buf, "8BIM", 4) != 0) { cli_dbgmsg("missed 8bim\n"); return -1; } id[0] = (uint8_t)buf[4]; id[1] = (uint8_t)buf[5]; cli_dbgmsg("ID: 0x%.2x%.2x\n", id[0], id[1]); nlength = buf[6]; ntmp = nlength + ((((uint16_t)nlength)+1) & 0x01); offset += 4 + 2 + 1 + ntmp; if (fmap_readn(map, &size, offset, 4) != 4) { return -1; } size = special_endian_convert_32(size); if (size == 0) { return -1; } if ((size & 0x01) == 1) { size++; } *off = offset + 4 + size; /* Is it a thumbnail image: 0x0409 or 0x040c */ if ((id[0] == 0x04) && ((id[1] == 0x09) || (id[1] == 0x0c))) { /* Yes */ cli_dbgmsg("found thumbnail\n"); } else { /* No - Seek past record */ return 0; } /* Jump past header */ offset += 4 + 28; retval = cli_check_jpeg_exploit(ctx, offset); if (retval == 1) { cli_dbgmsg("Exploit found in thumbnail\n"); } return retval; }
int fmap_readn(fmap_t *m, void *dst, size_t at, size_t len) { char *src; if(at > m->len) return -1; if(len > m->len - at) len = m->len - at; src = fmap_need_off_once(m, at, len); if(!src) return -1; memcpy(dst, src, len); return len; }
static int riff_read_chunk(fmap_t *map, off_t *offset, int big_endian, int rec_level) { const uint32_t *buf; uint32_t chunk_size; off_t cur_offset = *offset; if (rec_level > 1000) { cli_dbgmsg("riff_read_chunk: recursion level exceeded\n"); return 0; } if(!(buf = fmap_need_off_once(map, cur_offset, 4*2))) return 0; cur_offset += 4*2; chunk_size = riff_endian_convert_32(buf[1], big_endian); if(!memcmp(buf, "anih", 4) && chunk_size != 36) return 2; if (memcmp(buf, "RIFF", 4) == 0) { return 0; } else if (memcmp(buf, "RIFX", 4) == 0) { return 0; } if ((memcmp(buf, "LIST", 4) == 0) || (memcmp(buf, "PROP", 4) == 0) || (memcmp(buf, "FORM", 4) == 0) || (memcmp(buf, "CAT ", 4) == 0)) { if (!fmap_need_ptr_once(map, buf+2, 4)) { cli_dbgmsg("riff_read_chunk: read list type failed\n"); return 0; } *offset = cur_offset+4; return riff_read_chunk(map, offset, big_endian, ++rec_level); } *offset = cur_offset + chunk_size + (chunk_size&1); if (*offset < cur_offset) { return 0; } /* FIXME: WTF!? if (lseek(fd, offset, SEEK_SET) != offset) { return 2; } */ return 1; }
/* Hashes a file onto the provided buffer and looks it up the cache. Returns CL_VIRUS if found, CL_CLEAN if not FIXME or a recoverable error, and returns CL_EREAD if unrecoverable */ int cache_check(unsigned char *hash, cli_ctx *ctx) { fmap_t *map; size_t todo, at = 0; void *hashctx; int ret; if(!ctx || !ctx->engine || !ctx->engine->cache) return CL_VIRUS; if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE) { cli_dbgmsg("cache_check: Caching disabled. Returning CL_VIRUS.\n"); return CL_VIRUS; } map = *ctx->fmap; todo = map->len; hashctx = cl_hash_init("md5"); if (!(hashctx)) return CL_VIRUS; while(todo) { const void *buf; size_t readme = todo < FILEBUFF ? todo : FILEBUFF; if(!(buf = fmap_need_off_once(map, at, readme))) { cl_hash_destroy(hashctx); return CL_EREAD; } todo -= readme; at += readme; if (cl_update_hash(hashctx, (void *)buf, readme)) { cl_hash_destroy(hashctx); cli_errmsg("cache_check: error reading while generating hash!\n"); return CL_EREAD; } } cl_finish_hash(hashctx, hash); ret = cache_lookup_hash(hash, map->len, ctx->engine->cache, ctx->recursion); cli_dbgmsg("cache_check: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x is %s\n", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], (ret == CL_VIRUS) ? "negative" : "positive"); return ret; }
int cli_check_mydoom_log(cli_ctx *ctx) { const uint32_t *record; uint32_t check, key; fmap_t *map = *ctx->fmap; unsigned int blocks = map->len / (8*4); cli_dbgmsg("in cli_check_mydoom_log()\n"); if(blocks<2) return CL_CLEAN; if(blocks>5) blocks = 5; record = fmap_need_off_once(map, 0, 8*4*blocks); if(!record) return CL_CLEAN; while(blocks) { /* This wasn't probably intended but that's what the current code does anyway */ if(record[--blocks] == 0xffffffff) return CL_CLEAN; } key = ~be32_to_host(record[0]); check = (be32_to_host(record[1])^key) + (be32_to_host(record[2])^key) + (be32_to_host(record[3])^key) + (be32_to_host(record[4])^key) + (be32_to_host(record[5])^key) + (be32_to_host(record[6])^key) + (be32_to_host(record[7])^key); if ((~check) != key) return CL_CLEAN; key = ~be32_to_host(record[8]); check = (be32_to_host(record[9])^key) + (be32_to_host(record[10])^key) + (be32_to_host(record[11])^key) + (be32_to_host(record[12])^key) + (be32_to_host(record[13])^key) + (be32_to_host(record[14])^key) + (be32_to_host(record[15])^key); if ((~check) != key) return CL_CLEAN; cli_append_virus(ctx, "Heuristics.Worm.Mydoom.M.log"); return CL_VIRUS; }
int cache_get_MD5(unsigned char *hash, cli_ctx *ctx) { fmap_t *map; size_t todo, at = 0; void *hashctx; map = *ctx->fmap; todo = map->len; hashctx = cl_hash_init("md5"); if (!(hashctx)) return CL_VIRUS; while(todo) { const void *buf; size_t readme = todo < FILEBUFF ? todo : FILEBUFF; if(!(buf = fmap_need_off_once(map, at, readme))) { cl_hash_destroy(hashctx); return CL_EREAD; } todo -= readme; at += readme; if (cl_update_hash(hashctx, (void *)buf, readme)) { cl_hash_destroy(hashctx); cli_errmsg("cache_check: error reading while generating hash!\n"); return CL_EREAD; } } cl_finish_hash(hashctx, hash); return CL_CLEAN; }
uint32_t cli_bcapi_disasm_x86(struct cli_bc_ctx *ctx, struct DISASM_RESULT *res, uint32_t len) { int n; const unsigned char *buf; const unsigned char* next; if (!res || !ctx->fmap || ctx->off >= ctx->fmap->len) { API_MISUSE(); return -1; } /* 32 should be longest instr we support decoding. * When we'll support mmx/sse instructions this should be updated! */ n = MIN(32, ctx->fmap->len - ctx->off); buf = fmap_need_off_once(ctx->fmap, ctx->off, n); if (buf) next = cli_disasm_one(buf, n, res, 0); else next = NULL; if (!next) { cli_dbgmsg("bcapi_disasm: failed\n"); cli_event_count(EV, BCEV_DISASM_FAIL); return -1; } return ctx->off + next - buf; }
int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx) { char md5[33]; unsigned int i; const char *virname=NULL; fmap_t *map; const char *ptr; uint8_t shash1[SHA1_HASH_SIZE*2+1]; uint8_t shash256[SHA256_HASH_SIZE*2+1]; int have_sha1, have_sha256, do_dsig_check = 1; stats_section_t sections; if(cli_hm_scan(digest, size, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(md5): Found false positive detection (fp sig: %s), size: %d\n", virname, (int)size); return CL_CLEAN; } else if(cli_hm_scan_wild(digest, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(md5): Found false positive detection (fp sig: %s), size: *\n", virname); return CL_CLEAN; } if(cli_debug_flag || ctx->engine->cb_hash) { for(i = 0; i < 16; i++) sprintf(md5 + i * 2, "%02x", digest[i]); md5[32] = 0; cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) size, cli_get_last_virus(ctx) ? cli_get_last_virus(ctx) : "Name"); } if(cli_get_last_virus(ctx)) do_dsig_check = strncmp("W32S.", cli_get_last_virus(ctx), 5); map = *ctx->fmap; have_sha1 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA1) || (cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 1) && do_dsig_check); have_sha256 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA256, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA256); if(have_sha1 || have_sha256) { if((ptr = fmap_need_off_once(map, 0, size))) { if(have_sha1) { cl_sha1(ptr, size, &shash1[SHA1_HASH_SIZE], NULL); if(cli_hm_scan(&shash1[SHA1_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha1): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } if(cli_hm_scan_wild(&shash1[SHA1_HASH_SIZE], &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha1): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } if(do_dsig_check && cli_hm_scan(&shash1[SHA1_HASH_SIZE], 1, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha1): Found false positive detection via catalog file\n"); return CL_CLEAN; } } if(have_sha256) { cl_sha256(ptr, size, &shash256[SHA256_HASH_SIZE], NULL); if(cli_hm_scan(&shash256[SHA256_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha256): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } if(cli_hm_scan_wild(&shash256[SHA256_HASH_SIZE], &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha256): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } } } } #ifdef HAVE__INTERNAL__SHA_COLLECT if((ctx->options & CL_SCAN_INTERNAL_COLLECT_SHA) && ctx->sha_collect>0) { if((ptr = fmap_need_off_once(map, 0, size))) { if(!have_sha256) cl_sha256(ptr, size, shash256+SHA256_HASH_SIZE, NULL); for(i=0; i<SHA256_HASH_SIZE; i++) sprintf((char *)shash256+i*2, "%02x", shash256[SHA256_HASH_SIZE+i]); if(!have_sha1) cl_sha1(ptr, size, shash1+SHA1_HASH_SIZE); for(i=0; i<SHA1_HASH_SIZE; i++) sprintf((char *)shash1+i*2, "%02x", shash1[SHA1_HASH_SIZE+i]); cli_errmsg("COLLECT:%s:%s:%u:%s:%s\n", shash256, shash1, size, cli_get_last_virus(ctx), ctx->entry_filename); } else cli_errmsg("can't compute sha\n!"); ctx->sha_collect = -1; } #endif memset(§ions, 0x00, sizeof(stats_section_t)); if(do_dsig_check || ctx->engine->cb_stats_add_sample) { uint32_t flags = (do_dsig_check ? CL_CHECKFP_PE_FLAG_AUTHENTICODE : 0); if (!(ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_STATS) && !(ctx->engine->dconf->stats & (DCONF_STATS_DISABLED | DCONF_STATS_PE_SECTION_DISABLED))) flags |= CL_CHECKFP_PE_FLAG_STATS; switch(cli_checkfp_pe(ctx, shash1, §ions, flags)) { case CL_CLEAN: cli_dbgmsg("cli_checkfp(pe): PE file whitelisted due to valid embedded digital signature\n"); return CL_CLEAN; case CL_VIRUS: if(cli_hm_scan(shash1, 2, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(pe): PE file whitelisted by catalog file\n"); return CL_CLEAN; } } } if (ctx->engine->cb_hash) ctx->engine->cb_hash(fmap_fd(*ctx->fmap), size, (const unsigned char *)md5, cli_get_last_virus(ctx), ctx->cb_ctx); if (ctx->engine->cb_stats_add_sample) ctx->engine->cb_stats_add_sample(cli_get_last_virus(ctx), digest, size, §ions, ctx->engine->stats_data); if (sections.sections) free(sections.sections); return CL_VIRUS; }
int cli_msexpand(cli_ctx *ctx, int ofd) { const struct msexp_hdr *hdr; uint8_t i, mask, bits; unsigned char buff[B_SIZE], wbuff[RW_SIZE]; const unsigned char *rbuff; unsigned int j = B_SIZE - 16, k, l, r = 0, w = 0, rbytes = 0, wbytes = 0; fmap_t *map = *ctx->fmap; off_t cur_off = sizeof(*hdr); unsigned int fsize; int ret; if(!(hdr = fmap_need_off_once(map, 0, sizeof(*hdr)))) return CL_EREAD; if(EC32(hdr->magic1) != MAGIC1 || EC32(hdr->magic2) != MAGIC2 || EC16(hdr->magic3) != MAGIC3) { cli_dbgmsg("MSEXPAND: Not supported file format\n"); return CL_EFORMAT; } fsize = EC32(hdr->fsize); cli_dbgmsg("MSEXPAND: File size from header: %u\n", fsize); if(cli_checklimits("MSEXPAND", ctx, fsize, 0, 0)!=CL_CLEAN) return CL_SUCCESS; memset(buff, 0, B_SIZE); while(1) { if(!rbytes || (r == rbytes)) { READBYTES; } bits = rbuff[r]; r++; mask = 1; for(i = 0; i < 8; i++) { if(bits & mask) { if(r == rbytes) { READBYTES; } if(w == RW_SIZE) { WRITEBYTES; } wbuff[w] = buff[j] = rbuff[r]; r++; w++; j++; j %= B_SIZE; } else { if(r == rbytes) { READBYTES; } k = rbuff[r]; r++; if(r == rbytes) { READBYTES; } l = rbuff[r]; r++; k += (l & 0xf0) << 4; l = (l & 0x0f) + 3; while(l--) { if(w == RW_SIZE) { WRITEBYTES; } wbuff[w] = buff[j] = buff[k]; w++; k++; k %= B_SIZE; j++; j %= B_SIZE; } } mask *= 2; } } if(w) { WRITEBYTES; } return CL_SUCCESS; }
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash) { //CHR ftype => type == CL_TYPE_TEXT_ASCII ? 0 : type //CHR ftonly => 0 cli_infomsg(NULL,"DEBUG: in cli_fmap_scandesc\n");//CHR const unsigned char *buff; int ret = CL_CLEAN, type = CL_CLEAN, bytes, compute_hash[CLI_HASH_AVAIL_TYPES]; unsigned int i = 0, bm_offmode = 0; uint32_t maxpatlen, offset = 0; struct cli_ac_data gdata, tdata; struct cli_bm_off toff; cli_md5_ctx md5ctx; SHA256_CTX sha256ctx; SHA1Context sha1ctx; unsigned char digest[CLI_HASH_AVAIL_TYPES][32]; struct cli_matcher *groot = NULL, *troot = NULL; struct cli_target_info info; fmap_t *map = *ctx->fmap; struct cli_matcher *hdb, *fp; const char *virname = NULL; uint32_t viroffset = 0; uint32_t viruses_found = 0; if(!ctx->engine) { cli_errmsg("cli_scandesc: engine == NULL\n"); return CL_ENULLARG; } cli_infomsg(NULL,"DEBUG: ftype=%d, ftonly=%d(using generic signatures for groot)\n",ftype,ftonly);//CHR if(!ftonly) groot = ctx->engine->root[0]; /* generic signatures */ //CHR in test.txt case, ftype=0, troot is for sepcific types: from 1 to CLI_MTARGETS if(ftype) { for(i = 1; i < CLI_MTARGETS; i++) { if(cli_mtargets[i].target == ftype) { troot = ctx->engine->root[i]; break; } } } if(ftonly) { if(!troot) return CL_CLEAN; maxpatlen = troot->maxpatlen; } else { if(troot) maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen); else{ maxpatlen = groot->maxpatlen; cli_infomsg(NULL,"DEBUG: no troot, maxpatlen using groot->maxpatlen=%d\n",groot->maxpatlen);//CHR } } targetinfo(&info, i, map); if(!ftonly) // CHR init AC scan structure? if((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) { if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } // not run in this case test.txt if(troot) { if((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(troot, &tdata, &info))) { if(!ftonly) cli_ac_freedata(&gdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } if(troot->bm_offmode) {// CHR offset mode if(map->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) { if((ret = cli_bm_initoff(troot, &toff, &info))) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } bm_offmode = 1; } } } hdb = ctx->engine->hm_hdb; fp = ctx->engine->hm_fp; // hdb related if(!ftonly && hdb) { //CHR if have hdb should init mn5 contex if(!refhash) { if(cli_hm_have_size(hdb, CLI_HASH_MD5, map->len) || cli_hm_have_size(fp, CLI_HASH_MD5, map->len)) { cli_md5_init(&md5ctx); compute_hash[CLI_HASH_MD5] = 1; } else compute_hash[CLI_HASH_MD5] = 0; } else { compute_hash[CLI_HASH_MD5] = 0; memcpy(digest[CLI_HASH_MD5], refhash, 16); } if(cli_hm_have_size(hdb, CLI_HASH_SHA1, map->len) || cli_hm_have_size(fp, CLI_HASH_SHA1, map->len)) { SHA1Init(&sha1ctx); compute_hash[CLI_HASH_SHA1] = 1; } else compute_hash[CLI_HASH_SHA1] = 0; if(cli_hm_have_size(hdb, CLI_HASH_SHA256, map->len) || cli_hm_have_size(fp, CLI_HASH_SHA256, map->len)) { sha256_init(&sha256ctx); compute_hash[CLI_HASH_SHA256] = 1; } else compute_hash[CLI_HASH_SHA256] = 0; } while(offset < map->len) { bytes = MIN(map->len - offset, SCANBUFF); if(!(buff = fmap_need_off_once(map, offset, bytes))) break; if(ctx->scanned) *ctx->scanned += bytes / CL_COUNT_PRECISION; // CHR not troot for this case if(troot) { cli_infomsg(NULL,"DEBUG: not troot, should not show this msg\n");//CHR virname = NULL; viroffset = 0; // CHR run match ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, acres, map, bm_offmode ? &toff : NULL, &viroffset, ctx); if (virname) { viruses_found++; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } } if(!ftonly) { cli_infomsg(NULL,"DEBUG: !ftonly=true, will run matcher_run with groot with generic sigs\n");//CHR virname = NULL; viroffset = 0; ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, acres, map, NULL, &viroffset, ctx); if (virname) { viruses_found++; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { cli_ac_freedata(&gdata); if(troot) { cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } else if((acmode & AC_SCAN_FT) && ret >= CL_TYPENO) { if(ret > type) type = ret; } if(hdb && !SCAN_ALL) { const void *data = buff + maxpatlen * (offset!=0); uint32_t data_len = bytes - maxpatlen * (offset!=0); if(compute_hash[CLI_HASH_MD5]) cli_md5_update(&md5ctx, data, data_len); if(compute_hash[CLI_HASH_SHA1]) SHA1Update(&sha1ctx, data, data_len); if(compute_hash[CLI_HASH_SHA256]) sha256_update(&sha256ctx, data, data_len); } } if(SCAN_ALL && viroffset) { offset = viroffset; continue; } if(bytes < SCANBUFF) break; offset += bytes - maxpatlen; } if(!ftonly && hdb) { enum CLI_HASH_TYPE hashtype, hashtype2; if(compute_hash[CLI_HASH_MD5]) cli_md5_final(digest[CLI_HASH_MD5], &md5ctx); if(refhash) compute_hash[CLI_HASH_MD5] = 1; if(compute_hash[CLI_HASH_SHA1]) SHA1Final(&sha1ctx, digest[CLI_HASH_SHA1]); if(compute_hash[CLI_HASH_SHA256]) sha256_final(&sha256ctx, digest[CLI_HASH_SHA256]); virname = NULL; for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) { if(compute_hash[hashtype] && (ret = cli_hm_scan(digest[hashtype], map->len, &virname, hdb, hashtype)) == CL_VIRUS) { if(fp) { for(hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) { if(compute_hash[hashtype2] && cli_hm_scan(digest[hashtype2], map->len, NULL, fp, hashtype2) == CL_VIRUS) { ret = CL_CLEAN; break; } } } if (ret == CL_VIRUS) { viruses_found++; cli_append_virus(ctx, virname); if (!SCAN_ALL) break; } virname = NULL; } } } if(troot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, troot, &tdata, &info, refhash); if (ret == CL_VIRUS) viruses_found++; cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(groot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, groot, &gdata, &info, refhash); cli_ac_freedata(&gdata); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); if (SCAN_ALL && viruses_found) return CL_VIRUS; if(ret == CL_VIRUS) return CL_VIRUS; return (acmode & AC_SCAN_FT) ? type : CL_CLEAN; }
/* Read and convert the HFS+ volume header */ static int hfsplus_volumeheader(cli_ctx *ctx, hfsPlusVolumeHeader **header) { hfsPlusVolumeHeader *volHeader; const uint8_t *mPtr; if (!header) { return CL_ENULLARG; } /* Start with volume header, 512 bytes at offset 1024 */ if ((*ctx->fmap)->len < 1536) { cli_dbgmsg("cli_scanhfsplus: too short for HFS+\n"); return CL_EFORMAT; } mPtr = fmap_need_off_once(*ctx->fmap, 1024, 512); if (!mPtr) { cli_errmsg("cli_scanhfsplus: cannot read header from map\n"); return CL_EMAP; } volHeader = cli_malloc(sizeof(hfsPlusVolumeHeader)); if (!volHeader) { cli_errmsg("cli_scanhfsplus: header malloc failed\n"); return CL_EMEM; } *header = volHeader; memcpy(volHeader, mPtr, 512); volHeader->signature = be16_to_host(volHeader->signature); volHeader->version = be16_to_host(volHeader->version); if ((volHeader->signature == 0x482B) && (volHeader->version == 4)) { cli_dbgmsg("cli_scanhfsplus: HFS+ signature matched\n"); } else if ((volHeader->signature == 0x4858) && (volHeader->version == 5)) { cli_dbgmsg("cli_scanhfsplus: HFSX v5 signature matched\n"); } else { cli_dbgmsg("cli_scanhfsplus: no matching signature\n"); return CL_EFORMAT; } /* skip fields that will definitely be ignored */ volHeader->attributes = be32_to_host(volHeader->attributes); volHeader->fileCount = be32_to_host(volHeader->fileCount); volHeader->folderCount = be32_to_host(volHeader->folderCount); volHeader->blockSize = be32_to_host(volHeader->blockSize); volHeader->totalBlocks = be32_to_host(volHeader->totalBlocks); cli_dbgmsg("HFS+ Header:\n"); cli_dbgmsg("Signature: %x\n", volHeader->signature); cli_dbgmsg("Attributes: %x\n", volHeader->attributes); cli_dbgmsg("File Count: " STDu32 "\n", volHeader->fileCount); cli_dbgmsg("Folder Count: " STDu32 "\n", volHeader->folderCount); cli_dbgmsg("Block Size: " STDu32 "\n", volHeader->blockSize); cli_dbgmsg("Total Blocks: " STDu32 "\n", volHeader->totalBlocks); /* Block Size must be power of 2 between 512 and 1 MB */ if ((volHeader->blockSize < 512) || (volHeader->blockSize > (1 << 20))) { cli_dbgmsg("cli_scanhfsplus: Invalid blocksize\n"); return CL_EFORMAT; } if (volHeader->blockSize & (volHeader->blockSize - 1)) { cli_dbgmsg("cli_scanhfsplus: Invalid blocksize\n"); return CL_EFORMAT; } forkdata_to_host(&(volHeader->allocationFile)); forkdata_to_host(&(volHeader->extentsFile)); forkdata_to_host(&(volHeader->catalogFile)); forkdata_to_host(&(volHeader->attributesFile)); forkdata_to_host(&(volHeader->startupFile)); if (cli_debug_flag) { forkdata_print("allocationFile", &(volHeader->allocationFile)); forkdata_print("extentsFile", &(volHeader->extentsFile)); forkdata_print("catalogFile", &(volHeader->catalogFile)); forkdata_print("attributesFile", &(volHeader->attributesFile)); forkdata_print("startupFile", &(volHeader->startupFile)); } return CL_CLEAN; }
int cli_scanxdp(cli_ctx *ctx) { #if HAVE_LIBXML2 xmlTextReaderPtr reader = NULL; fmap_t *map = *(ctx->fmap); const char *buf; const xmlChar *name, *value; char *decoded; size_t decodedlen; int rc = CL_SUCCESS; char *dumpname; size_t i; buf = (const char *)fmap_need_off_once(map, map->offset, map->len); if (!(buf)) return CL_EREAD; if (ctx->engine->keeptmp) { dumpname = dump_xdp(ctx, buf, map->len); if (dumpname) free(dumpname); } /* * Since a PDF file can contain embedded XDP documents, * it's possible that the filetyping code matched an embedded XDP document. * If that's the case, then xmlReaderForMemory will throw an error. For now, * silently ignore the error and return CL_SUCCESS so the filetyping code can * continue on. */ reader = xmlReaderForMemory(buf, (int)(map->len), "noname.xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); if (!(reader)) return CL_SUCCESS; while (xmlTextReaderRead(reader) == 1) { name = xmlTextReaderConstLocalName(reader); if (!(name)) continue; if (!strcmp((const char *)name, "chunk") && xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { value = xmlTextReaderReadInnerXml(reader); if (value) { decoded = cl_base64_decode((char *)value, strlen((const char *)value), NULL, &decodedlen, 0); if (decoded) { unsigned int shouldscan=0; if (decodedlen > 5) { for (i=0; i < MIN(MAGIC_BUFFER_SIZE, decodedlen-5); i++) { if (decoded[i] != '%') continue; if (decoded[i+1] == 'P' || decoded[i+1] == 'p') { if (decoded[i+2] == 'D' || decoded[i+2] == 'd') { if (decoded[i+3] == 'F' || decoded[i+3] == 'f') { if (decoded[i+4] == '-') { shouldscan=1; break; } } } } } } if (!shouldscan) { free(decoded); xmlFree((void *)value); break; } rc = cli_mem_scandesc(decoded, decodedlen, ctx); free(decoded); if (rc != CL_SUCCESS || rc == CL_BREAK) { xmlFree((void *)value); break; } } xmlFree((void *)value); } } } xmlFreeTextReader(reader); return rc; #else return CL_SUCCESS; #endif }
/* Read and convert the header node */ static int hfsplus_readheader(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsNodeDescriptor *nodeDesc, hfsHeaderRecord *headerRec, int headerType, const char *name) { const uint8_t *mPtr = NULL; off_t offset; uint32_t minSize, maxSize; /* From TN1150: Node Size must be power of 2 between 512 and 32768 */ /* Node Size for Catalog or Attributes must be at least 4096 */ maxSize = 32768; /* Doesn't seem to vary */ switch (headerType) { case HFS_FILETREE_ALLOCATION: offset = volHeader->allocationFile.extents[0].startBlock * volHeader->blockSize; minSize = 512; break; case HFS_FILETREE_EXTENTS: offset = volHeader->extentsFile.extents[0].startBlock * volHeader->blockSize; minSize = 512; break; case HFS_FILETREE_CATALOG: offset = volHeader->catalogFile.extents[0].startBlock * volHeader->blockSize; minSize = 4096; break; case HFS_FILETREE_ATTRIBUTES: offset = volHeader->attributesFile.extents[0].startBlock * volHeader->blockSize; minSize = 4096; break; case HFS_FILETREE_STARTUP: offset = volHeader->startupFile.extents[0].startBlock * volHeader->blockSize; minSize = 512; break; default: cli_errmsg("hfsplus_readheader: %s: invalid headerType %d\n", name, headerType); return CL_EARG; } mPtr = fmap_need_off_once(*ctx->fmap, offset, volHeader->blockSize); if (!mPtr) { cli_dbgmsg("hfsplus_header: %s: headerNode is out-of-range\n", name); return CL_EFORMAT; } /* Node descriptor first */ memcpy(nodeDesc, mPtr, sizeof(hfsNodeDescriptor)); nodedescriptor_to_host(nodeDesc); nodedescriptor_print(name, nodeDesc); if (nodeDesc->kind != HFS_NODEKIND_HEADER) { cli_dbgmsg("hfsplus_header: %s: headerNode not header kind\n", name); return CL_EFORMAT; } if ((nodeDesc->bLink != 0) || (nodeDesc->height != 0) || (nodeDesc->numRecords != 3)) { cli_dbgmsg("hfsplus_header: %s: Invalid headerNode\n", name); return CL_EFORMAT; } /* Then header record */ memcpy(headerRec, mPtr + sizeof(hfsNodeDescriptor), sizeof(hfsHeaderRecord)); headerrecord_to_host(headerRec); headerrecord_print(name, headerRec); if ((headerRec->nodeSize < minSize) || (headerRec->nodeSize > maxSize)) { cli_dbgmsg("hfsplus_header: %s: Invalid nodesize\n", name); return CL_EFORMAT; } if (headerRec->nodeSize & (headerRec->nodeSize - 1)) { cli_dbgmsg("hfsplus_header: %s: Invalid nodesize\n", name); return CL_EFORMAT; } /* KeyLength must be between 6 and 516 for catalog */ if (headerType == HFS_FILETREE_CATALOG) { if ((headerRec->maxKeyLength < 6) || (headerRec->maxKeyLength > 516)) { cli_dbgmsg("hfsplus_header: %s: Invalid cat maxKeyLength\n", name); return CL_EFORMAT; } if (headerRec->maxKeyLength > (headerRec->nodeSize / 2)) { cli_dbgmsg("hfsplus_header: %s: Invalid cat maxKeyLength based on nodeSize\n", name); return CL_EFORMAT; } } else if (headerType == HFS_FILETREE_EXTENTS) { if (headerRec->maxKeyLength != 10) { cli_dbgmsg("hfsplus_header: %s: Invalid ext maxKeyLength\n", name); return CL_EFORMAT; } } /* hdr->treeDepth = rootnode->height */ return CL_CLEAN; }
/* Read and dump a file for scanning */ static int hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *extHeader, hfsPlusForkData *fork, const char *dirname) { hfsPlusExtentDescriptor *currExt; const uint8_t *mPtr = NULL; char *tmpname = NULL; int ofd, ret = CL_CLEAN; uint64_t targetSize; uint32_t outputBlocks = 0; uint8_t ext; UNUSEDPARAM(extHeader); /* bad record checks */ if (!fork || (fork->logicalSize == 0) || (fork->totalBlocks == 0)) { cli_dbgmsg("hfsplus_dumpfile: Empty file.\n"); return CL_CLEAN; } /* check limits */ targetSize = fork->logicalSize; #if SIZEOF_LONG < 8 if (targetSize > ULONG_MAX) { cli_dbgmsg("hfsplus_dumpfile: File too large for limit check.\n"); return CL_EFORMAT; } #endif ret = cli_checklimits("hfsplus_scanfile", ctx, (unsigned long)targetSize, 0, 0); if (ret != CL_CLEAN) { return ret; } /* open file */ ret = cli_gentempfd(dirname, &tmpname, &ofd); if (ret != CL_CLEAN) { cli_dbgmsg("hfsplus_dumpfile: Cannot generate temporary file.\n"); return ret; } cli_dbgmsg("hfsplus_dumpfile: Extracting to %s\n", tmpname); ext = 0; /* Dump file, extent by extent */ do { uint32_t currBlock, endBlock, outputSize = 0; if (targetSize == 0) { cli_dbgmsg("hfsplus_dumpfile: output complete\n"); break; } if (outputBlocks >= fork->totalBlocks) { cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); break; } /* Prepare extent */ if (ext < 8) { currExt = &(fork->extents[ext]); cli_dbgmsg("hfsplus_dumpfile: extent %u\n", ext); } else { cli_dbgmsg("hfsplus_dumpfile: need next extent from ExtentOverflow\n"); /* Not implemented yet */ ret = CL_EFORMAT; break; } /* have extent, so validate and get block range */ if ((currExt->startBlock == 0) || (currExt->blockCount == 0)) { cli_dbgmsg("hfsplus_dumpfile: next extent empty, done\n"); break; } if ((currExt->startBlock & 0x10000000) && (currExt->blockCount & 0x10000000)) { cli_dbgmsg("hfsplus_dumpfile: next extent illegal!\n"); ret = CL_EFORMAT; break; } currBlock = currExt->startBlock; endBlock = currExt->startBlock + currExt->blockCount - 1; if ((currBlock > volHeader->totalBlocks) || (endBlock > volHeader->totalBlocks) || (currExt->blockCount > volHeader->totalBlocks)) { cli_dbgmsg("hfsplus_dumpfile: bad extent!\n"); ret = CL_EFORMAT; break; } /* Write the blocks, walking the map */ while (currBlock <= endBlock) { size_t to_write = MIN(targetSize, volHeader->blockSize); ssize_t written; off_t offset = currBlock * volHeader->blockSize; /* move map to next block */ mPtr = fmap_need_off_once(*ctx->fmap, offset, volHeader->blockSize); if (!mPtr) { cli_errmsg("hfsplus_dumpfile: map error\n"); ret = CL_EMAP; break; } written = cli_writen(ofd, mPtr, to_write); if ((size_t)written != to_write) { cli_errmsg("hfsplus_dumpfile: write error\n"); ret = CL_EWRITE; break; } targetSize -= to_write; outputSize += to_write; currBlock++; if (targetSize == 0) { cli_dbgmsg("hfsplus_dumpfile: all data written\n"); break; } if (outputBlocks >= fork->totalBlocks) { cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); break; } } /* Finished the extent, move to next */ ext++; } while (ret == CL_CLEAN); /* if successful so far, scan the output */ if (ret == CL_CLEAN) { ret = cli_magic_scandesc(ofd, tmpname, ctx); } if (ofd >= 0) { close(ofd); } if (!ctx->engine->keeptmp) { if (cli_unlink(tmpname)) { ret = CL_EUNLINK; } } free(tmpname); return ret; }
int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx) { char md5[33]; unsigned int i; const char *virname; SHA1Context sha1; SHA256_CTX sha256; fmap_t *map; const char *ptr; uint8_t shash1[SHA1_HASH_SIZE*2+1]; uint8_t shash256[SHA256_HASH_SIZE*2+1]; int have_sha1, have_sha256, do_dsig_check = 1; if(cli_hm_scan(digest, size, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(md5): Found false positive detection (fp sig: %s), size: %d\n", virname, (int)size); return CL_CLEAN; } else if(cli_hm_scan_wild(digest, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(md5): Found false positive detection (fp sig: %s), size: *\n", virname); return CL_CLEAN; } if(cli_debug_flag || ctx->engine->cb_hash) { for(i = 0; i < 16; i++) sprintf(md5 + i * 2, "%02x", digest[i]); md5[32] = 0; cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) size, cli_get_last_virus(ctx) ? cli_get_last_virus(ctx) : "Name"); } if(cli_get_last_virus(ctx)) do_dsig_check = strncmp("W32S.", cli_get_last_virus(ctx), 5); map = *ctx->fmap; have_sha1 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA1) || (cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 1) && do_dsig_check); have_sha256 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA256, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA256); if(have_sha1 || have_sha256) { if((ptr = fmap_need_off_once(map, 0, size))) { if(have_sha1) { SHA1Init(&sha1); SHA1Update(&sha1, ptr, size); SHA1Final(&sha1, &shash1[SHA1_HASH_SIZE]); if(cli_hm_scan(&shash1[SHA1_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha1): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } if(cli_hm_scan_wild(&shash1[SHA1_HASH_SIZE], &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha1): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } if(do_dsig_check && cli_hm_scan(&shash1[SHA1_HASH_SIZE], 1, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha1): Found false positive detection via catalog file\n"); return CL_CLEAN; } } if(have_sha256) { sha256_init(&sha256); sha256_update(&sha256, ptr, size); sha256_final(&sha256, &shash256[SHA256_HASH_SIZE]); if(cli_hm_scan(&shash256[SHA256_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha256): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } if(cli_hm_scan_wild(&shash256[SHA256_HASH_SIZE], &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(sha256): Found false positive detection (fp sig: %s)\n", virname); return CL_CLEAN; } } } } #ifdef HAVE__INTERNAL__SHA_COLLECT if((ctx->options & CL_SCAN_INTERNAL_COLLECT_SHA) && ctx->sha_collect>0) { if((ptr = fmap_need_off_once(map, 0, size))) { if(!have_sha256) { sha256_init(&sha256); sha256_update(&sha256, ptr, size); sha256_final(&sha256, &shash256[SHA256_HASH_SIZE]); } for(i=0; i<SHA256_HASH_SIZE; i++) sprintf((char *)shash256+i*2, "%02x", shash256[SHA256_HASH_SIZE+i]); if(!have_sha1) { SHA1Init(&sha1); SHA1Update(&sha1, ptr, size); SHA1Final(&sha1, &shash1[SHA1_HASH_SIZE]); } for(i=0; i<SHA1_HASH_SIZE; i++) sprintf((char *)shash1+i*2, "%02x", shash1[SHA1_HASH_SIZE+i]); cli_errmsg("COLLECT:%s:%s:%u:%s:%s\n", shash256, shash1, size, cli_get_last_virus(ctx), ctx->entry_filename); } else cli_errmsg("can't compute sha\n!"); ctx->sha_collect = -1; } #endif if(do_dsig_check) { switch(cli_checkfp_pe(ctx, shash1)) { case CL_CLEAN: cli_dbgmsg("cli_checkfp(pe): PE file whitelisted due to valid embedded digital signature\n"); return CL_CLEAN; case CL_VIRUS: if(cli_hm_scan(shash1, 2, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { cli_dbgmsg("cli_checkfp(pe): PE file whitelisted by catalog file\n"); return CL_CLEAN; } } } if (ctx->engine->cb_hash) ctx->engine->cb_hash(fmap_fd(*ctx->fmap), size, md5, cli_get_last_virus(ctx), ctx->cb_ctx); return CL_VIRUS; }
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash) { const unsigned char *buff; int ret = CL_CLEAN, type = CL_CLEAN, bytes, compute_hash[CLI_HASH_AVAIL_TYPES]; unsigned int i = 0, bm_offmode = 0; uint32_t maxpatlen, offset = 0; struct cli_ac_data gdata, tdata; struct cli_bm_off toff; cli_md5_ctx md5ctx; SHA256_CTX sha256ctx; SHA1Context sha1ctx; unsigned char digest[CLI_HASH_AVAIL_TYPES][32]; struct cli_matcher *groot = NULL, *troot = NULL; struct cli_target_info info; fmap_t *map = *ctx->fmap; struct cli_matcher *hdb, *fp; const char *virname = NULL; uint32_t viroffset = 0; uint32_t viruses_found = 0; if(!ctx->engine) { cli_errmsg("cli_scandesc: engine == NULL\n"); return CL_ENULLARG; } if(!ftonly) groot = ctx->engine->root[0]; /* generic signatures */ if(ftype) { for(i = 1; i < CLI_MTARGETS; i++) { if(cli_mtargets[i].target == ftype) { troot = ctx->engine->root[i]; break; } } } if(ftonly) { if(!troot) return CL_CLEAN; maxpatlen = troot->maxpatlen; } else { if(troot) maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen); else maxpatlen = groot->maxpatlen; } targetinfo(&info, i, map); if(!ftonly) if((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) { if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } if(troot) { if((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(troot, &tdata, &info))) { if(!ftonly) cli_ac_freedata(&gdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } if(troot->bm_offmode) { if(map->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) { if((ret = cli_bm_initoff(troot, &toff, &info))) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } bm_offmode = 1; } } } hdb = ctx->engine->hm_hdb; fp = ctx->engine->hm_fp; if(!ftonly && hdb) { if(!refhash) { if(cli_hm_have_size(hdb, CLI_HASH_MD5, map->len) || cli_hm_have_size(fp, CLI_HASH_MD5, map->len)) { cli_md5_init(&md5ctx); compute_hash[CLI_HASH_MD5] = 1; } else compute_hash[CLI_HASH_MD5] = 0; } else { compute_hash[CLI_HASH_MD5] = 0; memcpy(digest[CLI_HASH_MD5], refhash, 16); } if(cli_hm_have_size(hdb, CLI_HASH_SHA1, map->len) || cli_hm_have_wild(hdb, CLI_HASH_SHA1) || cli_hm_have_size(fp, CLI_HASH_SHA1, map->len) || cli_hm_have_wild(fp, CLI_HASH_SHA1) ) { SHA1Init(&sha1ctx); compute_hash[CLI_HASH_SHA1] = 1; } else compute_hash[CLI_HASH_SHA1] = 0; if(cli_hm_have_size(hdb, CLI_HASH_SHA256, map->len) || cli_hm_have_wild(hdb, CLI_HASH_SHA256) || cli_hm_have_size(fp, CLI_HASH_SHA256, map->len) || cli_hm_have_wild(fp, CLI_HASH_SHA256)) { sha256_init(&sha256ctx); compute_hash[CLI_HASH_SHA256] = 1; } else compute_hash[CLI_HASH_SHA256] = 0; } while(offset < map->len) { bytes = MIN(map->len - offset, SCANBUFF); if(!(buff = fmap_need_off_once(map, offset, bytes))) break; if(ctx->scanned) *ctx->scanned += bytes / CL_COUNT_PRECISION; if (ctx->engine->cb_progress && map->handle_is_fd && !ctx->engine->cb_progress((ssize_t) map->handle, bytes, ctx->engine->cb_progress_ctx)) return CL_BREAK; if(troot) { virname = NULL; viroffset = 0; ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, acres, map, bm_offmode ? &toff : NULL, &viroffset, ctx); if (virname) { /* virname already appended by matcher_run */ viruses_found = 1; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } } if(!ftonly) { virname = NULL; viroffset = 0; ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, acres, map, NULL, &viroffset, ctx); if (virname) { /* virname already appended by matcher_run */ viruses_found = 1; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { cli_ac_freedata(&gdata); if(troot) { cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } else if((acmode & AC_SCAN_FT) && ret >= CL_TYPENO) { if(ret > type) type = ret; } if(hdb && !SCAN_ALL) { const void *data = buff + maxpatlen * (offset!=0); uint32_t data_len = bytes - maxpatlen * (offset!=0); if(compute_hash[CLI_HASH_MD5]) cli_md5_update(&md5ctx, data, data_len); if(compute_hash[CLI_HASH_SHA1]) SHA1Update(&sha1ctx, data, data_len); if(compute_hash[CLI_HASH_SHA256]) sha256_update(&sha256ctx, data, data_len); } } if(SCAN_ALL && viroffset) { offset = viroffset; continue; } if(bytes < SCANBUFF) break; offset += bytes - maxpatlen; } if(!ftonly && hdb) { enum CLI_HASH_TYPE hashtype, hashtype2; if(compute_hash[CLI_HASH_MD5]) cli_md5_final(digest[CLI_HASH_MD5], &md5ctx); if(refhash) compute_hash[CLI_HASH_MD5] = 1; if(compute_hash[CLI_HASH_SHA1]) SHA1Final(&sha1ctx, digest[CLI_HASH_SHA1]); if(compute_hash[CLI_HASH_SHA256]) sha256_final(&sha256ctx, digest[CLI_HASH_SHA256]); virname = NULL; for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) { const char * virname_w = NULL; int found = 0; /* If no hash, skip to next type */ if(!compute_hash[hashtype]) continue; /* Do hash scan */ if((ret = cli_hm_scan(digest[hashtype], map->len, &virname, hdb, hashtype)) == CL_VIRUS) { found += 1; } if(!found || SCAN_ALL) { if ((ret = cli_hm_scan_wild(digest[hashtype], &virname_w, hdb, hashtype)) == CL_VIRUS) found += 2; } /* If found, do immediate hash-only FP check */ if (found && fp) { for(hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) { if(!compute_hash[hashtype2]) continue; if(cli_hm_scan(digest[hashtype2], map->len, NULL, fp, hashtype2) == CL_VIRUS) { found = 0; ret = CL_CLEAN; break; } else if(cli_hm_scan_wild(digest[hashtype2], NULL, fp, hashtype2) == CL_VIRUS) { found = 0; ret = CL_CLEAN; break; } } } /* If matched size-based hash ... */ if (found % 2) { viruses_found = 1; cli_append_virus(ctx, virname); if (!SCAN_ALL) break; virname = NULL; } /* If matched size-agnostic hash ... */ if (found > 1) { viruses_found = 1; cli_append_virus(ctx, virname_w); if (!SCAN_ALL) break; } } } if(troot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, troot, &tdata, &info, refhash); if (ret == CL_VIRUS) viruses_found++; cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(groot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, groot, &gdata, &info, refhash); cli_ac_freedata(&gdata); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); if (SCAN_ALL && viruses_found) return CL_VIRUS; if(ret == CL_VIRUS) return CL_VIRUS; return (acmode & AC_SCAN_FT) ? type : CL_CLEAN; }
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash) { const unsigned char *buff; int ret = CL_CLEAN, type = CL_CLEAN, bytes, compute_hash[CLI_HASH_AVAIL_TYPES]; unsigned int i = 0, j = 0, bm_offmode = 0; uint32_t maxpatlen, offset = 0; struct cli_ac_data gdata, tdata; struct cli_bm_off toff; struct cli_pcre_off gpoff, tpoff; unsigned char digest[CLI_HASH_AVAIL_TYPES][32]; struct cli_matcher *groot = NULL, *troot = NULL; struct cli_target_info info; fmap_t *map = *ctx->fmap; struct cli_matcher *hdb, *fp; const char *virname = NULL; uint32_t viruses_found = 0; void *md5ctx, *sha1ctx, *sha256ctx; if(!ctx->engine) { cli_errmsg("cli_scandesc: engine == NULL\n"); return CL_ENULLARG; } md5ctx = cl_hash_init("md5"); if (!(md5ctx)) return CL_EMEM; sha1ctx = cl_hash_init("sha1"); if (!(sha1ctx)) { cl_hash_destroy(md5ctx); return CL_EMEM; } sha256ctx = cl_hash_init("sha256"); if (!(sha256ctx)) { cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); return CL_EMEM; } if(!ftonly) groot = ctx->engine->root[0]; /* generic signatures */ if(ftype) { for(i = 1; i < CLI_MTARGETS; i++) { for (j = 0; j < cli_mtargets[i].target_count; ++j) { if(cli_mtargets[i].target[j] == ftype) { troot = ctx->engine->root[i]; break; } } if (troot) break; } } if(ftonly) { if(!troot) { cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return CL_CLEAN; } maxpatlen = troot->maxpatlen; } else { if(troot) maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen); else maxpatlen = groot->maxpatlen; } cli_targetinfo(&info, i, map); if(!ftonly) { if((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) { if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } if((ret = cli_pcre_recaloff(groot, &gpoff, &info, ctx))) { cli_ac_freedata(&gdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } } if(troot) { if((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(troot, &tdata, &info))) { if(!ftonly) { cli_ac_freedata(&gdata); cli_pcre_freeoff(&gpoff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } if(troot->bm_offmode) { if(map->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) { if((ret = cli_bm_initoff(troot, &toff, &info))) { if(!ftonly) { cli_ac_freedata(&gdata); cli_pcre_freeoff(&gpoff); } cli_ac_freedata(&tdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } bm_offmode = 1; } } if ((ret = cli_pcre_recaloff(troot, &tpoff, &info, ctx))) { if(!ftonly) { cli_ac_freedata(&gdata); cli_pcre_freeoff(&gpoff); } cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } } hdb = ctx->engine->hm_hdb; fp = ctx->engine->hm_fp; if(!ftonly && hdb) { if(!refhash) { if(cli_hm_have_size(hdb, CLI_HASH_MD5, map->len) || cli_hm_have_size(fp, CLI_HASH_MD5, map->len) || cli_hm_have_wild(hdb, CLI_HASH_MD5) || cli_hm_have_wild(fp, CLI_HASH_MD5)) { compute_hash[CLI_HASH_MD5] = 1; } else { compute_hash[CLI_HASH_MD5] = 0; } } else { compute_hash[CLI_HASH_MD5] = 0; memcpy(digest[CLI_HASH_MD5], refhash, 16); } if(cli_hm_have_size(hdb, CLI_HASH_SHA1, map->len) || cli_hm_have_wild(hdb, CLI_HASH_SHA1) || cli_hm_have_size(fp, CLI_HASH_SHA1, map->len) || cli_hm_have_wild(fp, CLI_HASH_SHA1) ) { compute_hash[CLI_HASH_SHA1] = 1; } else { compute_hash[CLI_HASH_SHA1] = 0; } if(cli_hm_have_size(hdb, CLI_HASH_SHA256, map->len) || cli_hm_have_wild(hdb, CLI_HASH_SHA256) || cli_hm_have_size(fp, CLI_HASH_SHA256, map->len) || cli_hm_have_wild(fp, CLI_HASH_SHA256)) { compute_hash[CLI_HASH_SHA256] = 1; } else { compute_hash[CLI_HASH_SHA256] = 0; } } while(offset < map->len) { bytes = MIN(map->len - offset, SCANBUFF); if(!(buff = fmap_need_off_once(map, offset, bytes))) break; if(ctx->scanned) *ctx->scanned += bytes / CL_COUNT_PRECISION; if(troot) { virname = NULL; ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, map, bm_offmode ? &toff : NULL, &tpoff, ctx); if (virname) { /* virname already appended by matcher_run */ viruses_found = 1; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { if(!ftonly) { cli_ac_freedata(&gdata); cli_pcre_freeoff(&gpoff); } cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); cli_pcre_freeoff(&tpoff); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } } if(!ftonly) { virname = NULL; ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, map, NULL, &gpoff, ctx); if (virname) { /* virname already appended by matcher_run */ viruses_found = 1; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { cli_ac_freedata(&gdata); cli_pcre_freeoff(&gpoff); if(troot) { cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); cli_pcre_freeoff(&tpoff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); return ret; } else if((acmode & AC_SCAN_FT) && ret >= CL_TYPENO) { if(ret > type) type = ret; } /* if (bytes <= (maxpatlen * (offset!=0))), it means the last window finished the file hashing * * since the last window is responsible for adding intersection between windows (maxpatlen) */ if(hdb && (bytes > (maxpatlen * (offset!=0)))) { const void *data = buff + maxpatlen * (offset!=0); uint32_t data_len = bytes - maxpatlen * (offset!=0); if(compute_hash[CLI_HASH_MD5]) cl_update_hash(md5ctx, (void *)data, data_len); if(compute_hash[CLI_HASH_SHA1]) cl_update_hash(sha1ctx, (void *)data, data_len); if(compute_hash[CLI_HASH_SHA256]) cl_update_hash(sha256ctx, (void *)data, data_len); } } if(bytes < SCANBUFF) break; offset += bytes - maxpatlen; } if(!ftonly && hdb) { enum CLI_HASH_TYPE hashtype, hashtype2; if(compute_hash[CLI_HASH_MD5]) { cl_finish_hash(md5ctx, digest[CLI_HASH_MD5]); md5ctx = NULL; } if(refhash) compute_hash[CLI_HASH_MD5] = 1; if(compute_hash[CLI_HASH_SHA1]) { cl_finish_hash(sha1ctx, digest[CLI_HASH_SHA1]); sha1ctx = NULL; } if(compute_hash[CLI_HASH_SHA256]) { cl_finish_hash(sha256ctx, digest[CLI_HASH_SHA256]); sha256ctx = NULL; } virname = NULL; for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) { const char * virname_w = NULL; int found = 0; /* If no hash, skip to next type */ if(!compute_hash[hashtype]) continue; /* Do hash scan */ if((ret = cli_hm_scan(digest[hashtype], map->len, &virname, hdb, hashtype)) == CL_VIRUS) { found += 1; } if(!found || SCAN_ALL) { if ((ret = cli_hm_scan_wild(digest[hashtype], &virname_w, hdb, hashtype)) == CL_VIRUS) found += 2; } /* If found, do immediate hash-only FP check */ if (found && fp) { for(hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) { if(!compute_hash[hashtype2]) continue; if(cli_hm_scan(digest[hashtype2], map->len, NULL, fp, hashtype2) == CL_VIRUS) { found = 0; ret = CL_CLEAN; break; } else if(cli_hm_scan_wild(digest[hashtype2], NULL, fp, hashtype2) == CL_VIRUS) { found = 0; ret = CL_CLEAN; break; } } } /* If matched size-based hash ... */ if (found % 2) { viruses_found = 1; cli_append_virus(ctx, virname); if (!SCAN_ALL) break; virname = NULL; } /* If matched size-agnostic hash ... */ if (found > 1) { viruses_found = 1; cli_append_virus(ctx, virname_w); if (!SCAN_ALL) break; } } } cl_hash_destroy(md5ctx); cl_hash_destroy(sha1ctx); cl_hash_destroy(sha256ctx); if(troot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_exp_eval(ctx, troot, &tdata, &info, (const char *)refhash); if (ret == CL_VIRUS) viruses_found++; cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); cli_pcre_freeoff(&tpoff); } if(groot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_exp_eval(ctx, groot, &gdata, &info, (const char *)refhash); cli_ac_freedata(&gdata); cli_pcre_freeoff(&gpoff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); if (SCAN_ALL && viruses_found) return CL_VIRUS; if(ret == CL_VIRUS) return CL_VIRUS; return (acmode & AC_SCAN_FT) ? type : CL_CLEAN; }
static inline int matcher_run(const struct cli_matcher *root, const unsigned char *buffer, uint32_t length, const char **virname, struct cli_ac_data *mdata, uint32_t offset, const struct cli_target_info *tinfo, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int acmode, unsigned int pcremode, struct cli_ac_result **acres, fmap_t *map, struct cli_bm_off *offdata, struct cli_pcre_off *poffdata, cli_ctx *ctx) { int ret, saved_ret = CL_CLEAN; int32_t pos = 0; struct filter_match_info info; uint32_t orig_length, orig_offset; const unsigned char* orig_buffer; unsigned int viruses_found = 0; if (root->filter) { if(filter_search_ext(root->filter, buffer, length, &info) == -1) { /* for safety always scan last maxpatlen bytes */ pos = length - root->maxpatlen - 1; if (pos < 0) pos = 0; PERF_LOG_FILTER(pos, length, root->type); } else { /* must not cut buffer for 64[4-4]6161, because we must be able to check * 64! */ pos = info.first_match - root->maxpatlen - 1; if (pos < 0) pos = 0; PERF_LOG_FILTER(pos, length, root->type); } } else { PERF_LOG_FILTER(0, length, root->type); } orig_length = length; orig_buffer = buffer; orig_offset = offset; length -= pos; buffer += pos; offset += pos; if (!root->ac_only) { PERF_LOG_TRIES(0, 1, length); if (root->bm_offmode) { /* Don't use prefiltering for BM offset mode, since BM keeps tracks * of offsets itself, and doesn't work if we skip chunks of input * data */ ret = cli_bm_scanbuff(orig_buffer, orig_length, virname, NULL, root, orig_offset, tinfo, offdata, ctx); } else { ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata, ctx); } if (ret != CL_CLEAN) { if (ret != CL_VIRUS) return ret; /* else (ret == CL_VIRUS) */ if (SCAN_ALL) viruses_found = 1; else { cli_append_virus(ctx, *virname); return ret; } } } PERF_LOG_TRIES(acmode, 0, length); ret = cli_ac_scanbuff(buffer, length, virname, NULL, acres, root, mdata, offset, ftype, ftoffset, acmode, ctx); if (ret != CL_CLEAN) { if (ret == CL_VIRUS) { if (SCAN_ALL) viruses_found = 1; else { cli_append_virus(ctx, *virname); return ret; } } else if (ret > CL_TYPENO && acmode & AC_SCAN_VIR) saved_ret = ret; else return ret; } /* due to logical triggered, pcres cannot be evaluated until after full subsig matching */ /* cannot save pcre execution state without possible evasion; must scan entire buffer */ /* however, scanning the whole buffer may require the whole buffer being loaded into memory */ #if HAVE_PCRE if (root->pcre_metas) { int rc; uint64_t maxfilesize; if (map && (pcremode == PCRE_SCAN_FMAP)) { if (offset+length >= map->len) { /* check that scanned map does not exceed pcre maxfilesize limit */ maxfilesize = (uint64_t)cl_engine_get_num(ctx->engine, CL_ENGINE_PCRE_MAX_FILESIZE, &rc); if (rc != CL_SUCCESS) return rc; if (maxfilesize && (map->len > maxfilesize)) { cli_dbgmsg("matcher_run: pcre max filesize (map) exceeded (limit: %llu, needed: %llu)\n", (long long unsigned)maxfilesize, (long long unsigned)map->len); return CL_EMAXSIZE; } cli_dbgmsg("matcher_run: performing regex matching on full map: %u+%u(%u) >= %zu\n", offset, length, offset+length, map->len); buffer = fmap_need_off_once(map, 0, map->len); if (!buffer) return CL_EMEM; /* scan the full buffer */ ret = cli_pcre_scanbuf(buffer, map->len, virname, acres, root, mdata, poffdata, ctx); } } else if (pcremode == PCRE_SCAN_BUFF) { /* check that scanned buffer does not exceed pcre maxfilesize limit */ maxfilesize = (uint64_t)cl_engine_get_num(ctx->engine, CL_ENGINE_PCRE_MAX_FILESIZE, &rc); if (rc != CL_SUCCESS) return rc; if (maxfilesize && (length > maxfilesize)) { cli_dbgmsg("matcher_run: pcre max filesize (buf) exceeded (limit: %llu, needed: %u)\n", (long long unsigned)maxfilesize, length); return CL_EMAXSIZE; } cli_dbgmsg("matcher_run: performing regex matching on buffer with no map: %u+%u(%u)\n", offset, length, offset+length); /* scan the specified buffer */ ret = cli_pcre_scanbuf(buffer, length, virname, acres, root, mdata, poffdata, ctx); } } #endif /* HAVE_PCRE */ /* end experimental fragment */ if (ctx && !SCAN_ALL && ret == CL_VIRUS) cli_append_virus(ctx, *virname); if (ctx && SCAN_ALL && viruses_found) return CL_VIRUS; if (saved_ret && ret == CL_CLEAN) return saved_ret; return ret; }
static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, struct cl_engine *engine) { struct cli_asn1 asn1, deep, deeper; uint8_t sha1[SHA1_HASH_SIZE], issuer[SHA1_HASH_SIZE], md[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE]; const uint8_t *message, *attrs; unsigned int dsize, message_size, attrs_size; cli_crt_hashtype hashtype; cli_crt *x509; void *ctx; int result; int isBlacklisted = 0; cli_dbgmsg("in asn1_parse_mscat\n"); do { if(!(message = fmap_need_off_once(map, offset, 1))) { cli_dbgmsg("asn1_parse_mscat: failed to read pkcs#7 entry\n"); break; } if(asn1_expect_objtype(map, message, &size, &asn1, 0x30)) /* SEQUENCE */ break; /* if(size) { */ /* cli_dbgmsg("asn1_parse_mscat: found extra data after pkcs#7 %u\n", size); */ /* break; */ /* } */ size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x06, lenof(OID_signedData), OID_signedData)) /* OBJECT 1.2.840.113549.1.7.2 - contentType = signedData */ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0xa0)) /* [0] - content */ break; if(size) { cli_dbgmsg("asn1_parse_mscat: found extra data in pkcs#7\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* SEQUENCE */ break; if(size) { cli_dbgmsg("asn1_parse_mscat: found extra data in signedData\n"); break; } size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* INTEGER - VERSION 1 */ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x31)) /* SET OF DigestAlgorithmIdentifier */ break; if(asn1_expect_algo(map, &asn1.content, &asn1.size, lenof(OID_sha1), OID_sha1)) /* DigestAlgorithmIdentifier[0] == sha1 */ break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: only one digestAlgorithmIdentifier is allowed\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* SEQUENCE - contentInfo */ break; /* Here there is either a PKCS #7 ContentType Object Identifier for Certificate Trust List (szOID_CTL) * or a single SPC_INDIRECT_DATA_OBJID */ if( (!embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, 0x06, lenof(OID_szOID_CTL), OID_szOID_CTL)) || (embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, 0x06, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID)) ) break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0xa0)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: found extra data in contentInfo\n"); break; } dsize = deep.size; if(asn1_expect_objtype(map, deep.content, &dsize, &deep, 0x30)) break; if(dsize) { cli_dbgmsg("asn1_parse_mscat: found extra data in content\n"); break; } *hashes = deep.content; *hashes_size = deep.size; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* certificates */ break; dsize = asn1.size; if(dsize) { crtmgr newcerts; crtmgr_init(&newcerts); while(dsize) { if(asn1_get_x509(map, &asn1.content, &dsize, cmgr, &newcerts)) { dsize = 1; break; } } if(dsize) break; if(newcerts.crts) { x509 = newcerts.crts; cli_dbgmsg("asn1_parse_mscat: %u new certificates collected\n", newcerts.items); while(x509) { cli_crt *parent = crtmgr_verify_crt(cmgr, x509); /* Dump the cert if requested before anything happens to it */ if (engine->dconf->pe & PE_CONF_DUMPCERT) { char issuer[SHA1_HASH_SIZE*2+1], subject[SHA1_HASH_SIZE*2+1], serial[SHA1_HASH_SIZE*2+1]; char mod[1024], exp[1024]; int j=1024; fp_toradix_n(&x509->n, mod, 16, j); fp_toradix_n(&x509->e, exp, 16, j); for (j=0; j < SHA1_HASH_SIZE; j++) { sprintf(&issuer[j*2], "%02x", x509->issuer[j]); sprintf(&subject[j*2], "%02x", x509->subject[j]); sprintf(&serial[j*2], "%02x", x509->serial[j]); } cli_dbgmsg_internal("cert subject:%s serial:%s pubkey:%s i:%s %lu->%lu %s %s %s\n", subject, serial, mod, issuer, (unsigned long)x509->not_before, (unsigned long)x509->not_after, x509->certSign ? "cert" : "", x509->codeSign ? "code" : "", x509->timeSign ? "time" : ""); } if(parent) { if (parent->isBlacklisted) { isBlacklisted = 1; cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as virus.\n", (parent->name ? parent->name : "(no name)")); } x509->codeSign &= parent->codeSign; x509->timeSign &= parent->timeSign; if(crtmgr_add(cmgr, x509)) break; crtmgr_del(&newcerts, x509); x509 = newcerts.crts; continue; } x509 = x509->next; } if(x509) break; if(newcerts.items) cli_dbgmsg("asn1_parse_mscat: %u certificates did not verify\n", newcerts.items); crtmgr_free(&newcerts); } } if(asn1_get_obj(map, asn1.next, &size, &asn1)) break; if(asn1.type == 0xa1 && asn1_get_obj(map, asn1.next, &size, &asn1)) /* crls - unused shouldn't be present */ break; if(asn1.type != 0x31) { /* signerInfos */ cli_dbgmsg("asn1_parse_mscat: unexpected type %02x for signerInfos\n", asn1.type); break; } if(size) { cli_dbgmsg("asn1_parse_mscat: unexpected extra data after signerInfos\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: only one signerInfo shall be present\n"); break; } size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* Version = 1 */ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* issuerAndSerialNumber */ break; dsize = asn1.size; if(asn1_expect_objtype(map, asn1.content, &dsize, &deep, 0x30)) /* issuer */ break; if(map_sha1(map, deep.content, deep.size, issuer)) break; if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x02)) /* serial */ break; if(map_sha1(map, deep.content, deep.size, serial)) break; if(dsize) { cli_dbgmsg("asn1_parse_mscat: extra data inside issuerAndSerialNumber\n"); break; } if(asn1_expect_algo(map, &asn1.next, &size, lenof(OID_sha1), OID_sha1)) /* digestAlgorithm == sha1 */ break; attrs = asn1.next; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* authenticatedAttributes */ break; attrs_size = (uint8_t *)(asn1.next) - attrs; if(attrs_size < 2) { cli_dbgmsg("asn1_parse_mscat: authenticatedAttributes size is too small\n"); break; } dsize = asn1.size; deep.next = asn1.content; result = 0; while(dsize) { struct cli_asn1 cobj; int content; if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x30)) { /* attribute */ dsize = 1; break; } if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, 0x06)) { /* attribute type */ dsize = 1; break; } if(deeper.size != lenof(OID_contentType)) continue; if(!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n"); dsize = 1; break; } if(!memcmp(deeper.content, OID_contentType, lenof(OID_contentType))) content = 0; /* contentType */ else if(!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest))) content = 1; /* messageDigest */ else continue; if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, 0x31)) { /* set - contents */ dsize = 1; break; } if(deep.size) { cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attributes\n"); dsize = 1; break; } if(result & (1<<content)) { cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest appear twice\n"); dsize = 1; break; } if(content == 0) { /* contentType */ if( (!embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, 0x06, lenof(OID_szOID_CTL), OID_szOID_CTL)) || /* cat file */ (embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, 0x06, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID)) /* embedded cat */ ) { dsize = 1; break; } result |= 1; } else { /* messageDigest */ if(asn1_expect_objtype(map, deeper.content, &deeper.size, &cobj, 0x04)) { dsize = 1; break; } if(cobj.size != SHA1_HASH_SIZE) { cli_dbgmsg("asn1_parse_mscat: messageDigest attribute has got the wrong size (%u)\n", cobj.size); dsize = 1; break; } if(!fmap_need_ptr_once(map, cobj.content, SHA1_HASH_SIZE)) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n"); dsize = 1; break; } memcpy(md, cobj.content, SHA1_HASH_SIZE); result |= 2; } if(deeper.size) { cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attribute\n"); dsize = 1; break; } } if(dsize) break; if(result != 3) { cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest are missing\n"); break; } if(asn1_expect_algo(map, &asn1.next, &size, lenof(OID_rsaEncryption), OID_rsaEncryption)) /* digestEncryptionAlgorithm == sha1 */ break; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x04)) /* encryptedDigest */ break; if(asn1.size > 513) { cli_dbgmsg("asn1_parse_mscat: encryptedDigest too long\n"); break; } if(map_sha1(map, *hashes, *hashes_size, sha1)) break; if(memcmp(sha1, md, sizeof(sha1))) { cli_dbgmsg("asn1_parse_mscat: messageDigest mismatch\n"); break; } if(!fmap_need_ptr_once(map, attrs, attrs_size)) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n"); break; } ctx = cl_hash_init("sha1"); if (!(ctx)) break; cl_update_hash(ctx, "\x31", 1); cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); cl_finish_hash(ctx, sha1); if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) { cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n"); break; } if(!(x509 = crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, CLI_SHA1RSA, sha1, VRFY_CODE))) { cli_dbgmsg("asn1_parse_mscat: pkcs7 signature verification failed\n"); break; } message = asn1.content; message_size = asn1.size; if(!size) { cli_dbgmsg("asn1_parse_mscat: countersignature is missing\n"); break; } if(size && asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa1)) /* unauthenticatedAttributes */ break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside signerInfo\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside unauthenticatedAttributes\n"); break; } size = asn1.size; /* 1.2.840.113549.1.9.6 - counterSignature */ if(asn1_expect_obj(map, &asn1.content, &size, 0x06, lenof(OID_countersignature), OID_countersignature)) break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x31)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside counterSignature\n"); break; } size = asn1.size; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) break; if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside unauthenticatedAttributes\n"); break; } size = asn1.size; if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* Version = 1*/ break; if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* issuerAndSerialNumber */ break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x30)) /* issuer */ break; if(map_sha1(map, deep.content, deep.size, issuer)) break; if(asn1_expect_objtype(map, deep.next, &asn1.size, &deep, 0x02)) /* serial */ break; if(map_sha1(map, deep.content, deep.size, serial)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: extra data inside countersignature issuer\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* digestAlgorithm */ break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x06)) break; if(deep.size != lenof(OID_sha1) && deep.size != lenof(OID_md5)) { cli_dbgmsg("asn1_parse_mscat: wrong digestAlgorithm size\n"); break; } if(!fmap_need_ptr_once(map, deep.content, deep.size)) { cli_dbgmsg("asn1_parse_mscat: failed to read digestAlgorithm OID\n"); break; } if(deep.size == lenof(OID_sha1) && !memcmp(deep.content, OID_sha1, lenof(OID_sha1))) { hashtype = CLI_SHA1RSA; if(map_sha1(map, message, message_size, md)) break; } else if(deep.size == lenof(OID_md5) && !memcmp(deep.content, OID_md5, lenof(OID_md5))) { hashtype = CLI_MD5RSA; if(map_md5(map, message, message_size, md)) break; } else { cli_dbgmsg("asn1_parse_mscat: unknown digest oid in countersignature\n"); break; } if(asn1.size && asn1_expect_obj(map, &deep.next, &asn1.size, 0x05, 0, NULL)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: extra data in countersignature oid\n"); break; } attrs = asn1.next; if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* authenticatedAttributes */ break; attrs_size = (uint8_t *)(asn1.next) - attrs; if(attrs_size < 2) { cli_dbgmsg("asn1_parse_mscat: countersignature authenticatedAttributes are too small\n"); break; } result = 0; dsize = asn1.size; deep.next = asn1.content; while(dsize) { int content; if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x30)) { /* attribute */ dsize = 1; break; } if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, 0x06)) { /* attribute type */ dsize = 1; break; } if(deeper.size != lenof(OID_contentType)) /* lenof(contentType) = lenof(messageDigest) = lenof(signingTime) = 9 */ continue; if(!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) { dsize = 1; break; } if(!memcmp(deeper.content, OID_contentType, lenof(OID_contentType))) content = 0; /* contentType */ else if(!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest))) content = 1; /* messageDigest */ else if(!memcmp(deeper.content, OID_signingTime, lenof(OID_signingTime))) content = 2; /* signingTime */ else continue; if(result & (1<<content)) { cli_dbgmsg("asn1_parse_mscat: duplicate field in countersignature\n"); dsize = 1; break; } result |= (1<<content); if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, 0x31)) { /* attribute type */ dsize = 1; break; } if(deep.size) { cli_dbgmsg("asn1_parse_mscat: extra data in countersignature value\n"); dsize = 1; break; } deep.size = deeper.size; switch(content) { case 0: /* contentType = pkcs7-data */ if(asn1_expect_obj(map, &deeper.content, &deep.size, 0x06, lenof(OID_pkcs7_data), OID_pkcs7_data)) deep.size = 1; else if(deep.size) cli_dbgmsg("asn1_parse_mscat: extra data in countersignature content-type\n"); break; case 1: /* messageDigest */ if(asn1_expect_obj(map, &deeper.content, &deep.size, 0x04, (hashtype == CLI_SHA1RSA) ? SHA1_HASH_SIZE : 16, md)) { deep.size = 1; cli_dbgmsg("asn1_parse_mscat: countersignature hash mismatch\n"); } else if(deep.size) cli_dbgmsg("asn1_parse_mscat: extra data in countersignature message-digest\n"); break; case 2: /* signingTime */ { time_t sigdate; /* FIXME shall i use it?! */ if(asn1_get_time(map, &deeper.content, &deep.size, &sigdate)) deep.size = 1; else if(deep.size) cli_dbgmsg("asn1_parse_mscat: extra data in countersignature signing-time\n"); else if(sigdate < x509->not_before || sigdate > x509->not_after) { cli_dbgmsg("asn1_parse_mscat: countersignature timestamp outside cert validity\n"); deep.size = 1; } break; } } if(deep.size) { dsize = 1; break; } } if(dsize) break; if(result != 7) { cli_dbgmsg("asn1_parse_mscat: some important attributes are missing in countersignature\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* digestEncryptionAlgorithm == sha1 */ break; if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x06)) /* digestEncryptionAlgorithm == sha1 */ break; if(deep.size != lenof(OID_rsaEncryption)) { /* lenof(OID_rsaEncryption) = lenof(OID_sha1WithRSAEncryption) = 9 */ cli_dbgmsg("asn1_parse_mscat: wrong digestEncryptionAlgorithm size in countersignature\n"); break; } if(!fmap_need_ptr_once(map, deep.content, lenof(OID_rsaEncryption))) { cli_dbgmsg("asn1_parse_mscat: cannot read digestEncryptionAlgorithm in countersignature\n"); break; } /* rsaEncryption or sha1withRSAEncryption */ if(memcmp(deep.content, OID_rsaEncryption, lenof(OID_rsaEncryption)) && memcmp(deep.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption))) { cli_dbgmsg("asn1_parse_mscat: digestEncryptionAlgorithm in countersignature is not sha1\n"); break; } if(asn1.size && asn1_expect_obj(map, &deep.next, &asn1.size, 0x05, 0, NULL)) break; if(asn1.size) { cli_dbgmsg("asn1_parse_mscat: extra data in digestEncryptionAlgorithm in countersignature\n"); break; } if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x04)) /* encryptedDigest */ break; if(asn1.size > 513) { cli_dbgmsg("asn1_parse_mscat: countersignature encryptedDigest too long\n"); break; } if(size) { cli_dbgmsg("asn1_parse_mscat: extra data inside countersignature\n"); break; } if(!fmap_need_ptr_once(map, attrs, attrs_size)) { cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n"); break; } if(hashtype == CLI_SHA1RSA) { ctx = cl_hash_init("sha1"); if (!(ctx)) break; cl_update_hash(ctx, "\x31", 1); cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); cl_finish_hash(ctx, sha1); } else { ctx = cl_hash_init("md5"); if (!(ctx)) break; cl_update_hash(ctx, "\x31", 1); cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); cl_finish_hash(ctx, sha1); } if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) { cli_dbgmsg("asn1_parse_mscat: failed to read countersignature encryptedDigest\n"); break; } if(!crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, sha1, VRFY_TIME)) { cli_dbgmsg("asn1_parse_mscat: pkcs7 countersignature verification failed\n"); break; } cli_dbgmsg("asn1_parse_mscat: catalog successfully parsed\n"); if (isBlacklisted) { return 1; } return 0; } while(0); cli_dbgmsg("asn1_parse_mscat: failed to parse catalog\n"); return 1; }