diff --git a/ChangeLog b/ChangeLog index 3fb4bffb4..b3744bbad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Tue Jul 14 18:17:59 CEST 2009 (tk) +---------------------------------- + * libclamav: add support for Universal Binaries (archives with Mach-O files for + different architectures, bb#1592) + Mon Jul 13 21:40:51 CEST 2009 (tk) ---------------------------------- * docs/signatures.pdf: cover Mach-O files diff --git a/libclamav/cpio.c b/libclamav/cpio.c index 524601ab0..cbc05e2b6 100644 --- a/libclamav/cpio.c +++ b/libclamav/cpio.c @@ -80,70 +80,7 @@ struct cpio_hdr_newc { char check[8]; }; -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -static int cpio_scanfile(int fd, uint32_t size, cli_ctx *ctx) -{ - int newfd, bread, sum = 0, ret; - char buff[FILEBUFF]; - char *name; - - - if(!(name = cli_gentemp(ctx->engine->tmpdir))) - return CL_EMEM; - - if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { - cli_errmsg("cpio_scanfile: Can't create file %s\n", name); - free(name); - return CL_ECREAT; - } - - while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) { - if((uint32_t) (sum + bread) >= size) { - if(write(newfd, buff, size - sum) == -1) { - cli_errmsg("cpio_scanfile: Can't write to %s\n", name); - cli_unlink(name); - free(name); - close(newfd); - return CL_EWRITE; - } - break; - } else { - if(write(newfd, buff, bread) == -1) { - cli_errmsg("cpio_scanfile: Can't write to %s\n", name); - cli_unlink(name); - free(name); - close(newfd); - return CL_EWRITE; - } - } - sum += bread; - } - cli_dbgmsg("CPIO: Extracted to %s\n", name); - lseek(newfd, 0, SEEK_SET); - if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS) - cli_dbgmsg("cpio_scanfile: Infected with %s\n", *ctx->virname); - - close(newfd); - if(!ctx->engine->keeptmp) { - if(cli_unlink(name)) { - free(name); - return CL_EUNLINK; - } - } - free(name); - return ret; -} - -static inline uint16_t EC16(uint16_t v, int c) -{ - if(!c) - return v; - else - return ((v >> 8) + (v << 8)); -} +#define EC16(v, conv) (conv ? cbswap16(v) : v) static void sanitname(char *name) { @@ -213,7 +150,7 @@ int cli_scancpio_old(int fd, cli_ctx *ctx) if(ret == CL_EMAXFILES) { return ret; } else if(ret == CL_SUCCESS) { - ret = cpio_scanfile(fd, filesize, ctx); + ret = cli_dumpscan(fd, 0, filesize, ctx); if(ret == CL_VIRUS) return ret; } @@ -287,7 +224,7 @@ int cli_scancpio_odc(int fd, cli_ctx *ctx) if(ret == CL_EMAXFILES) { return ret; } else if(ret == CL_SUCCESS) { - ret = cpio_scanfile(fd, filesize, ctx); + ret = cli_dumpscan(fd, 0, filesize, ctx); if(ret == CL_VIRUS) return ret; } @@ -363,7 +300,7 @@ int cli_scancpio_newc(int fd, cli_ctx *ctx, int crc) if(ret == CL_EMAXFILES) { return ret; } else if(ret == CL_SUCCESS) { - ret = cpio_scanfile(fd, filesize, ctx); + ret = cli_dumpscan(fd, 0, filesize, ctx); if(ret == CL_VIRUS) return ret; } diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c index aebca7619..0cbc096c0 100644 --- a/libclamav/filetypes.c +++ b/libclamav/filetypes.c @@ -57,6 +57,7 @@ static const struct ftmap_s { { "CL_TYPE_MSEXE", CL_TYPE_MSEXE }, { "CL_TYPE_ELF", CL_TYPE_ELF }, { "CL_TYPE_MACHO", CL_TYPE_MACHO }, + { "CL_TYPE_MACHO_UNIBIN", CL_TYPE_MACHO_UNIBIN }, { "CL_TYPE_POSIX_TAR", CL_TYPE_POSIX_TAR }, { "CL_TYPE_OLD_TAR", CL_TYPE_OLD_TAR }, { "CL_TYPE_CPIO_OLD", CL_TYPE_CPIO_OLD }, diff --git a/libclamav/filetypes.h b/libclamav/filetypes.h index 5a47f88ec..da6433da0 100644 --- a/libclamav/filetypes.h +++ b/libclamav/filetypes.h @@ -42,6 +42,7 @@ typedef enum { CL_TYPE_PE_DISASM, CL_TYPE_ELF, CL_TYPE_MACHO, + CL_TYPE_MACHO_UNIBIN, CL_TYPE_POSIX_TAR, CL_TYPE_OLD_TAR, CL_TYPE_CPIO_OLD, diff --git a/libclamav/filetypes_int.h b/libclamav/filetypes_int.h index f58b23b68..1e1f1fa90 100644 --- a/libclamav/filetypes_int.h +++ b/libclamav/filetypes_int.h @@ -148,6 +148,7 @@ static const char *ftypes_int[] = { "0:0:cffaedfe:Mach-O LE 64-bit:CL_TYPE_ANY:CL_TYPE_MACHO:45", "0:0:feedface:Mach-O BE:CL_TYPE_ANY:CL_TYPE_MACHO:45", "0:0:feedfacf:Mach-O BE 64-bit:CL_TYPE_ANY:CL_TYPE_MACHO:45", + "0:0:cafebabe:Universal Binary:CL_TYPE_ANY:CL_TYPE_MACHO_UNIBIN:46", NULL }; diff --git a/libclamav/macho.c b/libclamav/macho.c index f07e15fb1..5b06aff83 100644 --- a/libclamav/macho.c +++ b/libclamav/macho.c @@ -153,6 +153,21 @@ struct macho_thread_state_x86 uint32_t gs; }; +struct macho_fat_header +{ + uint32_t magic; + uint32_t nfats; +}; + +struct macho_fat_arch +{ + uint32_t cputype; + uint32_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; +}; + #define RETURN_BROKEN \ if(matcher) \ return -1; \ @@ -477,3 +492,57 @@ int cli_machoheader(int fd, struct cli_exe_info *fileinfo) { return cli_scanmacho(fd, NULL, fileinfo); } + +int cli_scanmacho_unibin(int fd, cli_ctx *ctx) +{ + struct macho_fat_header fat_header; + struct macho_fat_arch fat_arch; + unsigned int conv, i, matcher = 0; + int ret = CL_CLEAN; + struct stat sb; + off_t pos; + + if(fstat(fd, &sb) == -1) { + cli_dbgmsg("cli_scanmacho_unibin: fstat failed for fd %d\n", fd); + return CL_ESTAT; + } + + if(read(fd, &fat_header, sizeof(fat_header)) != sizeof(fat_header)) { + cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_header\n"); + return CL_EFORMAT; + } + + if(fat_header.magic == 0xcafebabe) { + conv = 0; + } else if(fat_header.magic == 0xbebafeca) { + conv = 1; + } else { + cli_dbgmsg("cli_scanmacho_unibin: Incorrect magic\n"); + return CL_EFORMAT; + } + + fat_header.nfats = EC32(fat_header.nfats, conv); + if(fat_header.nfats > 32) { + cli_dbgmsg("cli_scanmacho_unibin: Invalid number of architectures\n"); + RETURN_BROKEN; + } + cli_dbgmsg("UNIBIN: Number of architectures: %u\n", (unsigned int) fat_header.nfats); + for(i = 0; i < fat_header.nfats; i++) { + if(read(fd, &fat_arch, sizeof(fat_arch)) != sizeof(fat_arch)) { + cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_arch\n"); + RETURN_BROKEN; + } + pos = lseek(fd, 0, SEEK_CUR); + fat_arch.offset = EC32(fat_arch.offset, conv); + fat_arch.size = EC32(fat_arch.size, conv); + cli_dbgmsg("UNIBIN: Binary %u of %u\n", i + 1, fat_header.nfats); + cli_dbgmsg("UNIBIN: File offset: %u\n", fat_arch.offset); + cli_dbgmsg("UNIBIN: File size: %u\n", fat_arch.size); + ret = cli_dumpscan(fd, fat_arch.offset, fat_arch.size, ctx); + lseek(fd, pos, SEEK_SET); + if(ret == CL_VIRUS) + break; + } + + return ret; /* result from the last binary */ +} diff --git a/libclamav/macho.h b/libclamav/macho.h index bce0de8ce..d31a5640f 100644 --- a/libclamav/macho.h +++ b/libclamav/macho.h @@ -26,5 +26,6 @@ int cli_scanmacho(int fd, cli_ctx *ctx, struct cli_exe_info *fileinfo); int cli_machoheader(int fd, struct cli_exe_info *fileinfo); +int cli_scanmacho_unibin(int fd, cli_ctx *ctx); #endif diff --git a/libclamav/others.c b/libclamav/others.c index ac2cc9d63..83dbd94ef 100644 --- a/libclamav/others.c +++ b/libclamav/others.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Sourcefire, Inc. + * Copyright (C) 2007-2009 Sourcefire, Inc. * * Authors: Tomasz Kojm, Trog * @@ -837,6 +837,65 @@ int cli_rmdirs(const char *dirname) } #endif +int cli_dumpscan(int fd, off_t offset, size_t size, cli_ctx *ctx) +{ + int newfd, bread, sum = 0, ret; + char buff[FILEBUFF]; + char *name; + + if(offset) { + if(lseek(fd, offset, SEEK_SET) == -1) { + cli_dbgmsg("cli_dumpscan: Can't lseek to %u\n", (unsigned int) offset); + return CL_EFORMAT; /* most likely because of corrupted file */ + } + } + + if(!(name = cli_gentemp(ctx->engine->tmpdir))) + return CL_EMEM; + + if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { + cli_errmsg("cli_dumpscan: Can't create file %s\n", name); + free(name); + return CL_ECREAT; + } + + while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) { + if((uint32_t) (sum + bread) >= size) { + if(write(newfd, buff, size - sum) == -1) { + cli_errmsg("cli_dumpscan: Can't write to %s\n", name); + cli_unlink(name); + free(name); + close(newfd); + return CL_EWRITE; + } + break; + } else { + if(write(newfd, buff, bread) == -1) { + cli_errmsg("cli_dumpscan: Can't write to %s\n", name); + cli_unlink(name); + free(name); + close(newfd); + return CL_EWRITE; + } + } + sum += bread; + } + cli_dbgmsg("DUMP&SCAN: File extracted to %s\n", name); + lseek(newfd, 0, SEEK_SET); + if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS) + cli_dbgmsg("cli_dumpscan: Infected with %s\n", *ctx->virname); + + close(newfd); + if(!ctx->engine->keeptmp) { + if(cli_unlink(name)) { + free(name); + return CL_EUNLINK; + } + } + free(name); + return ret; +} + /* Implement a generic bitset, trog@clamav.net */ #define BITS_PER_CHAR (8) diff --git a/libclamav/others.h b/libclamav/others.h index b5a140cdc..21b7c2914 100644 --- a/libclamav/others.h +++ b/libclamav/others.h @@ -45,7 +45,7 @@ * in re-enabling affected modules. */ -#define CL_FLEVEL 45 +#define CL_FLEVEL 46 #define CL_FLEVEL_DCONF CL_FLEVEL extern uint8_t cli_debug_flag; @@ -381,6 +381,7 @@ char *cli_gentemp(const char *dir); int cli_gentempfd(const char *dir, char **name, int *fd); unsigned int cli_rndnum(unsigned int max); int cli_filecopy(const char *src, const char *dest); +int cli_dumpscan(int fd, off_t offset, size_t size, cli_ctx *ctx); bitset_t *cli_bitset_init(void); void cli_bitset_free(bitset_t *bs); int cli_bitset_set(bitset_t *bs, unsigned long bit_offset); diff --git a/libclamav/scanners.c b/libclamav/scanners.c index 4741ef7dc..22985cd1c 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -2090,6 +2090,11 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx) ret = cli_scanmacho(desc, ctx, NULL); break; + case CL_TYPE_MACHO_UNIBIN: + if(ctx->dconf->macho) + ret = cli_scanmacho_unibin(desc, ctx); + break; + case CL_TYPE_SIS: if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_SIS)) ret = cli_scansis(desc, ctx);