summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2004-09-18 01:00:26 +0000
committerStef Walter <stef@memberwebs.com>2004-09-18 01:00:26 +0000
commit9992298ca9a1549ab3267506896852f9df0369ee (patch)
tree30c6d464f9c56054f769a8cae2cf1f7e938558ea /src
parente5a50721d21a36b8f9f31fd4ef21e182781b22e2 (diff)
Virus action support.
Diffstat (limited to 'src')
-rw-r--r--src/clamsmtpd.c192
1 files changed, 138 insertions, 54 deletions
diff --git a/src/clamsmtpd.c b/src/clamsmtpd.c
index 1d10adf..591db3a 100644
--- a/src/clamsmtpd.c
+++ b/src/clamsmtpd.c
@@ -66,6 +66,7 @@ typedef struct clstate
const char* clamname;
const char* header; /* The header to add to email */
const char* directory; /* The directory for temp files */
+ const char* virusaction; /* Program to run when event occurs */
int bounce; /* Send back a reject line */
int quarantine; /* Leave virus files in temp dir */
int debug_files; /* Leave all files in temp dir */
@@ -123,6 +124,7 @@ clctx_t;
#define CFG_BOUNCE "Bounce"
#define CFG_QUARANTINE "Quarantine"
#define CFG_DEBUGFILES "DebugFiles"
+#define CFG_VIRUSACTION "VirusAction"
/* -----------------------------------------------------------------------
* GLOBALS
@@ -137,8 +139,8 @@ clstate_t g_clstate;
static void usage();
static int connect_clam(clctx_t* ctx);
static int disconnect_clam(clctx_t* ctx);
-static int quarantine_virus(clctx_t* ctx);
-static int clam_scan_file(clctx_t* ctx);
+static int virus_action(clctx_t* ctx, const char* virus);
+static int clam_scan_file(clctx_t* ctx, const char** virus);
/* -----------------------------------------------------------------------
* SIMPLE MACROS
@@ -322,6 +324,7 @@ static void usage()
int cb_check_data(spctx_t* sp)
{
int r = 0;
+ const char* virus;
clctx_t* ctx = (clctx_t*)sp;
/* Connect to clamav */
@@ -331,7 +334,7 @@ int cb_check_data(spctx_t* sp)
if(r != -1 && (r = sp_cache_data(sp)) > 0)
/* ClamAV doesn't like empty files */
- r = clam_scan_file(ctx);
+ r = clam_scan_file(ctx, &virus);
switch(r)
{
@@ -362,7 +365,7 @@ int cb_check_data(spctx_t* sp)
*/
case 1:
/* Any special post operation actions on the virus */
- quarantine_virus(ctx);
+ virus_action(ctx, virus);
if(sp_fail_data(sp, g_clstate.bounce ?
SMTP_DATAVIRUS : SMTP_DATAVIRUSOK) == -1)
@@ -424,6 +427,12 @@ int cb_parse_option(const char* name, const char* value)
return 1;
}
+ else if(strcasecmp(CFG_VIRUSACTION, name) == 0)
+ {
+ g_clstate.virusaction = value;
+ return 1;
+ }
+
return 0;
}
@@ -508,19 +517,25 @@ static int disconnect_clam(clctx_t* ctx)
return 0;
}
-static int clam_scan_file(clctx_t* ctx)
+static int clam_scan_file(clctx_t* ctx, const char** virus)
{
- int len;
+ int len, x;
+ char* line;
spctx_t* sp = &(ctx->sp);
+ ASSERT(ctx && virus);
+
+ *virus = NULL;
+
/* Needs to be long enough to hold path names */
ASSERT(SP_LINE_LENGTH > MAXPATHLEN + 32);
- strcpy(sp->line, CLAM_SCAN);
- strcat(sp->line, sp->cachename);
- strcat(sp->line, "\n");
+ line = ctx->clam.line;
+ strcpy(line, CLAM_SCAN);
+ strcat(line, sp->cachename);
+ strcat(line, "\n");
- if(spio_write_data(sp, &(ctx->clam), sp->line) == -1)
+ if(spio_write_data(sp, &(ctx->clam), line) == -1)
return -1;
len = spio_read_line(sp, &(ctx->clam), SPIO_DISCARD | SPIO_TRIM);
@@ -530,35 +545,58 @@ static int clam_scan_file(clctx_t* ctx)
return -1;
}
- if(is_last_word(sp->line, CLAM_OK, KL(CLAM_OK)))
+ if(is_last_word(line, CLAM_OK, KL(CLAM_OK)))
{
sp_add_log(sp, "status=", "CLEAN");
sp_messagex(sp, LOG_DEBUG, "no virus");
return 0;
}
- if(is_last_word(sp->line, CLAM_FOUND, KL(CLAM_FOUND)))
+ /*
+ * When a virus is found the returned line from
+ * clamd looks something like this:
+ *
+ * /path/to/virus: Virus.XXXX FOUND
+ */
+ if(is_last_word(line, CLAM_FOUND, KL(CLAM_FOUND)))
{
- len = strlen(sp->cachename);
+ x = strlen(sp->cachename);
+
+ /* A little sanity check ... */
+ if(len > x + KL(CLAM_FOUND))
+ {
+ /* Remove the "FOUND" from the end */
+ line[len - KL(CLAM_FOUND)] = 0;
+
+ /* Skip the filename returned, and colon */
+ line += x + 1;
+
+ line = trim_space(line);
+
+ sp_messagex(sp, LOG_DEBUG, "found virus: %s", line);
+ sp_add_log(sp, "status=VIRUS:", line);
+ *virus = line;
+ }
- if(sp->linelen > len)
- sp_add_log(sp, "status=VIRUS:", sp->line + len + 1);
else
+ {
+ sp_messagex(sp, LOG_WARNING, "couldn't parse virus name from clamd response: %s", line);
sp_add_log(sp, "status=", "VIRUS");
+ *virus = "Unparsable.Virus.Name";
+ }
- sp_messagex(sp, LOG_DEBUG, "found virus");
return 1;
}
- if(is_last_word(sp->line, CLAM_ERROR, KL(CLAM_ERROR)))
+ if(is_last_word(line, CLAM_ERROR, KL(CLAM_ERROR)))
{
- sp_messagex(sp, LOG_ERR, "clamav error: %s", sp->line);
+ sp_messagex(sp, LOG_ERR, "clamav error: %s", line);
sp_add_log(sp, "status=", "CLAMAV-ERROR");
return -1;
}
sp_add_log(sp, "status=", "CLAMAV-ERROR");
- sp_messagex(sp, LOG_ERR, "unexepected response from clamd: %s", sp->line);
+ sp_messagex(sp, LOG_ERR, "unexepected response from clamd: %s", line);
return -1;
}
@@ -566,56 +604,102 @@ static int clam_scan_file(clctx_t* ctx)
* TEMP FILE HANDLING
*/
-static int quarantine_virus(clctx_t* ctx)
+static int virus_action(clctx_t* ctx, const char* virus)
{
- char buf[MAXPATHLEN];
+ char qfilename[MAXPATHLEN];
spctx_t* sp = &(ctx->sp);
char* t;
+ int i;
+ pid_t pid;
- if(!g_clstate.quarantine)
- return 0;
+ if(g_clstate.quarantine)
+ {
+ strlcpy(qfilename, g_clstate.directory, MAXPATHLEN);
+ strlcat(qfilename, "/virus.", MAXPATHLEN);
+
+ /* Points to null terminator */
+ t = qfilename + strlen(qfilename);
+
+ /*
+ * Yes, I know we're using mktemp. And yet we're doing it in
+ * a safe manner due to the link command below not overwriting
+ * existing files.
+ */
+ for(;;)
+ {
+ /* Null terminate off the ending, and replace with X's for mktemp */
+ *t = 0;
+ strlcat(qfilename, "XXXXXX", MAXPATHLEN);
+
+ if(!mktemp(qfilename))
+ {
+ sp_message(sp, LOG_ERR, "couldn't create quarantine file name");
+ return -1;
+ }
- strlcpy(buf, g_clstate.directory, MAXPATHLEN);
- strlcat(buf, "/virus.", MAXPATHLEN);
+ /* Try to link the file over to the temp */
+ if(link(sp->cachename, qfilename) == -1)
+ {
+ /* We don't want to allow race conditions */
+ if(errno == EEXIST)
+ {
+ sp_message(sp, LOG_WARNING, "race condition when quarantining virus file: %s", qfilename);
+ continue;
+ }
+
+ sp_message(sp, LOG_ERR, "couldn't quarantine virus file");
+ return -1;
+ }
- /* Points to null terminator */
- t = buf + strlen(buf);
+ break;
+ }
- /*
- * Yes, I know we're using mktemp. And yet we're doing it in
- * a safe manner due to the link command below not overwriting
- * existing files.
- */
- for(;;)
- {
- /* Null terminate off the ending, and replace with X's for mktemp */
- *t = 0;
- strlcat(buf, "XXXXXX", MAXPATHLEN);
+ sp_messagex(sp, LOG_INFO, "quarantined virus file as: %s", qfilename);
+ }
- if(!mktemp(buf))
+ if(g_clstate.virusaction != NULL)
+ {
+ switch(pid = fork())
{
- sp_message(sp, LOG_ERR, "couldn't create quarantine file name");
+ case -1:
+ sp_message(sp, LOG_ERR, "couldn't fork for virus action");
return -1;
- }
- /* Try to link the file over to the temp */
- if(link(sp->cachename, buf) == -1)
- {
- /* We don't want to allow race conditions */
- if(errno == EEXIST)
- {
- sp_message(sp, LOG_WARNING, "race condition when quarantining virus file: %s", buf);
- continue;
- }
+ /* The child */
+ case 0:
+ /*
+ * New process group because we don't care about this child,
+ * it's return value, waiting for it to exit or any of that.
+ * Apparently this can't fail when done like this.
+ */
+ setsid();
- sp_message(sp, LOG_ERR, "couldn't quarantine virus file");
- return -1;
- }
+ /* Close all descriptors */
+ for(i = 0; i <= 2; i++)
+ close(i);
- break;
+ /* Set the environment variables */
+ sp_setup_env(sp, 0);
+
+ /* When quarantining we can hand the file name off */
+ if(g_clstate.quarantine)
+ setenv("EMAIL", qfilename, 1);
+
+ if(virus)
+ setenv("VIRUS", virus, 1);
+
+ sp_messagex(sp, LOG_DEBUG, "executing virus action: %s", g_clstate.virusaction);
+
+ /* And execute the program */
+ execl("/bin/sh", "sh", "-c", g_clstate.virusaction, NULL);
+
+ /* If that returned then there was an error */
+ sp_message(sp, LOG_ERR, "error executing the shell for virus action");
+ exit(1);
+ break;
+ };
}
- sp_messagex(sp, LOG_INFO, "quarantined virus file as: %s", buf);
return 0;
}