d3699d57 |
/*
* Author: 웃 Sebastian Andrzej Siewior |
6289eda8 |
* Summary: Glue code for libmspack handling.
*
* Acknowledgements: ClamAV uses Stuart Caie's libmspack to parse as number of
* Microsoft file formats. |
d3699d57 |
* ✉ sebastian @ breakpoint ̣cc
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h> |
87c800a7 |
#include <stdarg.h> |
8dbf92a4 |
#include <stdio.h> |
d3699d57 |
#include <mspack.h>
#include "clamav.h"
#include "fmap.h"
#include "scanners.h"
#include "others.h"
enum mspack_type {
FILETYPE_DUNNO,
FILETYPE_FMAP,
FILETYPE_FILENAME,
};
struct mspack_name {
fmap_t *fmap;
off_t org;
};
struct mspack_system_ex {
struct mspack_system ops;
off_t max_size;
};
struct mspack_handle {
enum mspack_type type;
fmap_t *fmap;
off_t org;
off_t offset;
FILE *f;
off_t max_size;
};
static struct mspack_file *mspack_fmap_open(struct mspack_system *self,
const char *filename, int mode)
{
struct mspack_name *mspack_name;
struct mspack_handle *mspack_handle;
struct mspack_system_ex *self_ex;
const char *fmode; |
11dbd436 |
const struct mspack_system *mptr = self; |
d3699d57 |
if (!filename) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
return NULL;
}
mspack_handle = malloc(sizeof(*mspack_handle)); |
c21235eb |
memset(mspack_handle, 0, (sizeof(*mspack_handle))); |
d3699d57 |
if (!mspack_handle) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
return NULL;
} |
520b0fd3 |
memset(mspack_handle, 0, sizeof(*mspack_handle));
|
d3699d57 |
switch (mode) {
case MSPACK_SYS_OPEN_READ:
mspack_handle->type = FILETYPE_FMAP;
mspack_name = (struct mspack_name *)filename;
mspack_handle->fmap = mspack_name->fmap;
mspack_handle->org = mspack_name->org;
mspack_handle->offset = 0;
return (struct mspack_file *)mspack_handle;
case MSPACK_SYS_OPEN_WRITE:
fmode = "wb";
break;
case MSPACK_SYS_OPEN_UPDATE:
fmode = "r+b";
break;
case MSPACK_SYS_OPEN_APPEND:
fmode = "ab";
break;
default:
cli_dbgmsg("%s() wrong mode\n", __func__);
goto out_err;
}
mspack_handle->type = FILETYPE_FILENAME;
mspack_handle->f = fopen(filename, fmode);
if (!mspack_handle->f) {
cli_dbgmsg("%s() failed %d\n", __func__, __LINE__);
goto out_err;
} |
11dbd436 |
self_ex = (struct mspack_system_ex *)((char *)mptr - offsetof(struct mspack_system_ex,ops)); |
d3699d57 |
mspack_handle->max_size = self_ex->max_size;
return (struct mspack_file *)mspack_handle;
out_err: |
c21235eb |
memset(mspack_handle, 0, (sizeof(*mspack_handle))); |
d3699d57 |
free(mspack_handle); |
c21235eb |
mspack_handle = NULL; |
d3699d57 |
return NULL;
}
static void mspack_fmap_close(struct mspack_file *file)
{
struct mspack_handle *mspack_handle = (struct mspack_handle *)file;
if (!mspack_handle)
return;
if (mspack_handle->type == FILETYPE_FILENAME) |
c21235eb |
if (mspack_handle->f)
fclose(mspack_handle->f);
memset(mspack_handle, 0, (sizeof(*mspack_handle))); |
d3699d57 |
free(mspack_handle); |
c21235eb |
mspack_handle = NULL;
return; |
d3699d57 |
}
static int mspack_fmap_read(struct mspack_file *file, void *buffer, int bytes)
{
struct mspack_handle *mspack_handle = (struct mspack_handle *)file;
off_t offset;
size_t count;
int ret;
if (bytes < 0) {
cli_dbgmsg("%s() %d\n", __func__, __LINE__);
return -1;
}
if (!mspack_handle) {
cli_dbgmsg("%s() %d\n", __func__, __LINE__);
return -1;
}
if (mspack_handle->type == FILETYPE_FMAP) {
offset = mspack_handle->offset + mspack_handle->org;
ret = fmap_readn(mspack_handle->fmap, buffer, offset, bytes);
if (ret != bytes) {
cli_dbgmsg("%s() %d %d, %d\n", __func__, __LINE__, bytes, ret);
return ret;
}
mspack_handle->offset += bytes;
return bytes;
}
count = fread(buffer, bytes, 1, mspack_handle->f);
if (count < 1) {
cli_dbgmsg("%s() %d %d, %zd\n", __func__, __LINE__, bytes, count);
return -1;
}
return bytes;
}
static int mspack_fmap_write(struct mspack_file *file, void *buffer, int bytes)
{
struct mspack_handle *mspack_handle = (struct mspack_handle *)file;
size_t count;
off_t max_size;
if (bytes < 0 || !mspack_handle) {
cli_dbgmsg("%s() err %d\n", __func__, __LINE__);
return -1;
}
if (mspack_handle->type == FILETYPE_FMAP) {
cli_dbgmsg("%s() err %d\n", __func__, __LINE__);
return -1;
}
if (!bytes)
return 0;
max_size = mspack_handle->max_size;
if (!max_size)
return bytes;
|
11dbd436 |
max_size = max_size < (off_t) bytes ? max_size : (off_t) bytes;
|
d3699d57 |
mspack_handle->max_size -= max_size;
count = fwrite(buffer, max_size, 1, mspack_handle->f);
if (count < 1) {
cli_dbgmsg("%s() err %m <%zd %d>\n", __func__, count, bytes);
return -1;
}
return bytes;
}
static int mspack_fmap_seek(struct mspack_file *file, off_t offset, int mode)
{
struct mspack_handle *mspack_handle = (struct mspack_handle *)file;
if (!mspack_handle) {
cli_dbgmsg("%s() err %d\n", __func__, __LINE__);
return -1;
}
if (mspack_handle->type == FILETYPE_FMAP) {
off_t new_pos;
switch (mode) {
case MSPACK_SYS_SEEK_START:
new_pos = offset;
break;
case MSPACK_SYS_SEEK_CUR:
new_pos = mspack_handle->offset + offset;
break;
case MSPACK_SYS_SEEK_END:
new_pos = mspack_handle->fmap->len + offset;
break;
default:
cli_dbgmsg("%s() err %d\n", __func__, __LINE__);
return -1;
} |
e09d8843 |
if (new_pos < 0 || new_pos > (off_t)mspack_handle->fmap->len) { |
d3699d57 |
cli_dbgmsg("%s() err %d\n", __func__, __LINE__);
return -1;
}
mspack_handle->offset = new_pos;
return 0;
}
switch (mode) {
case MSPACK_SYS_SEEK_START:
mode = SEEK_SET;
break;
case MSPACK_SYS_SEEK_CUR:
mode = SEEK_CUR;
break;
case MSPACK_SYS_SEEK_END:
mode = SEEK_END;
break;
default:
cli_dbgmsg("%s() err %d\n", __func__, __LINE__);
return -1;
}
|
3a639e63 |
return fseek(mspack_handle->f, offset, mode); |
d3699d57 |
}
static off_t mspack_fmap_tell(struct mspack_file *file)
{
struct mspack_handle *mspack_handle = (struct mspack_handle *)file;
if (!mspack_handle)
return -1;
if (mspack_handle->type == FILETYPE_FMAP)
return mspack_handle->offset;
|
3a639e63 |
return (off_t) ftell(mspack_handle->f); |
d3699d57 |
}
static void mspack_fmap_message(struct mspack_file *file, const char *fmt, ...)
{ |
e09d8843 |
UNUSEDPARAM(file); |
7cc40cba |
if (UNLIKELY(cli_debug_flag)) {
va_list args; |
8dbf92a4 |
char buff[BUFSIZ];
size_t len = sizeof("LibClamAV debug: ") - 1; |
7cc40cba |
|
8dbf92a4 |
memset(buff, 0, BUFSIZ); |
7cc40cba |
/* Add the prefix */ |
8dbf92a4 |
strncpy(buff, "LibClamAV debug: ", len); |
7cc40cba |
va_start(args, fmt);
vsnprintf(buff + len, sizeof(buff) - len - 2, fmt, args);
va_end(args);
/* Add a newline and a null terminator */
buff[strlen(buff)] = '\n'; |
0cf59ddd |
buff[strlen(buff) + 1] = '\0'; |
7cc40cba |
fputs(buff, stderr);
} |
d3699d57 |
} |
7cc40cba |
|
d3699d57 |
static void *mspack_fmap_alloc(struct mspack_system *self, size_t num)
{ |
e09d8843 |
UNUSEDPARAM(self); |
520b0fd3 |
void * addr = malloc(num);
if (addr) {
memset(addr, 0, num);
}
return addr; |
d3699d57 |
}
static void mspack_fmap_free(void *mem)
{ |
a8377368 |
if(mem) {
free(mem);
mem = NULL;
}
return; |
d3699d57 |
}
static void mspack_fmap_copy(void *src, void *dst, size_t num)
{
memcpy(dst, src, num);
}
static struct mspack_system mspack_sys_fmap_ops = {
.open = mspack_fmap_open,
.close = mspack_fmap_close,
.read = mspack_fmap_read,
.write = mspack_fmap_write,
.seek = mspack_fmap_seek,
.tell = mspack_fmap_tell,
.message = mspack_fmap_message,
.alloc = mspack_fmap_alloc,
.free = mspack_fmap_free,
.copy = mspack_fmap_copy,
};
static int cli_scanfile(const char *filename, cli_ctx *ctx)
{ |
dd0df814 |
int fd, ret = 0; |
d3699d57 |
/* internal version of cl_scanfile with arec/mrec preserved */
fd = safe_open(filename, O_RDONLY|O_BINARY);
if (fd < 0) |
dd0df814 |
return ret; |
d3699d57 |
|
d39cb658 |
ret = cli_magic_scandesc(fd, filename, ctx); |
d3699d57 |
close(fd);
return ret;
}
int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
{
struct mscab_decompressor *cab_d;
struct mscabd_cabinet *cab_h;
struct mscabd_file *cab_f; |
d731a939 |
int ret = 0;
int files; |
d3699d57 |
int virus_num = 0;
struct mspack_name mspack_fmap = {
.fmap = *ctx->fmap,
.org = sfx_offset,
}; |
11dbd436 |
struct mspack_system_ex ops_ex;
memset(&ops_ex, 0, sizeof(struct mspack_system_ex));
ops_ex.ops = mspack_sys_fmap_ops; |
d3699d57 |
cab_d = mspack_create_cab_decompressor(&ops_ex.ops);
if (!cab_d) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
return CL_EUNPACK;
}
|
40109256 |
cab_d->set_param(cab_d, MSCABD_PARAM_FIXMSZIP, 1); |
7b563ca7 |
#if MSCABD_PARAM_SALVAGE |
40109256 |
cab_d->set_param(cab_d, MSCABD_PARAM_SALVAGE, 1); |
7b563ca7 |
#endif |
40109256 |
|
d3699d57 |
cab_h = cab_d->open(cab_d, (char *)&mspack_fmap);
if (!cab_h) {
ret = CL_EFORMAT;
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
goto out_dest;
}
files = 0;
for (cab_f = cab_h->files; cab_f; cab_f = cab_f->next) {
off_t max_size; |
c21235eb |
char *tmp_fname = NULL; |
d3699d57 |
ret = cli_matchmeta(ctx, cab_f->filename, 0, cab_f->length, 0,
files, 0, NULL);
if (ret) {
if (ret == CL_VIRUS) {
virus_num++; |
d7979d4f |
if (!SCAN_ALLMATCHES) |
d3699d57 |
break;
}
goto out_close;
}
if (ctx->engine->maxscansize) {
if (ctx->scansize >= ctx->engine->maxscansize) {
ret = CL_CLEAN;
break;
}
}
if (ctx->engine->maxscansize &&
ctx->scansize + ctx->engine->maxfilesize >=
ctx->engine->maxscansize)
max_size = ctx->engine->maxscansize -
ctx->scansize;
else
max_size = ctx->engine->maxfilesize ?
ctx->engine->maxfilesize :
0xffffffff;
tmp_fname = cli_gentemp(ctx->engine->tmpdir);
if (!tmp_fname) {
ret = CL_EMEM;
break;
}
ops_ex.max_size = max_size;
/* scan */
ret = cab_d->extract(cab_d, cab_f, tmp_fname);
if (ret)
/* Failed to extract. Try to scan what is there */
cli_dbgmsg("%s() failed to extract %d\n", __func__, ret);
ret = cli_scanfile(tmp_fname, ctx);
if (ret == CL_VIRUS)
virus_num++;
if (!ctx->engine->keeptmp) {
if (!access(tmp_fname, R_OK) && cli_unlink(tmp_fname)) {
free(tmp_fname);
ret = CL_EUNLINK;
break;
}
}
free(tmp_fname);
files++; |
d7979d4f |
if (ret == CL_VIRUS && SCAN_ALLMATCHES) |
d3699d57 |
continue;
if (ret)
break;
}
out_close:
cab_d->close(cab_d, cab_h);
out_dest:
mspack_destroy_cab_decompressor(cab_d);
if (virus_num)
return CL_VIRUS;
return ret;
}
int cli_scanmschm(cli_ctx *ctx)
{
struct mschm_decompressor *mschm_d;
struct mschmd_header *mschm_h;
struct mschmd_file *mschm_f; |
927b2bab |
int ret = CL_CLEAN; // Default CLEAN in case CHM contains no files. |
d3699d57 |
int files;
int virus_num = 0;
struct mspack_name mspack_fmap = {
.fmap = *ctx->fmap,
}; |
11dbd436 |
struct mspack_system_ex ops_ex;
memset(&ops_ex, 0, sizeof(struct mspack_system_ex));
ops_ex.ops = mspack_sys_fmap_ops; |
d3699d57 |
mschm_d = mspack_create_chm_decompressor(&ops_ex.ops);
if (!mschm_d) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
return CL_EUNPACK;
}
mschm_h = mschm_d->open(mschm_d, (char *)&mspack_fmap);
if (!mschm_h) {
ret = CL_EFORMAT;
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
goto out_dest;
}
files = 0;
for (mschm_f = mschm_h->files; mschm_f; mschm_f = mschm_f->next) {
off_t max_size;
char *tmp_fname;
ret = cli_matchmeta(ctx, mschm_f->filename, 0, mschm_f->length,
0, files, 0, NULL);
if (ret) {
if (ret == CL_VIRUS) {
virus_num++; |
d7979d4f |
if (!SCAN_ALLMATCHES) |
d3699d57 |
break;
}
goto out_close;
}
if (ctx->engine->maxscansize) {
if (ctx->scansize >= ctx->engine->maxscansize) {
ret = CL_CLEAN;
break;
}
}
if (ctx->engine->maxscansize &&
ctx->scansize + ctx->engine->maxfilesize >=
ctx->engine->maxscansize)
max_size = ctx->engine->maxscansize -
ctx->scansize;
else
max_size = ctx->engine->maxfilesize ?
ctx->engine->maxfilesize :
0xffffffff;
ops_ex.max_size = max_size;
tmp_fname = cli_gentemp(ctx->engine->tmpdir);
if (!tmp_fname) {
ret = CL_EMEM;
break;
}
/* scan */
ret = mschm_d->extract(mschm_d, mschm_f, tmp_fname);
if (ret)
/* Failed to extract. Try to scan what is there */
cli_dbgmsg("%s() failed to extract %d\n", __func__, ret);
ret = cli_scanfile(tmp_fname, ctx);
if (ret == CL_VIRUS)
virus_num++;
if (!ctx->engine->keeptmp) {
if (!access(tmp_fname, R_OK) && cli_unlink(tmp_fname)) {
free(tmp_fname);
ret = CL_EUNLINK;
break;
}
}
free(tmp_fname);
files++; |
d7979d4f |
if (ret == CL_VIRUS && SCAN_ALLMATCHES) |
d3699d57 |
continue;
if (ret)
break;
}
out_close:
mschm_d->close(mschm_d, mschm_h);
out_dest:
mspack_destroy_chm_decompressor(mschm_d);
if (virus_num)
return CL_VIRUS;
return ret;
} |