3
RenAttach 1.1.1 - GNU email virus/trojan attachment filter
4
Copyright(C) 2000-2001 Jem E. Berkes
5
Release date: October 18, 2001
7
Author's e-mail: jberkes at pc-tools dot net
9
http://www.pc-tools.net/
11
Write to -stdout and modified file rename technique contributed by:
12
Colin McKinnon <colinmckinnon at technologist dot com>
14
Before compiling and executing this software you must read and agree to
17
This program is free software; you can redistribute it and/or modify
18
it under the terms of the GNU General Public License as published by
19
the Free Software Foundation; either version 2 of the License, or
20
(at your option) any later version.
22
This program is distributed in the hope that it will be useful,
23
but WITHOUT ANY WARRANTY; without even the implied warranty of
24
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
GNU General Public License for more details.
27
You should have received a copy of the GNU General Public License
28
along with this program (see LICENSE); if not, write to the Free
29
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
44
const char VERSION[]="RenAttach 1.1.1 (www.pc-tools.net)";
45
int cursize = DEF_BUFSIZE; /* sets buffer sizes; changes on the fly */
46
char *scratch, *good_list, *bad_list, *gl_scratch, *bl_scratch;
48
int mode, use_stdout=0;
49
/* these buffers have fixed size */
50
char messageid[DEF_BUFSIZE];
51
char generic[DEF_BUFSIZE];
52
char mime_buf[MAX_MIMEHEAD];
63
void log_it(char* filename)
64
/* Log a renamed filename and message ID to syslog */
67
openlog("renattach", LOG_PID, LOG_MAIL);
68
syslog(LOG_WARNING, "renamed \"%s\" in %s", filename, messageid);
73
char *startof_fn(char *line)
75
Looks inside line, and returns a pointer to the start of a found
76
filename, or NULL if no filename is located.
79
char *filename1, *filename2, *line_copy, *mime_fn, *loc;
82
filename1 = (char*)malloc(cursize);
83
filename2 = (char*)malloc(cursize);
84
line_copy = (char*)malloc(cursize);
85
if (filename1 == NULL)
87
if (filename2 == NULL)
92
if (line_copy == NULL)
98
line_length = strlen(line)+1;
99
for (i=0; i<line_length; i++)
100
line_copy[i] = tolower(line[i]);
101
if ((mime_fn=strstr(line_copy, "name")) == NULL)
103
if ( (mime_fn=strstr(line_copy, "content-description:")) == NULL)
112
mime_fn = mime_fn-line_copy+line+20;
115
if (sscanf(mime_fn, "%s", filename1) == 1)
117
loc = strstr(mime_fn, filename1);
129
mime_fn = mime_fn-line_copy+line;
131
switch (sscanf(mime_fn, "%s%s", filename1, filename2))
135
if (strcmp(filename1, "=") == 0)
137
else if (*filename1 == '=')
139
char *loc = strstr(mime_fn, filename1+1);
148
if (strcmp(filename1, "=") == 0)
150
char *loc = strstr(mime_fn, filename2);
155
else if (*filename1 == '=')
157
char *loc = strstr(mime_fn, filename1+1);
171
int is_kosher(char *address)
173
Catches bad "forward to" addresses. Tells whether address is good.
174
NB will reject prog mailers "|" and "`...`" [CM]
175
Return: 0 means address is bad, 1 means good
181
for (i=0; i < (int)strlen(address); i++)
184
if (ch == '.' || ch == '_' || ch == '-' || ch == '@')
186
else if (ch >= 'a' && ch <= 'z')
188
else if (ch >= 'A' && ch <= 'Z')
190
else if (ch >= '0' && ch <= '9')
198
int syntax(char *exec)
199
/* Generic syntax error */
201
fprintf(stderr, "\n%s\nCopyright(C) 2000-2001 Jem E. Berkes\n\n", VERSION);
202
fprintf(stderr, "\tRenAttach comes with ABSOLUTELY NO WARRANTY. This is\n"
203
"\tfree software, and you are welcome to redistribute it\n"
204
"\tunder certain conditions. See LICENSE for details.\n\n");
205
fprintf(stderr, "Usage: %s [-v] -b|-g|-f [-s|-t address]\n\n", exec);
206
fprintf(stderr, "-bad or -partial (old) will rename only dangerous extensions\n");
207
fprintf(stderr, "-good will rename everything except known safe extensions\n");
208
fprintf(stderr, "-full will rename all attachments\n");
209
fprintf(stderr, "-to will forward the filtered mail to some address\n");
210
fprintf(stderr, "-stdout writes the filtered output to stdout\n");
211
fprintf(stderr, "-verbose will dump internal settings (including lists)\n\n");
212
fprintf(stderr, "Examples: (insert into .forward, with quotes)\n");
213
fprintf(stderr, "\"|/path/renattach -g\"\n");
214
fprintf(stderr, "\"|/path/renattach -b -t address@domain.tld\"\n\n");
219
int is_match(char *filename, int good)
221
Compares filename's extension to either the good (1) or bad (0) list
222
Return: 0 is not match, 1 is match
225
char *extcopy, *ext, *lastdot, *token;
227
strcpy(gl_scratch, good_list);
228
strcpy(bl_scratch, bad_list);
231
extcopy = gl_scratch;
233
extcopy = bl_scratch;
235
lastdot = strrchr(filename, '.');
239
token = strtok(extcopy, CONF_TOKS);
240
while (token != NULL)
242
if (strcasecmp(token, ext) == 0)
244
token = strtok(NULL, CONF_TOKS);
250
int make_safe(char *orig_line, char *name, int in_mime)
252
Processes lines which contain filenames. Returns 1 if it renames
253
a filename. If in_mime, then output is appended to mime_buf instead
254
of output immediately. Note: orig_line and name must point into the
258
char *fnbuf, *filename, *txtend, *lastdot;
262
/* prepare this generic filename in case it is needed */
263
strncpy(generic, GENERIC_FN, DEF_BUFSIZE);
264
generic[DEF_BUFSIZE-1] = 0;
266
if (*name=='\n' || *name==0)
268
fnbuf = (char*)malloc(cursize);
272
if ((sscanf(name, "%[^\"]", filename)==1) ||
273
(sscanf(name, "\"%[^\"]", filename)==1) ||
274
(sscanf(name, "%[^\n]", filename)==1))
276
if (filename[strlen(filename)-1] == '\n')
277
filename[strlen(filename)-1] = 0;
284
n = (strstr(name, filename)-orig_line);
285
strncpy(scratch, orig_line, n);
287
txtend = strstr(name, filename) + strlen(filename);
288
for (i=strlen(filename)-1; (filename[i]=='.' || filename[i]==' ')
292
if (strstr(filename, "=?")!=NULL)
302
if (is_match(filename, 0)==1)
306
*strrchr(filename, '.') = '_';
307
if (strlen(filename)+strlen(NEW_EXTN)+1 < cursize)
308
strcat(filename, NEW_EXTN);
315
if (is_match(filename, 1)==1)
317
/* deliberate run into following case */
320
/* simply appending a string doesn't fix Eudora light where the
321
extension _may_ be truncated back to its original value */
324
lastdot = strrchr(filename, '.');
327
if (strlen(filename)+strlen(NEW_EXTN)+1 < cursize)
328
strcat(filename, NEW_EXTN);
333
strcat(scratch, filename);
334
strcat(scratch, txtend);
337
if (strlen(mime_buf)+strlen(scratch)+1 < MAX_MIMEHEAD)
338
strcat(mime_buf, scratch);
341
fputs(scratch, mailer);
347
int main (int argc, char *argv[])
349
char *line, *fname, *bigger_line, *bigger_scratch, *token, *after_mt;
350
int inheader=1, mime_type=0, change_mime=0, i;
351
int badlist=0, goodlist=0, full=0, address=0, verbose=0;
355
return syntax(argv[0]);
357
for (i = 1; i < argc; i++)
360
return syntax(argv[0]);
391
return syntax(argv[0]);
395
return syntax(argv[0]);
400
if (access(LOCAL_MAILER, X_OK) == -1)
402
fprintf(stderr, "Can not find/execute local mailer: %s\n", LOCAL_MAILER);
405
if (access(MTA_COMMAND, X_OK) == -1)
407
fprintf(stderr, "Can not find/execute MTA: %s\n", MTA_COMMAND);
412
if (read_lists(&good_list, &bad_list, &gl_scratch, &bl_scratch) != 0)
417
printf("Version string: %s\n", VERSION);
418
printf("Configuration file is %s\n", CONF_FILE);
419
printf("Mail transport agent is %s\n", MTA_COMMAND);
420
printf("Local mailer is %s\n", LOCAL_MAILER);
421
printf("\nReading lists from configuration file...\n");
422
printf("-- begin goodlist --\n");
423
strcpy(gl_scratch, good_list);
424
tokenize_list(gl_scratch, 1);
425
printf("\n-- begin badlist --\n");
426
strcpy(bl_scratch, bad_list);
427
tokenize_list(bl_scratch, 1);
428
printf("\n-- end --\n\n");
432
if ( (badlist==0 && goodlist==0 && full==0) ||
433
(badlist==1 && goodlist==1) || (badlist==1 && full==1) || (goodlist==1 && full==1) )
435
fprintf(stderr, "Specify -badlist, -goodlist or -full filtering (only one)\n");
439
if ((address>0) && use_stdout==1)
441
fprintf(stderr, "Specify -stdout or -to address, but not both\n");
447
else if (goodlist==1)
448
mode = MODE_GOODLIST;
451
scratch = (char*)malloc(cursize);
459
if (is_kosher(argv[address])==0)
461
fprintf(stderr, "\"%s\" is not a valid e-mail address\n", argv[address]);
466
else if (cursize < (int)(3+sizeof(MTA_COMMAND)+sizeof(MTA_TAIL)+strlen(argv[address])))
468
fprintf(stderr, "Address is too long\n");
473
else sprintf(scratch, "%s %s %s", MTA_COMMAND, MTA_TAIL, argv[address]);
477
strncpy(scratch, LOCAL_MAILER, cursize);
478
scratch[cursize-1] = 0;
480
line = (char*)malloc(cursize);
490
mailer = popen(scratch, "w"); /* init MDA */
498
while (fgets(line, cursize, stdin) != NULL)
500
while ( ((int)strlen(line)==cursize-1) && (line[strlen(line)-1]!='\n') )
502
if (cursize >= BUF_LIMIT)
507
bigger_line = (char*)malloc(cursize);
509
if (bigger_line == NULL)
516
strcpy(bigger_line, line);
520
bigger_scratch = (char*)malloc(cursize);
521
if (bigger_scratch == NULL)
527
scratch = bigger_scratch;
528
fgets(line+(cursize/2)-1, (cursize/2)+1, stdin);
532
if (strncasecmp(line, "Message-ID:", 11)==0)
534
strncpy(messageid, line, DEF_BUFSIZE);
535
messageid[DEF_BUFSIZE-1] = 0;
537
else if (*line=='\n')
540
Should have reached end of headers - add additional before body [CM]
543
fprintf(mailer, "X-Filtered-With: %s - ", VERSION);
544
if (mode == MODE_BADLIST)
545
fprintf(mailer, "badlist filter\n\n");
546
else if (mode == MODE_GOODLIST)
547
fprintf(mailer, "goodlist filter\n\n");
549
fprintf(mailer, "full filter\n\n");
555
if (mime_type==0 && (strncasecmp(line, "Content-type:", 13)==0))
563
if ( (strncmp(line, "begin", 5)==0) &&
564
(sscanf(line, "begin %*d %[^\n]", scratch)==1) )
566
if (make_safe(line, strstr(line+5, scratch), 0)==1)
576
if (change_mime == 1)
578
/* Change content-type */
579
token = strtok(mime_buf, "\r\n");
580
while (token != NULL)
582
if (strncasecmp(token, "Content-type:", 13)==0)
584
after_mt = strchr(token, ';');
585
if (after_mt == NULL)
587
fprintf(mailer, "Content-Type: %s%s\n", NEW_MIMETYPE, after_mt);
590
fprintf(mailer, "%s\n", token);
591
token = strtok(NULL, "\r\n");
595
fputs(mime_buf, mailer);
596
fprintf(mailer, "\n");
600
if ((fname=startof_fn(line)) != NULL)
602
if (make_safe(line, fname, 1)==1)
607
if (strlen(mime_buf)+strlen(line)+1 < MAX_MIMEHEAD)
608
strcat(mime_buf, line);
611
continue; /* no output, yet */