-
Notifications
You must be signed in to change notification settings - Fork 1
/
qmail-smtpd-viruscan-1.3.patch
299 lines (286 loc) · 8.68 KB
/
qmail-smtpd-viruscan-1.3.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
This patch changes qmail-smtpd so that it parses incoming emails. It
looks at the first line of MIME attachments to see if they're found in
control/signatures. This catches nearly all current Microsoft
viruses. As of version 1.3, it also cracks open DSN, Exim, and QSBMF
bounce messages, and parses the bouncing email.
Apply this patch like so:
cd /usr/local/src/qmail-1.03
wget http://qmail.org/qmail-smtpd-viruscan-1.3.patch
patch <qmail-smtpd-viruscan-1.3.patch
# create the signature file like this:
cat <<EOF >/var/qmail/control/signatures
# Windows executables seen in active virii
TVqQAAMAA
TVpQAAIAA
# Additional windows executable signatures not yet seen in virii
TVpAALQAc
TVpyAXkAX
TVrmAU4AA
TVrhARwAk
TVoFAQUAA
TVoAAAQAA
TVoIARMAA
TVouARsAA
TVrQAT8AA
# .ZIPfile signature seen in SoBig.E and mydoom:
UEsDBBQAA
UEsDBAoAAA
# .GIF file found in a previous Microsoft virus making the rounds.
R0lGODlhaAA7APcAAP///+rp6puSp6GZrDUjUUc6Zn53mFJMdbGvvVtXh2xre8bF1x8cU4yLprOy
EOF
diff -u orig/case_startb.c ./case_startb.c
--- orig/case_startb.c 2003-09-25 01:01:48.000000000 -0400
+++ ./case_startb.c 2003-03-06 17:53:59.000000000 -0500
@@ -0,0 +1,21 @@
+#include "case.h"
+
+int case_startb(s,len,t)
+register char *s;
+unsigned int len;
+register char *t;
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (!y) return 1;
+ if (!len) return 0;
+ --len;
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ if (x != y) return 0;
+ }
+}
diff -u orig/Makefile ./Makefile
--- orig/Makefile 1998-06-15 06:53:16.000000000 -0400
+++ ./Makefile 2003-03-10 10:49:44.000000000 -0500
@@ -217,9 +217,9 @@
case.a: \
makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \
-case_starts.o
+case_starts.o case_startb.o
./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \
- case_lowers.o case_starts.o
+ case_lowers.o case_starts.o case_startb.o
case_diffb.o: \
compile case_diffb.c case.h
@@ -237,6 +237,10 @@
compile case_lowers.c case.h
./compile case_lowers.c
+case_startb.o: \
+compile case_startb.c case.h
+ ./compile case_startb.c
+
case_starts.o: \
compile case_starts.c case.h
./compile case_starts.c
diff -u orig/qmail-smtpd.c ./qmail-smtpd.c
--- orig/qmail-smtpd.c 1998-06-15 06:53:16.000000000 -0400
+++ ./qmail-smtpd.c 2004-01-27 16:54:24.000000000 -0500
@@ -96,6 +96,8 @@
int bmfok = 0;
stralloc bmf = {0};
struct constmap mapbmf;
+int sigsok = 0;
+stralloc sigs = {0};
void setup()
{
@@ -117,6 +119,9 @@
if (bmfok)
if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+ sigsok = control_readfile(&sigs,"control/signatures",0);
+ if (sigsok == -1) die_control();
+
if (control_readint(&databytes,"control/databytes") == -1) die_control();
x = env_get("DATABYTES");
if (x) { scan_ulong(x,&u); databytes = u; }
@@ -208,6 +213,19 @@
return 0;
}
+int sigscheck(stralloc *line) {
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < sigs.len; i++) if (!sigs.s[i]) {
+ if (i-j < line->len)
+ if (!str_diffn(line->s,sigs.s+j,i-j))
+ return 1;
+ j = i+1;
+ }
+ return 0;
+}
+
int addrallowed()
{
int r;
@@ -281,9 +299,147 @@
struct qmail qqt;
unsigned int bytestooverflow = 0;
+int linespastheader; /* =0 after boundary is found in body, */
+ /* until blank line */
+char linetype;
+int flagexecutable;
+int flagqsbmf;
+
+stralloc line = {0};
+stralloc content = {0};
+stralloc boundary = {0};
+int boundary_start;
+
+/*
+
+def put(ch):
+ line.append(ch)
+ if ch == '\n':
+ if linepastheader == 0:
+ if line.startswith('Content-Type:'):
+ content =
+
+ put() puts characters into the queue. We remember those characters
+ and form them into a line. When we get a newline, we examine the
+ line. If we're currently in a header (0 linespastheader), we look
+ for Content-Type. If we're at the newline that ends a header, we
+ look to see if the content is multipart. If it is, then we push
+ the current boundary, remember the boundary, otherwise we set the
+ boundary to the empty string. Set the linespastheader to 1. When
+ linespastheader is 1, and the boundary is empty, scan the line for
+ signatures. If the boundary is non-empty, look for a match against
+ the boundary. If it matches and is followed by two dashes, pop the
+ boundary, otherwise set linespastheader to 0.
+*/
+
void put(ch)
char *ch;
{
+ char *cp, *cpstart, *cpafter;
+ unsigned int len;
+
+ if (line.len < 1024)
+ if (!stralloc_catb(&line,ch,1)) die_nomem();
+
+ if (*ch == '\n') {
+ if (linespastheader == 0) {
+ if (line.len == 1) {
+ linespastheader = 1;
+ if (flagqsbmf) {
+ flagqsbmf = 0;
+ linespastheader = 0;
+ }
+ if (content.len) { /* MIME header */
+ cp = content.s;
+ len = content.len;
+ while (len && (*cp == ' ' || *cp == '\t')) { ++cp; --len; }
+ cpstart = cp;
+ if (len && *cp == '"') { /* might be commented */
+ ++cp; --len; cpstart = cp;
+ while (len && *cp != '"') { ++cp; --len; }
+ } else {
+ while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
+ ++cp; --len;
+ }
+ }
+ if (!case_diffb(cpstart,cp-cpstart,"message/rfc822"))
+ linespastheader = 0;
+
+ cpafter = content.s+content.len;
+ while((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
+ ++cp;
+ while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp;
+ if (case_startb(cp,cpafter - cp,"boundary=")) {
+ cp += 9; /* after boundary= */
+ if (cp < cpafter && *cp == '"') {
+ ++cp;
+ cpstart = cp;
+ while (cp < cpafter && *cp != '"') ++cp;
+ } else {
+ cpstart = cp;
+ while (cp < cpafter &&
+ *cp != ';' && *cp != ' ' && *cp != '\t') ++cp;
+ }
+ /* push the current boundary. Append a null and remember start. */
+ if (!stralloc_0(&boundary)) die_nomem();
+ boundary_start = boundary.len;
+ if (!stralloc_cats(&boundary,"--")) die_nomem();
+ if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
+ die_nomem();
+ break;
+ }
+ }
+ }
+ } else { /* non-blank header line */
+ if ((*line.s == ' ' || *line.s == '\t')) {
+ switch(linetype) {
+ case 'C': if (!stralloc_catb(&content,line.s,line.len-1)) die_nomem(); break;
+ default: break;
+ }
+ } else {
+ if (case_startb(line.s,line.len,"content-type:")) {
+ if (!stralloc_copyb(&content,line.s+13,line.len-14)) die_nomem();
+ linetype = 'C';
+ } else {
+ linetype = ' ';
+ }
+ }
+ }
+ } else { /* non-header line */
+ if (boundary.len-boundary_start && *line.s == '-' && line.len > (boundary.len-boundary_start) &&
+ !str_diffn(line.s,boundary.s+boundary_start,boundary.len-boundary_start)) { /* matches a boundary */
+ if (line.len > boundary.len-boundary_start + 2 &&
+ line.s[boundary.len-boundary_start+0] == '-' &&
+ line.s[boundary.len-boundary_start+1] == '-') {
+ /* XXXX - pop the boundary here */
+ if (boundary_start) boundary.len = boundary_start - 1;
+ boundary_start = boundary.len;
+ while(boundary_start--) if (!boundary.s[boundary_start]) break;
+ boundary_start++;
+ linespastheader = 2;
+ } else {
+ linespastheader = 0;
+ }
+ } else if (linespastheader == 1) { /* first line -- match a signature? */
+ if (/*mailfrom.s[0] == '\0' && */
+ str_start(line.s,"Hi. This is the "))
+ flagqsbmf = 1;
+ else if (/*mailfrom.s[0] == '\0' && */
+ str_start(line.s,"This message was created automatically by mail delivery software"))
+ flagqsbmf = 1;
+ else if (sigscheck(&line)) {
+ flagexecutable = 1;
+ qmail_fail(&qqt);
+ }
+ linespastheader = 2;
+ }
+ if (flagqsbmf && str_start(line.s,"---")) {
+ linespastheader = 0;
+ }
+ }
+ line.len = 0;
+ }
+
if (bytestooverflow)
if (!--bytestooverflow)
qmail_fail(&qqt);
@@ -374,6 +530,13 @@
if (!rcptto.len) { err_wantrcpt(); return; }
seenmail = 0;
if (databytes) bytestooverflow = databytes + 1;
+ boundary.len = 0;
+ boundary_start = 0;
+ content.len = 0;
+ linespastheader = 0;
+ flagexecutable = 0;
+ flagqsbmf = 0;
+ linetype = ' ';
if (qmail_open(&qqt) == -1) { err_qqt(); return; }
qp = qmail_qp(&qqt);
out("354 go ahead\r\n");
@@ -389,6 +552,7 @@
if (!*qqx) { acceptmessage(qp); return; }
if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
+ if (flagexecutable) { out("552 we don't accept email with such content (#5.3.4)\r\n"); return; }
if (*qqx == 'D') out("554 "); else out("451 ");
out(qqx + 1);
out("\r\n");