]> git.pld-linux.org Git - packages/dspam-exim.git/blame - dspam_exim.c
- new
[packages/dspam-exim.git] / dspam_exim.c
CommitLineData
ac0f354f
AM
1 /**
2 * kSpam plugin for Exim Local Scan.
3 * Copyright (C) 2005 James Kibblewhite <kibble@aproxity.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * -----------------------------------------------------------------------------
20 * $Id$
21 * -----------------------------------------------------------------------------
22 *
23 * This local_scan.c file compiles in with exim4.
24 * Exim: http://www.exim.org/
25 * MySql: http://www.mysql.com/
26 * ClamAV: http://www.clamav.org/
27 * DSpam: http://www.nuclearelephant.com/projects/dspam/
28 *
29 * Changes:
30 * Version 0.8: Bug fixes further improved. No crashes can be replicated. Futher testing
31 * required. Removed debugging exim_mainlog output. Will try and get user
32 * rules implemented...
33 *
34 * Version 0.7: Bugs all fixed, seems to be a fully working system, need to test
35 * all features before full public release can be made...
36 * Will remove all debugging output in 0.9... public release v1.0
37 *
38 * Version 0.6: Improved email validation. Added `cleanitup` as a cleanup routeen
39 * ClamAV lib updated [now 0.8x & above required as min requirement]
40 * Bleeding Edge CVS of dspam is also required
41 *
42 * Version 0.5: Started ruleset loading support from database.
43 *
44 * Version 0.4: Got DSpam working and aliases sorted. First beta release.
45 *
46 * Version 0.3: Included a config file reader to take variables from the main configuration file.
47 * Also integrated the last of MySQL support and tidy duties on the code done.
48 *
49 * Version 0.2: Fixes to scan mbox style flatfiles with compressed files...
50 * thanks to ClamAV for: CL_ARCHIVE & CL_MAIL
51 * you've saved me the bother of extracting the mime base64 encoded stuff !! yay
52 *
53 * Version 0.1: For personal testing.
54 *
55 * Ideas: Can block email totally if probability & confidence is very high [like +95% (=>0.95)]
56 *
57 * Known bugs: [X] This is no longer a bug, all bugs fixed, testing is required...
58 *
59 * If you submit 'many' new emails all at once it has a tendancy to die but tell
60 * you that it has failed... Maybe by controlling the flow of emails will help
61 * [although will slow thru-put]. Will crash at 'mysql_real_connect()' in 'mysql_setup()'
62 * while trying to establish a connection on a second pass... [This is
63 *
64 * Donations: To paypal: jelly_bean_junky@hotmail.com 'if' you use this code commercially,
65 * ask your boss for it! And for a pay rise while your at it too... Otherwise I don't
66 * expect anything, unless you wanna send some cool stuff to me...
67 *
68 * Notes: This line may need appending to the end of the local_scan.o line:
69 * -I/usr/include/mysql -I/usr/include/dspam -DHAVE_CONFIG_H -DCONFIG_DEFAULT=/etc/sysconf.d/dspam.conf
70 */
71
72 /** include required by exim */
73 #include "local_scan.h"
74
75 /** include required by clamav for antivirus checking */
76 #include "clamav.h"
77
78 /** include required by mysql for access to the database */
79 #include "mysql.h"
80
81 /** include required by dspam for spam filtering */
82 #include "libdspam.h"
83
84 /** the usual suspects */
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <unistd.h>
88 #include <signal.h>
89 #include <string.h>
90 #include <fcntl.h>
91
92 #define SPAMREPT 2
93 #define FALSEPOS 4
94 #define SPAMFLAG 8
95 #define TOGBLACK 16
96 #define TOGWHITE 32
97 #define SMBYTE 64
98 #define EMBYTE 128
99 #define QMBYTE 256
100 #define HMBYTE 512
101 #define WMBYTE 1024
102 #define BUFFER_SIZE 2048
103
104 /** blocks hosts & ips & emails & headers */
105 typedef struct bhosts_s { /** _lscan._lusers_s.bhosts->next */
106 struct bhosts_s * next;
107 char * sender_hostname;
108 char * sender_ipaddr;
109 char * sender_logics; /** OR | AND -> hostname - ipaddr */
110 char * email;
111 char * email_mtype; /** contains | exact match */
112 char * header_field;
113 char * header_value;
114 char * header_mtype; /** contains | exact match -> header_value if header_value == NULL || "" use header_field */
115 char * logics; /** OR | AND -> all values */
116 } _bhosts_s;
117
118 /** linked list of local users requiring filtering */
119 typedef struct lusers_s { /** _lscan._lusers_s.username */
120 struct lusers_s * next;
121 _bhosts_s * bhosts;
122 int mailuser_id; /** refers to database id */
123 int enabled; /** is filtering enabled... */
124 char rcptname[EMBYTE]; /** rcpt name as appears in recipients_list */
125 char realemail[EMBYTE]; /** if rcptname is an alias, this will be the real email
126 for loading dpsma rules with, else set the same as rcptname */
127 } _lusers_s;
128
129 typedef struct email_struct {
130 char localpart[SMBYTE];
131 char domain[SMBYTE];
132 } _email_struct;
133
134 /** varaibles of mass instructions */
135 typedef struct lscan_structure {
136 MYSQL * mysql;
137 MYSQL_RES * result;
138 MYSQL_ROW row;
139 _lusers_s * l_users;
140 _email_struct lpart_domain;
141 struct cl_limits limits;
142 struct cl_node * root;
143 header_line * hl_ptr;
144 char * virname;
145 char emailaddy[HMBYTE];
146 char querystr[BUFFER_SIZE];
147 char buffer[BUFFER_SIZE];
148 char scanpath[BUFFER_SIZE];
149 int i;
150 int iNo;
151 int spamflag;
152 int writefd;
153 } _lscan;
154
155 _lscan lscan;
156
157 /**
158 * Remember to set LOCAL_SCAN_HAS_OPTIONS=yes in Local/Makefile
159 * otherwise you get stuck with the compile-time defaults
160 */
161 /** Al our variables we draw in from the 'exim-localscan.conf' file */
162 static uschar * database = US"socket_aproxity";
163 static uschar * hostname = US"localhost";
164 static uschar * password = US"password";
165 static uschar * poolpath = US"/home/mail/spool";
166 static uschar * spamflag = US"X-KD-Spam";
167 static uschar * username = US"mail";
168
169 optionlist local_scan_options[] = { /** alphabetical order */
170 { "database", opt_stringptr, &database },
171 { "hostname", opt_stringptr, &hostname },
172 { "password", opt_stringptr, &password },
173 { "poolpath", opt_stringptr, &poolpath },
174 { "spamflag", opt_stringptr, &spamflag },
175 { "username", opt_stringptr, &username }
176 };
177
178 int local_scan_options_count = sizeof(local_scan_options) / sizeof(optionlist);
179
180 #ifdef DLOPEN_LOCAL_SCAN
181 /** Return the verion of the local_scan ABI, if being compiled as a .so */
182 int local_scan_version_major(void) {
183 return(LOCAL_SCAN_ABI_VERSION_MAJOR);
184 }
185
186 int local_scan_version_minor(void) {
187 return(LOCAL_SCAN_ABI_VERSION_MINOR);
188 }
189
190 /**
191 * Left over for compatilibility with old patched exims that didn't have
192 * a version number with minor an major. Keep in mind that it will not work
193 * with older exim4s (I think 4.11 and above is required)
194 */
195
196 #ifdef DLOPEN_LOCAL_SCAN_OLD_API
197 int local_scan_version(void) {
198 return(1);
199 }
200 #endif
201 #endif
202
203 /** delete our cached file */
204 void del_cachef() {
205 if (unlink(lscan.scanpath)) {
206 debug_printf("file [%s] not removed", lscan.scanpath);
207 }
208 return;
209 } /** del_cachef */
210
211 /**
212 * Scan email for virus. Returns 1 if virus
213 * detected or 0 if no virus is detected. Sets
214 * lscan.virname to virua or error output...
215 */
216 int scan_clamav(char * scanpath) {
217
218 sprintf(lscan.scanpath, "%s", scanpath);
219 lscan.iNo = 0;
220
221 /** lets load all our virus defs database's into memory */
222 lscan.root = NULL; /** without this line, the dbload will crash... */
223 if((lscan.i = cl_loaddbdir(cl_retdbdir(), &lscan.root, &lscan.iNo))) {
224 sprintf(lscan.virname, "error: [%s]", cl_perror(lscan.i));
225 } else {
226 if((lscan.i = cl_build(lscan.root))) {
227 sprintf(lscan.virname, "database initialization error: [%s]", cl_perror(lscan.i));
228 cl_free(lscan.root);
229 }
230 memset(&lscan.limits, 0x0, sizeof(struct cl_limits));
231 lscan.limits.maxfiles = 1000; /** max files */
232 lscan.limits.maxfilesize = 10 * 1048576; /** maximal archived file size == 10 Mb */
233 lscan.limits.maxreclevel = 12; /** maximal recursion level */
234 lscan.limits.maxratio = 200; /** maximal compression ratio */
235 lscan.limits.archivememlim = 0; /** disable memory limit for bzip2 scanner */
236
237 if ((lscan.i = cl_scanfile(lscan.scanpath, (const char **)&lscan.virname, NULL, lscan.root,
238 &lscan.limits, CL_SCAN_ARCHIVE | CL_SCAN_MAIL | CL_SCAN_OLE2 | CL_SCAN_BLOCKBROKEN | CL_SCAN_HTML | CL_SCAN_PE)) != CL_VIRUS) {
239 if (lscan.i != CL_CLEAN) {
240 sprintf(lscan.virname, "error: [%s]", cl_perror(lscan.i));
241 } else {
242 lscan.virname = NULL;
243 }
244 }
245 if (lscan.root != NULL) {
246 cl_free(lscan.root);
247 }
248 memset(&lscan.limits, 0x0, sizeof(struct cl_limits));
249 }
250
251 /** lets delete the spool message as we don't need it any more */
252 if (lscan.virname != NULL) { /** remove the file if we have a virus as we are going to reject it */
253 return(1);
254 } else { /** else keep the file for spam filtering */
255 return(0);
256 }
257
258 return(1);
259
260 } /** scan_clamav */
261
262 void cache_mesg(int fd) {
263
264 fd = fd;
265
266 sprintf(lscan.scanpath, "%s/%s", poolpath, message_id);
267
268 /** create the file handler */
269 lscan.writefd = creat(lscan.scanpath, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
270
271 /** lets make this thing look like an email mbox structured thing or clamav won't work !! */
272 memset(lscan.buffer, 0x0, BUFFER_SIZE);
273 sprintf(lscan.buffer, "From %s Mon Jan 00 00:00:00 0000\n", sender_address);
274 lscan.i = write(lscan.writefd, lscan.buffer, strlen(lscan.buffer));
275
276 lscan.hl_ptr = header_list;
277 while (lscan.hl_ptr != NULL) {
278 /** type '*' means the header is internal, don't print it, or if the variable is NULL, what's the point...? */
279 if ((lscan.hl_ptr->type != '*') || (lscan.hl_ptr->text != NULL)) {
280 lscan.i = write(lscan.writefd, lscan.hl_ptr->text, strlen(lscan.hl_ptr->text));
281 }
282 lscan.hl_ptr = lscan.hl_ptr->next;
283 }
284
285 memset(lscan.buffer, 0x0, BUFFER_SIZE);
286 sprintf(lscan.buffer, "\n");
287 lscan.i = write(lscan.writefd, lscan.buffer, strlen(lscan.buffer));
288
289 /** output all the data, read from orignal and write to spool */
290 while ((lscan.i = read(fd, lscan.buffer, BUFFER_SIZE)) > 0) {
291 lscan.i = write(lscan.writefd, lscan.buffer, lscan.i);
292 }
293
294 /** close the handle */
295 lscan.i = close(lscan.writefd);
296 debug_printf("path of cached file [%s]", lscan.scanpath);
297
298 return;
299
300 } /** cache_mesg */
301
302 void remove_headers(char * hfield) {
303
304 lscan.hl_ptr = header_list;
305 while (lscan.hl_ptr != NULL) {
306 if ( ((lscan.hl_ptr->type != '*')) && (!strncmp(lscan.hl_ptr->text, hfield, strlen(hfield))) ) {
307 lscan.hl_ptr->type = '*';
308 }
309 lscan.hl_ptr = (struct header_line *)lscan.hl_ptr->next;
310 }
311 } /** remove_headers */
312
313 /**
314 * If the supplied email address is syntactically valid,
315 * spc_email_isvalid() will return 1; otherwise, it will
316 * return 0. Need to check that there is at least one '@'
317 * symbol and only one in the whole email address, else
318 * `getlocalp_domain` function won't work correctly...
319 */
320 int spc_email_isvalid(const char *address) {
321
322 int count = 0;
323 const char *c, *domain;
324 static char *rfc822_specials = "()<>@,;:\\\"[]/";
325
326 /** first we validate the name portion (name@domain) */
327 for (c = address; *c; c++) {
328 if ((*c == '\"') && (c == address || *(c - 1) == '.' || *(c - 1) == '\"')) {
329 while (*++c) {
330 if (*c == '\"') {
331 break;
332 }
333 if ((*c == '\\') && (*++c == ' ')) {
334 continue;
335 }
336 if (*c < ' ' || *c >= 127) {
337 return(0);
338 }
339 }
340 if (!*c++) {
341 return(0);
342 }
343 if (*c == '@') {
344 break;
345 }
346 if (*c != '.') {
347 return(0);
348 }
349 continue;
350 }
351 if (*c == '@') {
352 break;
353 }
354 if (*c <= ' ' || *c >= 127) {
355 return(0);
356 }
357 if (strchr(rfc822_specials, *c)) {
358 return(0);
359 }
360 }
361 if (c == address || *(c - 1) == '.') {
362 return(0);
363 }
364
365 /** next we validate the domain portion (name@domain) */
366 if (!*(domain = ++c)) {
367 return(0);
368 }
369
370 do {
371 if (*c == '.') {
372 if (c == domain || *(c - 1) == '.') {
373 return(0);
374 }
375 count++;
376 }
377 if (*c <= ' ' || *c >= 127) {
378 return(0);
379 }
380 if (strchr(rfc822_specials, *c)) {
381 return(0);
382 }
383 } while (*++c);
384 return(count >= 1);
385 } /** spc_email_isvalid */
386
387 /** this function returns the localpart and the domain section of an email in any given string */
388 _email_struct getlocalp_domain(char * emailaddr, _email_struct lpart_domain) {
389
390 memset(lscan.emailaddy, 0x0, HMBYTE);
391 memset(lpart_domain.localpart, 0x0, SMBYTE);
392 memset(lpart_domain.domain, 0x0, SMBYTE);
393 sprintf(lscan.emailaddy, "%s", emailaddr);
394
395 if (spc_email_isvalid(lscan.emailaddy)) {
396 sprintf(lpart_domain.localpart, "%s", strtok(lscan.emailaddy, "@"));
397 sprintf(lpart_domain.domain, "%s", strtok(NULL, "@"));
398 }
399 return(lpart_domain);
400 } /** getlocalp_domain */
401
402 /** mysql results and rows cleanup routine */
403 void mysqlrr_cleanup() {
404
405 lscan.row = NULL;
406
407 if (lscan.result != NULL) {
408 mysql_free_result(lscan.result);
409 lscan.result = NULL;
410 }
411
412 } /** mysqlrr_cleanup */
413
414 /** mysql results and rows cleanup routine */
415 void mysql_cleanup() {
416
417 mysqlrr_cleanup();
418
419 mysql_close(lscan.mysql);
420 memset(&lscan.mysql, 0x0, sizeof(lscan.mysql));
421 free(lscan.mysql);
422
423 } /** mysql_cleanup */
424
425 int mysql_setup() {
426
427 mysql_cleanup();
428
429 if (!(lscan.mysql = mysql_init(NULL))) {
430 log_write(0, LOG_MAIN, "mysql_init [%s]", mysql_error(lscan.mysql));
431 return(1);
432 }
433
434 /** we are always connecting to localhost!! to slow otherwise... */
435 if (!mysql_real_connect(lscan.mysql, hostname, username, password, database, 0, NULL, 0)) {
436 log_write(0, LOG_MAIN, "mysql_real_connect [%s]", mysql_error(lscan.mysql));
437 mysql_close(lscan.mysql);
438 return(1);
439 }
440
441 if (mysql_select_db(lscan.mysql, database)) {
442 log_write(0, LOG_MAIN, "mysql_select_db [%s]", mysql_error(lscan.mysql));
443 mysql_close(lscan.mysql);
444 return(1);
445 }
446
447 return(0);
448
449 } /** mysql_setup */
450
451 /**
452 * Instead of returning a row of data, I've decided to return
453 * the results to obtain the rows, incase I need more than one
454 * set of rows from the results. This basically runs the current
455 * sql query in lscan.querystr.
456 */
457 MYSQL_RES * get_mysqlres() {
458
459 /** clean up result and row if required */
460 mysqlrr_cleanup();
461
462 debug_printf("running query:\n\t[%s]\n", lscan.querystr);
463
464 if (mysql_real_query(lscan.mysql, lscan.querystr, strlen(lscan.querystr))) {
465 log_write(0, LOG_MAIN, "mysql_real_query [%s]", mysql_error(lscan.mysql));
466 return((MYSQL_RES * )NULL);
467 }
468
469 if (!(lscan.result = mysql_store_result(lscan.mysql))) {
470 log_write(0, LOG_MAIN, "mysql_store_result [%s]", mysql_error(lscan.mysql));
471 return((MYSQL_RES * )NULL);
472 }
473
474 if (mysql_num_rows(lscan.result) != 0) {
475 return(lscan.result);
476 }
477
478 return((MYSQL_RES * )NULL);
479 } /** get_mysqlres */
480
481 /** add user and rulesets */
482 _lusers_s * add_userset(_lusers_s * l_users, int mailuser_id, int enabled, char * rcptname, _email_struct lpart_domain) {
483
484 _lusers_s * lp = l_users;
485
486 if (enabled == 0) {
487 return(l_users);
488 }
489
490 /** remove any duplicates of users in linked list... */
491
492 if (l_users != NULL) {
493 while (l_users->next != NULL) {
494 l_users = (_lusers_s *)l_users->next;
495 }
496 l_users->next = (struct lusers_s *)malloc(sizeof(_lusers_s));
497 l_users = (_lusers_s *)l_users->next;
498
499 l_users->mailuser_id = mailuser_id;
500 l_users->enabled = enabled;
501 memset(l_users->rcptname, 0x0, EMBYTE);
502 memset(l_users->realemail, 0x0, EMBYTE);
503 sprintf(l_users->rcptname, "%s", rcptname);
504 sprintf(l_users->realemail, "%s@%s", (char *)lpart_domain.localpart, (char *)lpart_domain.domain);
505
506 l_users->next = NULL;
507 l_users = lp;
508 } else {
509 l_users = (_lusers_s *)(struct lusers_s *)malloc(sizeof(_lusers_s));
510
511 l_users->mailuser_id = mailuser_id;
512 l_users->enabled = enabled;
513 memset(l_users->rcptname, 0x0, EMBYTE);
514 memset(l_users->realemail, 0x0, EMBYTE);
515 sprintf(l_users->rcptname, "%s", rcptname);
516 sprintf(l_users->realemail, "%s@%s", (char *)lpart_domain.localpart, (char *)lpart_domain.domain);
517
518 l_users->next = NULL;
519 l_users = l_users;
520 }
521
522 /** we should now do a look up for the rules and add them to 'l_users->bhosts' */
523
524 return(l_users);
525 }
526
527 int load_realuser(char * emailaddr) {
528
529 lscan.lpart_domain = getlocalp_domain(emailaddr, lscan.lpart_domain); /** lscan.lpart_domain.localpart && lscan.lpart_domain.domain */
530 memset(lscan.querystr, 0x0, BUFFER_SIZE);
531 sprintf(lscan.querystr, "SELECT mailuser_id, enabled FROM mail_mailusers WHERE local_part = '%s' AND domain = '%s'", lscan.lpart_domain.localpart, lscan.lpart_domain.domain);
532
533 if (get_mysqlres()) { /** sets lscan.result to results returned from db */
534 if (!(lscan.row = mysql_fetch_row(lscan.result))) { /** lscan.row[0] */
535 log_write(0, LOG_MAIN, "mysql_fetch_row [%s]", mysql_error(lscan.mysql));
536 return(1);
537 }
538 } else {
539 return(1);
540 }
541
542 /** add user to results & rules... */
543 if ((int)atoi(lscan.row[1]) != 0) {
544 log_write(0, LOG_MAIN, "adding user ...");
545 lscan.l_users = add_userset(lscan.l_users, (int)atoi(lscan.row[0]), (int)atoi(lscan.row[1]), (char *)recipients_list[lscan.i].address, lscan.lpart_domain);
546 }
547
548 return(0);
549 } /** load_realuser */
550
551 int load_aliases(char * emailaddr) {
552
553 lscan.lpart_domain = getlocalp_domain(emailaddr, lscan.lpart_domain); /** lscan.lpart_domain.localpart && lscan.lpart_domain.domain */
554 memset(lscan.querystr, 0x0, BUFFER_SIZE);
555 sprintf(lscan.querystr, "SELECT alias FROM mail_aliases WHERE local_part = '%s' AND domain = '%s'", lscan.lpart_domain.localpart, lscan.lpart_domain.domain);
556
557 if (get_mysqlres()) { /** sets lscan.result to results returned from db */
558 if (!(lscan.row = mysql_fetch_row(lscan.result))) { /** lscan.row[0] */
559 log_write(0, LOG_MAIN, "mysql_fetch_row [%s]", mysql_error(lscan.mysql));
560 return(1);
561 }
562 if (load_realuser((char *)lscan.row[0])) { /** failed to load alias as real user */
563 return(1);
564 }
565 } else {
566 /** check for wildcard localpart */
567 memset(lscan.querystr, 0x0, BUFFER_SIZE);
568 sprintf(lscan.querystr, "SELECT alias FROM mail_aliases WHERE local_part = '*' AND domain = '%s'", lscan.lpart_domain.domain);
569
570 if (get_mysqlres()) { /** sets lscan.result to results returned from db */
571 if (!(lscan.row = mysql_fetch_row(lscan.result))) { /** lscan.row[0] */
572 log_write(0, LOG_MAIN, "mysql_fetch_row [%s]", mysql_error(lscan.mysql));
573 return(1);
574 }
575 if (load_realuser((char *)lscan.row[0])) { /** failed to load alias as real user */
576 return(1);
577 }
578 }
579 }
580
581 return(0);
582 } /** load_aliases */
583
584 /** this loads all users settings into memory */
585 int init_users() {
586
587 if (mysql_setup()) {
588 return(1);
589 }
590
591 for (lscan.i = 0; lscan.i != recipients_count; lscan.i++) {
592 if (load_realuser(recipients_list[lscan.i].address)) {
593 debug_printf("cound not find user [%s]", recipients_list[lscan.i].address);
594 if (load_aliases(recipients_list[lscan.i].address)) {
595 debug_printf("alias [%s] not found check your sql schema", recipients_list[lscan.i].address);
596 }
597 }
598 }
599
600 mysql_cleanup();
601
602 return(0);
603 } /** init_users */
604
605 char * read_emailmem(char * message) {
606
607 long len = 1;
608 long oCount = 0;
609 int fd = 0;
610
611 memset(lscan.buffer, 0x0, BUFFER_SIZE);
612 fd = open(lscan.scanpath, O_RDONLY);
613 /* read in the message from stdin */
614 message[0] = 0;
615 while ((oCount = read(fd, lscan.buffer, sizeof(lscan.buffer))) > 0) {
616 len += strlen(lscan.buffer);
617 message = realloc(message, len);
618 strcat(message, lscan.buffer);
619 }
620 close(fd);
621
622 return(message);
623 } /** read_emailmem */
624
625 /**
626 * Usage:
627 * CTX = attach_ctx_dbaccess(CTX);
628 *
629 29903: [3/2/2005 22:34:24] bailing on error 22
630 29903: [3/2/2005 22:34:24] received invalid result (! DSR_ISSPAM || DSR_INNOCENT || DSR_ISWHITELISTED): 22
631 -> happened because read_emailmem() was not returning the message ptr correctly...
632 */
633 DSPAM_CTX * attach_ctx_dbaccess(DSPAM_CTX * CTX) {
634
635 if (dspam_clearattributes(CTX)) {
636 log_write(0, LOG_MAIN, "dspam_clearattributes failed!");
637 }
638
639 dspam_addattribute(CTX, "MySQLServer", (const char *)hostname);
640 dspam_addattribute(CTX, "MySQLPort", (const char *)"3306");
641 dspam_addattribute(CTX, "MySQLUser", (const char *)username);
642 dspam_addattribute(CTX, "MySQLPass", (const char *)password);
643 dspam_addattribute(CTX, "MySQLDb", (const char *)database);
644 dspam_addattribute(CTX, "IgnoreHeader", (const char *)spamflag);
645
646 if (dspam_attach(CTX, (void *)NULL)) {
647 log_write(0, LOG_MAIN, "dspam_attach failed!");
648 CTX = NULL;
649 }
650
651 return(CTX);
652 } /** attach_ctx_dbaccess */
653
654 void load_usersrs(_lusers_s * l_users) {
655
656 _lusers_s * tmp = l_users;
657 char * message = malloc(1);
658 DSPAM_CTX * CTX = NULL; /** DSPAM Context */
659 struct _ds_spam_signature SIG; /** Example signature */
660
661 if (tmp == NULL) {
662 memset(message, 0x0, sizeof(message));
663 free(message);
664 return;
665 }
666
667 message = read_emailmem(message);
668
669 while (tmp != NULL) {
670
671 CTX = dspam_create((char *)tmp->realemail, NULL, NULL, DSM_PROCESS, DSF_CHAINED | DSF_SIGNATURE | DSF_NOISE);
672 CTX = attach_ctx_dbaccess(CTX);
673 if (CTX == NULL) {
674 log_write(0, LOG_MAIN, "dspam_create failed!");
675 break;
676 }
677 if (dspam_process(CTX, message) != 0) { /** Call DSPAM's processor with the message text */
678 log_write(0, LOG_MAIN, "dspam_process failed!");
679 dspam_destroy(CTX);
680 break;
681 }
682 if (CTX->result == DSR_ISSPAM) { /** Print processing results */
683 log_write(0, LOG_MAIN, "spam->[%s]:\n\tProbability:\t[%2.4f]\n\tConfidence:\t[%2.4f]",
684 (char *)tmp->realemail, CTX->probability, CTX->confidence);
685 header_add(' ', "%s: %s\n", (char *)spamflag, (char *)tmp->realemail);
686 lscan.spamflag = SPAMFLAG;
687 } else {
688 log_write(0, LOG_MAIN, "not spam->[%s]", (char *)tmp->realemail);
689 lscan.spamflag = 0;
690 }
691
692 if (CTX->signature != NULL) {
693 SIG.data = malloc(CTX->signature->length);
694 if (SIG.data != NULL) {
695 memcpy(SIG.data, CTX->signature->data, CTX->signature->length);
696 }
697 }
698 SIG.length = CTX->signature->length;
699
700 if (dspam_destroy(CTX) != 0) { /** Destroy the context */
701 log_write(0, LOG_MAIN, "dspam_destroy failed!");
702 break;
703 }
704
705 tmp = (_lusers_s *)tmp->next; /** Move on to next user */
706 }
707
708 memset(message, 0x0, sizeof(message));
709 free(message);
710 memset(&CTX, 0x0, sizeof(CTX));
711 free(CTX);
712
713 return;
714 } /** load_usersrs */
715
716 int report_spam(int spamflag) {
717
718 int sflag = 0;
719 char * message = malloc(1);
720 DSPAM_CTX * CTX = NULL; /** DSPAM Context */
721 struct _ds_spam_signature SIG; /** Example signature */
722
723 message = read_emailmem(message);
724
725 switch (spamflag) {
726 case SPAMREPT: /** set up the context for error correction as spam */
727 CTX = dspam_create((char *)sender_address, NULL, NULL, DSM_PROCESS, DSF_CHAINED);
728 CTX = attach_ctx_dbaccess(CTX);
729 if (CTX == NULL) {
730 log_write(0, LOG_MAIN, "ERROR: dspam_create failed!\n");
731 sflag = 1;
732 }
733 CTX->classification = DSR_ISSPAM;
734 CTX->source = DSS_ERROR;
735 break;
736 case FALSEPOS: /** set up the context for error correction as innocent */
737 CTX = dspam_create((char *)sender_address, NULL, NULL, DSM_PROCESS, DSF_CHAINED | DSF_SIGNATURE);
738 CTX = attach_ctx_dbaccess(CTX);
739 if (CTX == NULL) {
740 log_write(0, LOG_MAIN, "ERROR: dspam_create failed!\n");
741 sflag = 1;
742 }
743 CTX->classification = DSR_ISINNOCENT;
744 CTX->source = DSS_ERROR;
745 CTX->signature = &SIG; /** Attach the signature to the context */
746 break;
747 default: /** no reporting required, scan for spam perhaps ? */
748 log_write(0, LOG_MAIN, "report_spam -> no reporting required");
749 break;
750 }
751
752 if (dspam_process(CTX, message) != 0) { /** Call DSPAM */
753 log_write(0, LOG_MAIN, "ERROR: dspam_process failed!");
754 sflag = 1;
755 }
756
757 if (dspam_destroy(CTX) != 0) { /** Destroy the context */
758 log_write(0, LOG_MAIN, "ERROR: dspam_destroy failed!");
759 sflag = 1;
760 }
761
762 memset(message, 0x0, sizeof(message));
763 free(message);
764 memset(&CTX, 0x0, sizeof(CTX));
765 free(CTX);
766
767 if (sflag == 0) {
768 log_write(0, LOG_MAIN, "<= %s [%s] P=%s A=%s:%s",
769 (char *)sender_address, (char *)sender_host_address, (char *)received_protocol, (char *)sender_host_authenticated, (char *)sender_address);
770 log_write(0, LOG_MAIN, "=> (null) <%s> R=system_localuser T=local_delivery", (char *)recipients_list[0].address);
771 }
772
773 /** if we are reporting, which is pretty much so if you reach here, we blackhole the email */
774 recipients_count = 0;
775
776 return(sflag);
777 } /** report_spam */
778
779 int check_spamflag(char * subspamdom) {
780
781 memset(lscan.emailaddy, 0x0, HMBYTE);
782 sprintf(lscan.emailaddy, "%s@%s.%s", lscan.lpart_domain.localpart, subspamdom, lscan.lpart_domain.domain);
783
784 if (strcmp(lscan.emailaddy, recipients_list[0].address)) {
785 return(0);
786 }
787
788 return(1);
789 } /** check_spamflag */
790
791 int getrept_type() {
792
793 if (check_spamflag("spamrept")) {
794 return(SPAMREPT);
795 }
796
797 if (check_spamflag("falsepos")) {
798 return(FALSEPOS);
799 }
800
801 return(0);
802 } /** getrept_type */
803
804 /**
805 * Providing some sort of clean up routeen...
806 */
807 int cleanitup(int return_flag) {
808
809 /** log_write(0, LOG_MAIN, "shutting down driver...");
810 dspam_shutdown_driver(NULL);
811 log_write(0, LOG_MAIN, "dspam has been shut down!"); */
812
813 mysql_cleanup();
814 del_cachef();
815
816 return(return_flag);
817 } /** cleanitup */
818
819 void inititial_spam_filtering() {
820
821 remove_headers(spamflag);
822
823 if (init_users()) {
824 log_write(0, LOG_MAIN, "Opps!!");
825 return;
826 }
827
828 /** lets start dspam filtering... */
829 load_usersrs(lscan.l_users);
830
831 return;
832 } /** inititial_spam_filtering */
833
834 /**
835 * Note to self: need to provide a cleanup function for '_lusers_s'
836 */
837 int local_scan(volatile int fd, uschar **return_text) {
838
839 fd = fd;
840 return_text = return_text;
841
842 /** log_write(0, LOG_MAIN, "init dspam driver...");
843 dspam_init_driver(NULL);
844 log_write(0, LOG_MAIN, "dspam driver init completed!"); */
845
846 cache_mesg(fd);
847 if (scan_clamav(lscan.scanpath)) {
848 log_write(0, LOG_MAIN, "rejecting email [%s] contains known virus [%s]", message_id, lscan.virname);
849 return(cleanitup(LOCAL_SCAN_REJECT));
850 }
851
852 /** check is spam reporting [spamrept.domain.tld|falsepos.domain.tld] */
853 lscan.lpart_domain = getlocalp_domain((char *)sender_address, lscan.lpart_domain);
854 if ((recipients_count == 1) && (sender_host_authenticated != NULL)) { /** must be authenticated with only one recipient */
855
856 switch (getrept_type()) {
857 case SPAMREPT: /** report spam email */
858 if (report_spam(SPAMREPT)) {
859 log_write(0, LOG_MAIN, "it all went wrong... [spamrept]");
860 }
861 break;
862 case FALSEPOS: /** report false positive */
863 log_write(0, LOG_MAIN, "reporting [falsepos]");
864 if (report_spam(FALSEPOS)) {
865 log_write(0, LOG_MAIN, "it all went wrong... [falsepos]");
866 }
867 break;
868 default: /** no reporting required, scan for spam perhaps ? */
869 log_write(0, LOG_MAIN, "single user rcpt, scanning...");
870 break;
871 }
872 } else if ( (check_spamflag("falsepos")) || (check_spamflag("spamrept")) ) {
873 /** user's can not report spam if they are not authenticated */
874 return(cleanitup(LOCAL_SCAN_REJECT));
875 }
876
877 if (sender_host_authenticated == NULL) {
878 inititial_spam_filtering();
879 /**
880 * some very simple method of slowing spammers down... [teergrube??]
881 * maybe some indication on 'probability' & 'confidence' we can lenghten time
882 * perhaps even a record on blacklists... ? Rejection here too ?
883 */
884 /** if (lscan.spamflag == SPAMFLAG) {
885 alarm(0);
886 sleep(30);
887 } */
888 }
889
890 return(cleanitup(LOCAL_SCAN_ACCEPT));
891 } /** local_scan */
892
This page took 0.225009 seconds and 4 git commands to generate.