libclamav/libmspack.c
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;
 }