ÜberWall

UWfirmforce

/* $Id: plugin.c,v 1.1 2009/02/21 21:25:17 khorben Exp $ */
/* Copyright (c) 2007 khorben of Uberwall */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#define DEBUG
#include <sys/stat.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include "plugins/plugin.h"
#include "UWfirmforce.h"
/* macros */
#define max(a, b) ((a) > (b) ? (a) : (b))
/* Plugin */
/* types */
typedef enum _PluginFlag
{
PF_VERBOSE = 0x01,
PF_EXTRACT = 0x02
} PluginFlag;
struct _PluginHelperData
{
PluginFlag flags;
char const * filename;
int pass;
};
struct _PluginHandle
{
Plugin * plugin;
void * handle;
};
PluginHandle * plugin_new(char const * filename)
{
PluginHandle * plugin;
if((plugin = malloc(sizeof(*plugin))) == NULL)
{
UWfirmforce_error(filename, 0);
return NULL;
}
if((plugin->handle = dlopen(filename, RTLD_LAZY)) == NULL)
{
UWfirmforce_dlerror(filename, 0);
plugin_delete(plugin);
return NULL;
}
if((plugin->plugin = dlsym(plugin->handle, "plugin")) == NULL
|| plugin->plugin->name == NULL)
{
UWfirmforce_dlerror(filename, 0);
plugin_delete(plugin);
return NULL;
}
return plugin;
}
/* plugin_delete */
void plugin_delete(PluginHandle * plugin)
{
if(plugin->handle != NULL && dlclose(plugin->handle) != 0)
UWfirmforce_dlerror("plugin_delete", 0);
free(plugin);
}
/* accessors */
/* plugin_get_name */
char const * plugin_get_name(PluginHandle * plugin)
{
return plugin->plugin->name;
}
/* plugin_get_buffer_size */
size_t plugin_get_buffer_size(PluginHandle * plugin)
{
unsigned int i;
size_t len = 0;
if(plugin->plugin->magic == NULL)
return 0;
for(i = 0; plugin->plugin->magic[i].magic != NULL; i++)
{
len = max(len, plugin->plugin->magic[i].overhead);
len = max(len, plugin->plugin->magic[i].offset
+ plugin->plugin->magic[i].magic_len);
}
return len;
}
/* useful */
/* plugin_do */
static int _do_printf(PluginHelper * ph, char const * format, ...);
static FILE * _do_fopen(struct _PluginHelper * ph, const char * pathname);
static size_t _do_fwrite(const void * ptr, size_t size, size_t nmemb,
FILE * fp);
static int _do_fclose(FILE * fp);
int plugin_do(PluginHandle * plugin, int signature, int verbose, int extract,
char const * filename, FILE * fp)
{
static PluginHelperData data = { 0, NULL, -1 };
static PluginHelper ph =
{
&data,
_do_printf,
_do_fopen, _do_fwrite, _do_fclose
};
if(plugin->plugin->callback == NULL)
return 0;
if(data.filename != filename)
data.pass = -1;
data.flags = verbose ? PF_VERBOSE : 0;
data.flags |= extract ? PF_EXTRACT : 0;
data.filename = filename;
return plugin->plugin->callback(&ph, signature, fp);
}
static int _do_printf(PluginHelper * ph, char const * format, ...)
{
int ret;
va_list arg;
if(!(ph->data->flags & PF_VERBOSE))
return 0;
va_start(arg, format);
ret = vprintf(format, arg);
va_end(arg);
/* fflush(stdout); FIXME find a more optimal solution */
return ret;
}
static char * _fopen_directory(PluginHelperData * data);
static void _fopen_sanitize(char * filename);
static FILE * _do_fopen(struct _PluginHelper * ph, const char * pathname)
{
char * str;
size_t len;
char * p;
char * q;
FILE * fp;
if(!(ph->data->flags & PF_EXTRACT))
return NULL;
/* check for available directories */
if((str = _fopen_directory(ph->data)) == NULL)
return NULL;
len = strlen(str);
/* sanitize pathname */
if((p = strdup(pathname)) == NULL)
{
UWfirmforce_error(pathname, 0);
free(str);
return NULL;
}
_fopen_sanitize(p);
if((q = realloc(str, len + strlen(p) + 2)) == NULL)
{
UWfirmforce_error(ph->data->filename, 0);
free(p);
free(str);
return NULL;
}
str = q;
sprintf(&str[len], "/%s", p);
if((fp = fopen(str, "w")) == NULL) /* FIXME may overwrite stuff */
UWfirmforce_error(str, 0);
free(p);
free(str);
return fp;
}
static char * _fopen_directory(PluginHelperData * data)
{
size_t len = strlen(data->filename);
char * str;
int i;
struct stat st;
if((str = malloc(len + 5)) == NULL)
{
UWfirmforce_error(data->filename, 0);
return NULL;
}
strncpy(str, data->filename, len);
if(data->pass >= 0 && data->pass < 1000)
{
snprintf(&str[len], 5, ".%03d", data->pass);
return str;
}
for(i = 0; i < 1000; i++) /* more will overflow the buffer */
{
snprintf(&str[len], 5, ".%03d", i);
if(stat(str, &st) == 0 || errno != ENOENT)
continue;
if(mkdir(str, 0777) != 0) /* create the output directory */
{
UWfirmforce_error(str, 0);
break;
}
/* FIXME then create "%03d-%s", cnt, pluginname) */
data->pass = i;
return str;
}
free(str);
return NULL;
}
static void _fopen_sanitize(char * filename)
/* FIXME audit this */
{
size_t i;
size_t parent = 0;
size_t len = strlen(filename);
#ifdef DEBUG
fprintf(stderr, "DEBUG: sanitized \"%s\"", filename);
#endif
for(i = 0; filename[i] != '\0'; i++)
{
if(filename[i] != '\0' && filename[i] != '/')
continue;
if(strncmp(&filename[i + 1], "..", 2) != 0)
continue;
if(filename[i + 3] != '\0' && filename[i + 3] != '/')
continue;
memmove(&filename[parent], &filename[i + 3], len - i);
parent = i;
len -= (len - i);
}
#ifdef DEBUG
fprintf(stderr, " to \"%s\"\n", filename);
#endif
}
static size_t _do_fwrite(const void * ptr, size_t size, size_t nmemb,
FILE * fp)
{
if(fp == NULL)
return nmemb;
if(nmemb == 0)
return nmemb;
/* FIXME implement */
return fwrite(ptr, size, nmemb, fp);
}
static int _do_fclose(FILE * fp)
{
if(fp == NULL)
return 0;
return fclose(fp);
}
/* plugin_info */
void plugin_info(PluginHandle * plugin)
{
struct
{
PluginType type;
char const * description;
} td[] =
{
{ PT_COMPRESSION, "compressed" },
{ PT_EXECUTABLE, "executable" },
{ PT_ARCHIVE, "archive" },
{ PT_FORENSIC, "forensic" },
{ 0, NULL }
};
unsigned int i;
printf("%s:", plugin->plugin->name);
for(i = 0; td[i].description != NULL; i++)
if((td[i].type & plugin->plugin->type) == td[i].type)
printf(" %s", td[i].description);
fputc('\n', stdout);
}
/* plugin_match
* RETURNS: >= 0 matched signature id returned
* else nothing was found or an error occured */
int plugin_match(PluginHandle * plugin, char const * buf, size_t buf_cnt)
{
unsigned int i;
PluginMagic * magic;
for(i = 0; (magic = &plugin->plugin->magic[i]) != NULL
&& magic->magic != NULL; i++)
{
if(buf_cnt < magic->offset + magic->magic_len
|| memcmp(&buf[magic->offset], magic->magic,
magic->magic_len) != 0)
continue;
return i;
}
return -1;
}