mirror of https://github.com/Cisco-Talos/clamav
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
12 KiB
435 lines
12 KiB
/* An implementation of the mspack_system interface which can access many
|
|
* things:
|
|
* - regular disk files
|
|
* - already opened stdio FILE* file pointers
|
|
* - open file descriptors
|
|
* - blocks of memory
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <mspack.h>
|
|
|
|
/* definitions */
|
|
|
|
#define MTYPE_DISKFILE (0x01)
|
|
#define MTYPE_STDIOFH (0x02)
|
|
#define MTYPE_FILEDESC (0x03)
|
|
#define MTYPE_MEMORY (0x04)
|
|
|
|
struct m_filename {
|
|
unsigned char type; /* one of MTYPE_DISKFILE, STDIOFH, FILEDESC or MEMORY */
|
|
const char *filename; /* the user-friendly printable filename (may be NULL) */
|
|
union {
|
|
const char *diskfile; /* char *filename for MTYPE_DISKFILE */
|
|
FILE *stdiofh; /* FILE *existing_fh for MTYPE_STDIOFH */
|
|
int filedesc; /* int file_descriptor for MTYPE_FILEDESC */
|
|
struct {
|
|
unsigned char *data;
|
|
size_t length;
|
|
} memory;
|
|
} x;
|
|
};
|
|
|
|
struct m_file {
|
|
struct m_filename *file; /* pointer back to the m_filename data */
|
|
union {
|
|
FILE *fh; /* only used in DISKFILE, STDIOFH and FILEDESC types */
|
|
size_t position; /* only used in MEMORY types */
|
|
} x;
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* mspack_system implementation */
|
|
|
|
static void *m_alloc(struct mspack_system *self, size_t bytes) {
|
|
return malloc(bytes);
|
|
}
|
|
|
|
static void m_free(void *buffer) {
|
|
free(buffer);
|
|
}
|
|
|
|
static void m_copy(void *src, void *dest, size_t bytes) {
|
|
memcpy(dest, src, bytes);
|
|
}
|
|
|
|
/* A message printer that prints to stderr */
|
|
static void m_msg(struct m_file *file, const char *format, ...) {
|
|
va_list ap;
|
|
if (file && file->file && file->file->filename) {
|
|
fprintf(stderr, "%s: ", file->file->filename);
|
|
}
|
|
va_start(ap, format);
|
|
vfprintf(stderr, format, ap);
|
|
va_end(ap);
|
|
fputc((int) '\n', stderr);
|
|
fflush(stderr);
|
|
}
|
|
|
|
|
|
static struct m_file *m_open_mem(struct mspack_system *self,
|
|
struct m_filename *fn, int mode)
|
|
{
|
|
struct m_file *fh;
|
|
|
|
/* validate arguments of the filename */
|
|
if (!fn->x.memory.data) return NULL;
|
|
if (!fn->x.memory.length) return NULL;
|
|
|
|
if ((fh = (struct m_file *) m_alloc(self, sizeof(struct m_file)))) {
|
|
fh->x.position = (mode == MSPACK_SYS_OPEN_APPEND) ?
|
|
fn->x.memory.length : 0;
|
|
fh->file = fn;
|
|
}
|
|
return fh;
|
|
}
|
|
|
|
static struct m_file *m_open_file(struct mspack_system *self,
|
|
struct m_filename *fn, int mode)
|
|
{
|
|
struct m_file *fh;
|
|
const char *fmode;
|
|
int fd;
|
|
|
|
switch (mode) {
|
|
case MSPACK_SYS_OPEN_READ: fmode = "rb"; break;
|
|
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: return NULL;
|
|
}
|
|
|
|
/* validate the arguments in the provided filename */
|
|
switch (fn->type) {
|
|
case MTYPE_DISKFILE: if (!fn->x.diskfile) return NULL; break;
|
|
case MTYPE_STDIOFH: if (!fn->x.stdiofh) return NULL; break;
|
|
case MTYPE_FILEDESC: if (fn->x.filedesc < 0) return NULL; break;
|
|
}
|
|
|
|
/* allocate memory for the file handle */
|
|
if (!(fh = (struct m_file *) m_alloc(self, sizeof(struct m_file)))) return NULL;
|
|
|
|
/* open or duplicate the filehandle */
|
|
switch (fn->type) {
|
|
case MTYPE_DISKFILE:
|
|
fh->x.fh = fopen(fn->x.diskfile, fmode);
|
|
break;
|
|
|
|
case MTYPE_STDIOFH:
|
|
fd = fileno(fn->x.stdiofh);
|
|
fh->x.fh = (fd >= 0) ? fdopen(fd, fmode) : NULL;
|
|
break;
|
|
|
|
case MTYPE_FILEDESC:
|
|
fh->x.fh = fdopen(fn->x.filedesc, fmode);
|
|
break;
|
|
}
|
|
|
|
/* validate the new stdio filehandle */
|
|
if (fh->x.fh) {
|
|
fh->file = fn;
|
|
}
|
|
else {
|
|
free(fh);
|
|
fh = NULL;
|
|
}
|
|
|
|
return fh;
|
|
}
|
|
|
|
static struct m_file *m_open(struct mspack_system *self,
|
|
struct m_filename *fn, int mode)
|
|
{
|
|
if (!self || !fn) return NULL;
|
|
|
|
switch (fn->type) {
|
|
case MTYPE_DISKFILE:
|
|
case MTYPE_STDIOFH:
|
|
case MTYPE_FILEDESC:
|
|
return m_open_file(self, fn, mode);
|
|
|
|
case MTYPE_MEMORY:
|
|
return m_open_mem(self, fn, mode);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void m_close(struct m_file *fh) {
|
|
if (!fh || !fh->file) return;
|
|
if (fh->file->type != MTYPE_MEMORY) fclose(fh->x.fh);
|
|
m_free(fh);
|
|
}
|
|
|
|
|
|
static int m_read(struct m_file *fh, void *buffer, int bytes) {
|
|
if (!fh || !fh->file || !buffer || bytes < 0) return -1;
|
|
|
|
if (fh->file->type == MTYPE_MEMORY) {
|
|
int count = fh->file->x.memory.length - fh->x.position;
|
|
if (count > bytes) count = bytes;
|
|
if (count > 0) {
|
|
m_copy(&fh->file->x.memory.data[fh->x.position], buffer, (size_t) count);
|
|
}
|
|
fh->x.position += count;
|
|
return count;
|
|
}
|
|
else {
|
|
size_t count = fread(buffer, 1, (size_t) bytes, fh->x.fh);
|
|
if (!ferror(fh->x.fh)) return (int) count;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int m_write(struct m_file *fh, void *buffer, int bytes) {
|
|
if (!fh || !fh->file || !buffer || bytes < 0) return -1;
|
|
|
|
if (fh->file->type == MTYPE_MEMORY) {
|
|
int count = fh->file->x.memory.length - fh->x.position;
|
|
if (count > bytes) count = bytes;
|
|
if (count > 0) {
|
|
m_copy(buffer, &fh->file->x.memory.data[fh->x.position], (size_t) count);
|
|
}
|
|
fh->x.position += count;
|
|
return count;
|
|
}
|
|
else {
|
|
size_t count = fwrite(buffer, 1, (size_t) bytes, fh->x.fh);
|
|
if (!ferror(fh->x.fh)) return (int) count;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int m_seek(struct m_file *fh, off_t offset, int mode) {
|
|
if (!fh || !fh->file) return 1;
|
|
|
|
if (fh->file->type == MTYPE_MEMORY) {
|
|
switch (mode) {
|
|
case MSPACK_SYS_SEEK_START:
|
|
break;
|
|
case MSPACK_SYS_SEEK_CUR:
|
|
offset += (off_t) fh->x.position;
|
|
break;
|
|
case MSPACK_SYS_SEEK_END:
|
|
offset += (off_t) fh->file->x.memory.length;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
if (offset < 0) return 1;
|
|
if (offset > (off_t) fh->file->x.memory.length) return 1;
|
|
fh->x.position = (size_t) offset;
|
|
return 0;
|
|
}
|
|
|
|
/* file IO based method */
|
|
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: return 1;
|
|
}
|
|
#if HAVE_FSEEKO
|
|
return fseeko(fh->x.fh, offset, mode);
|
|
#else
|
|
return fseek(fh->x.fh, offset, mode);
|
|
#endif
|
|
}
|
|
|
|
|
|
static off_t m_tell(struct m_file *fh) {
|
|
if (!fh || !fh->file) return -1;
|
|
if (fh->file->type == MTYPE_MEMORY) {
|
|
return (off_t) fh->x.position;
|
|
}
|
|
#if HAVE_FSEEKO
|
|
return (off_t) ftello(fh->x.fh);
|
|
#else
|
|
return (off_t) ftell(fh->x.fh);
|
|
#endif
|
|
}
|
|
|
|
|
|
static struct mspack_system multi_system = {
|
|
(struct mspack_file * (*)(struct mspack_system *, const char *, int)) &m_open,
|
|
(void (*)(struct mspack_file *)) &m_close,
|
|
(int (*)(struct mspack_file *, void *, int)) &m_read,
|
|
(int (*)(struct mspack_file *, void *, int)) &m_write,
|
|
(int (*)(struct mspack_file *, off_t, int)) &m_seek,
|
|
(off_t (*)(struct mspack_file *)) &m_tell,
|
|
(void (*)(struct mspack_file *, const char *, ...)) &m_msg,
|
|
&m_alloc,
|
|
&m_free,
|
|
&m_copy,
|
|
NULL
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* constructors and destructor */
|
|
|
|
const char *create_filename(const char *filename) {
|
|
struct m_filename *fn;
|
|
|
|
if (!filename) return NULL; /* filename must not be null */
|
|
|
|
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
|
|
fn->type = MTYPE_DISKFILE;
|
|
fn->filename = filename; /* pretty-printable filename */
|
|
fn->x.diskfile = filename;
|
|
}
|
|
return (const char *) fn;
|
|
}
|
|
|
|
const char *create_filename_from_handle(FILE *fh) {
|
|
struct m_filename *fn;
|
|
|
|
if (!fh) return NULL; /* file handle must not be null */
|
|
|
|
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
|
|
fn->type = MTYPE_STDIOFH;
|
|
fn->filename = NULL; /* pretty-printable filename */
|
|
fn->x.stdiofh = fh;
|
|
}
|
|
return (const char *) fn;
|
|
}
|
|
|
|
const char *create_filename_from_descriptor(int fd) {
|
|
struct m_filename *fn;
|
|
|
|
if (fd < 0) return NULL; /* file descriptor must be valid */
|
|
|
|
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
|
|
fn->type = MTYPE_FILEDESC;
|
|
fn->filename = NULL; /* pretty-printable filename */
|
|
fn->x.filedesc = fd;
|
|
}
|
|
return (const char *) fn;
|
|
}
|
|
|
|
const char *create_filename_from_memory(void *data, size_t length) {
|
|
struct m_filename *fn;
|
|
|
|
if (!data) return NULL; /* data pointer must not be NULL */
|
|
if (length == 0) return NULL; /* length must not be zero */
|
|
|
|
if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
|
|
fn->type = MTYPE_MEMORY;
|
|
fn->filename = NULL; /* pretty-printable filename */
|
|
fn->x.memory.data = (unsigned char *) data;
|
|
fn->x.memory.length = length;
|
|
}
|
|
return (const char *) fn;
|
|
}
|
|
|
|
void set_filename_printable_name(const char *filename, const char *name) {
|
|
struct m_filename *fn = (struct m_filename *) filename;
|
|
if (!fn) return;
|
|
/* very basic validation of structure */
|
|
if ((fn->type < MTYPE_DISKFILE) || (fn->type > MTYPE_MEMORY)) return;
|
|
fn->filename = name;
|
|
}
|
|
|
|
void free_filename(const char *filename) {
|
|
free((void *) filename);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* example of usage with mscab_decompressor */
|
|
|
|
/* a simple cabinet */
|
|
static unsigned char memory_cab[] = {
|
|
0x4D,0x53,0x43,0x46,0x00,0x00,0x00,0x00,0xFD,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x01,0x01,0x00,0x02,0x00,
|
|
0x00,0x00,0x22,0x06,0x00,0x00,0x5E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4D,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0x22,0xBA,0x59,0x20,0x00,
|
|
0x68,0x65,0x6C,0x6C,0x6F,0x2E,0x63,0x00,0x4A,0x00,0x00,0x00,0x4D,0x00,0x00,
|
|
0x00,0x00,0x00,0x6C,0x22,0xE7,0x59,0x20,0x00,0x77,0x65,0x6C,0x63,0x6F,0x6D,
|
|
0x65,0x2E,0x63,0x00,0xBD,0x5A,0xA6,0x30,0x97,0x00,0x97,0x00,0x23,0x69,0x6E,
|
|
0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x74,0x64,0x69,0x6F,0x2E,0x68,0x3E,
|
|
0x0D,0x0A,0x0D,0x0A,0x76,0x6F,0x69,0x64,0x20,0x6D,0x61,0x69,0x6E,0x28,0x76,
|
|
0x6F,0x69,0x64,0x29,0x0D,0x0A,0x7B,0x0D,0x0A,0x20,0x20,0x20,0x20,0x70,0x72,
|
|
0x69,0x6E,0x74,0x66,0x28,0x22,0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x20,0x77,0x6F,
|
|
0x72,0x6C,0x64,0x21,0x5C,0x6E,0x22,0x29,0x3B,0x0D,0x0A,0x7D,0x0D,0x0A,0x23,
|
|
0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x74,0x64,0x69,0x6F,0x2E,
|
|
0x68,0x3E,0x0D,0x0A,0x0D,0x0A,0x76,0x6F,0x69,0x64,0x20,0x6D,0x61,0x69,0x6E,
|
|
0x28,0x76,0x6F,0x69,0x64,0x29,0x0D,0x0A,0x7B,0x0D,0x0A,0x20,0x20,0x20,0x20,
|
|
0x70,0x72,0x69,0x6E,0x74,0x66,0x28,0x22,0x57,0x65,0x6C,0x63,0x6F,0x6D,0x65,
|
|
0x21,0x5C,0x6E,0x22,0x29,0x3B,0x0D,0x0A,0x7D,0x0D,0x0A,0x0D,0x0A
|
|
};
|
|
|
|
int main() {
|
|
const char *mem_cab, *std_out, *std_err, *example;
|
|
struct mscab_decompressor *cabd;
|
|
struct mscabd_cabinet *cab;
|
|
struct mscabd_file *file;
|
|
int err;
|
|
|
|
mem_cab = create_filename_from_memory(&memory_cab[0], sizeof(memory_cab));
|
|
if (!mem_cab) exit(1);
|
|
|
|
std_out = create_filename_from_descriptor(1);
|
|
if (!std_out) exit(1);
|
|
|
|
std_err = create_filename_from_handle(stderr);
|
|
if (!std_err) exit(1);
|
|
|
|
example = create_filename("example.txt");
|
|
if (!example) exit(1);
|
|
|
|
set_filename_printable_name(mem_cab, "internal");
|
|
set_filename_printable_name(std_out, "stdout");
|
|
set_filename_printable_name(std_err, "stderr");
|
|
|
|
/* if self-test reveals an error */
|
|
MSPACK_SYS_SELFTEST(err);
|
|
if (err) exit(1);
|
|
|
|
/* create a cab decompressor using our custom mspack_system interface */
|
|
if ((cabd = mspack_create_cab_decompressor(&multi_system))) {
|
|
|
|
/* open a cab file direct from memory */
|
|
if ((cab = cabd->open(cabd, mem_cab))) {
|
|
|
|
/* first file in the cabinet: print it to stdout */
|
|
file = cab->files;
|
|
if (cabd->extract(cabd, file, std_out)) {
|
|
exit(1);
|
|
}
|
|
|
|
/* second file in the cabinet: print it to stderr */
|
|
file = file->next;
|
|
if (cabd->extract(cabd, file, std_err)) {
|
|
exit(1);
|
|
}
|
|
/* also write it to "example.txt" */
|
|
if (cabd->extract(cabd, file, example)) {
|
|
exit(1);
|
|
}
|
|
cabd->close(cabd, cab);
|
|
}
|
|
else {
|
|
fprintf(stderr, "can't open cabinet (%d)\n", cabd->last_error(cabd));
|
|
}
|
|
mspack_destroy_cab_decompressor(cabd);
|
|
}
|
|
else {
|
|
fprintf(stderr, "can't make decompressor\n");
|
|
}
|
|
|
|
free_filename(example);
|
|
free_filename(std_err);
|
|
free_filename(std_out);
|
|
free_filename(mem_cab);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|