1 | /* $Id: agent_shared.c,v 1.86 2011/07/11 22:05:48 sbajic Exp $ */ |
---|
2 | |
---|
3 | /* |
---|
4 | DSPAM |
---|
5 | COPYRIGHT (C) 2002-2012 DSPAM PROJECT |
---|
6 | |
---|
7 | This program is free software: you can redistribute it and/or modify |
---|
8 | it under the terms of the GNU Affero General Public License as |
---|
9 | published by the Free Software Foundation, either version 3 of the |
---|
10 | License, or (at your option) any later version. |
---|
11 | |
---|
12 | This program is distributed in the hope that it will be useful, |
---|
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | GNU Affero General Public License for more details. |
---|
16 | |
---|
17 | You should have received a copy of the GNU Affero General Public License |
---|
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
19 | |
---|
20 | */ |
---|
21 | |
---|
22 | /* |
---|
23 | * agent_shared.c - shared agent-based components |
---|
24 | * |
---|
25 | * DESCRIPTION |
---|
26 | * agent-based components shared between the full dspam agent (dspam) |
---|
27 | * and the lightweight client agent (dspamc) |
---|
28 | */ |
---|
29 | |
---|
30 | #ifdef HAVE_CONFIG_H |
---|
31 | #include <auto-config.h> |
---|
32 | #endif |
---|
33 | |
---|
34 | #include <stdio.h> |
---|
35 | #include <stdlib.h> |
---|
36 | #include <string.h> |
---|
37 | #include <ctype.h> |
---|
38 | #include <errno.h> |
---|
39 | #ifdef HAVE_UNISTD_H |
---|
40 | #include <unistd.h> |
---|
41 | #include <pwd.h> |
---|
42 | #endif |
---|
43 | #include <sys/types.h> |
---|
44 | #include <signal.h> |
---|
45 | #include <sys/stat.h> |
---|
46 | #ifdef _WIN32 |
---|
47 | #include <io.h> |
---|
48 | #include <process.h> |
---|
49 | #define WIDEXITED(x) 1 |
---|
50 | #define WEXITSTATUS(x) (x) |
---|
51 | #include <windows.h> |
---|
52 | #else |
---|
53 | #include <sys/wait.h> |
---|
54 | #include <sys/param.h> |
---|
55 | #endif |
---|
56 | #include "util.h" |
---|
57 | #include "read_config.h" |
---|
58 | #ifdef DAEMON |
---|
59 | #include "daemon.h" |
---|
60 | #include "dspam.h" |
---|
61 | #endif |
---|
62 | |
---|
63 | #ifdef TIME_WITH_SYS_TIME |
---|
64 | # include <sys/time.h> |
---|
65 | # include <time.h> |
---|
66 | #else |
---|
67 | # ifdef HAVE_SYS_TIME_H |
---|
68 | # include <sys/time.h> |
---|
69 | # else |
---|
70 | # include <time.h> |
---|
71 | # endif |
---|
72 | #endif |
---|
73 | |
---|
74 | #include "agent_shared.h" |
---|
75 | #include "language.h" |
---|
76 | #include "buffer.h" |
---|
77 | |
---|
78 | char * __pw_name = NULL; |
---|
79 | uid_t __pw_uid; |
---|
80 | |
---|
81 | /* |
---|
82 | * initialize_atx(AGENT_CTX *) |
---|
83 | * |
---|
84 | * DESCRIPTION |
---|
85 | * initializes an existing agent context |
---|
86 | * |
---|
87 | * INPUT ARGUMENTS |
---|
88 | * ATX agent context to initialize |
---|
89 | * |
---|
90 | * RETURN VALUES |
---|
91 | * returns 0 on success |
---|
92 | */ |
---|
93 | |
---|
94 | int initialize_atx(AGENT_CTX *ATX) { |
---|
95 | memset(ATX, 0, sizeof(AGENT_CTX)); |
---|
96 | ATX->training_buffer = 0; |
---|
97 | ATX->train_pristine = 0; |
---|
98 | ATX->classification = DSR_NONE; |
---|
99 | ATX->source = DSS_NONE; |
---|
100 | ATX->operating_mode = DSM_PROCESS; |
---|
101 | ATX->fork = 1; |
---|
102 | ATX->users = nt_create (NT_CHAR); |
---|
103 | |
---|
104 | if (ATX->users == NULL) { |
---|
105 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
106 | return EUNKNOWN; |
---|
107 | } |
---|
108 | |
---|
109 | #ifdef TRUSTED_USER_SECURITY |
---|
110 | if (!__pw_name) { |
---|
111 | LOG(LOG_ERR, ERR_AGENT_RUNTIME_USER); |
---|
112 | exit(EXIT_FAILURE); |
---|
113 | } |
---|
114 | |
---|
115 | LOGDEBUG("checking trusted user list for %s(%d)", __pw_name, __pw_uid); |
---|
116 | |
---|
117 | if (__pw_uid == 0) |
---|
118 | ATX->trusted = 1; |
---|
119 | else |
---|
120 | ATX->trusted = _ds_match_attribute(agent_config, "Trust", __pw_name); |
---|
121 | |
---|
122 | if (!ATX->trusted) |
---|
123 | nt_add (ATX->users, __pw_name); |
---|
124 | #endif |
---|
125 | |
---|
126 | return 0; |
---|
127 | } |
---|
128 | |
---|
129 | /* |
---|
130 | * process_arguments(AGENT_CTX *, int argc, char *argv[]) |
---|
131 | * |
---|
132 | * DESCRIPTION |
---|
133 | * master commandline argument process loop |
---|
134 | * |
---|
135 | * INPUT ARGUMENTS |
---|
136 | * ATX agent context |
---|
137 | * argc number of arguments provided |
---|
138 | * argv array of arguments |
---|
139 | * |
---|
140 | * RETURN VALUES |
---|
141 | * returns 0 on success, EINVAL when invalid options specified |
---|
142 | */ |
---|
143 | |
---|
144 | int process_arguments(AGENT_CTX *ATX, int argc, char **argv) { |
---|
145 | int flag_u = 0, flag_r = 0; |
---|
146 | int client = (_ds_read_attribute(agent_config, "ClientHost") != NULL); |
---|
147 | char *ptrptr; |
---|
148 | int i; |
---|
149 | |
---|
150 | #ifdef DEBUG |
---|
151 | ATX->debug_args[0] = 0; |
---|
152 | #endif |
---|
153 | ATX->client_args[0] = 0; |
---|
154 | |
---|
155 | for (i=0; i<argc; i++) |
---|
156 | { |
---|
157 | |
---|
158 | #ifdef DEBUG |
---|
159 | strlcat (ATX->debug_args, argv[i], sizeof (ATX->debug_args)); |
---|
160 | strlcat (ATX->debug_args, " ", sizeof (ATX->debug_args)); |
---|
161 | #endif |
---|
162 | |
---|
163 | /* Terminate user/rcpt lists */ |
---|
164 | |
---|
165 | if ((flag_u || flag_r) && |
---|
166 | (argv[i][0] == '-' || argv[i][0] == 0 || !strcmp(argv[i], "--"))) |
---|
167 | { |
---|
168 | flag_u = flag_r = 0; |
---|
169 | if (!strcmp(argv[i], "--")) |
---|
170 | continue; |
---|
171 | } |
---|
172 | |
---|
173 | if (!strcmp (argv[i], "--user")) { |
---|
174 | flag_u = 1; |
---|
175 | continue; |
---|
176 | } |
---|
177 | |
---|
178 | if (!strcmp (argv[i], "--rcpt-to")) |
---|
179 | { |
---|
180 | if (!ATX->recipients) { |
---|
181 | ATX->recipients = nt_create(NT_CHAR); |
---|
182 | if (ATX->recipients == NULL) { |
---|
183 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
184 | return EUNKNOWN; |
---|
185 | } |
---|
186 | } |
---|
187 | flag_r = 1; |
---|
188 | continue; |
---|
189 | } |
---|
190 | |
---|
191 | /* Build arg list to pass to server (when in client/server mode) */ |
---|
192 | |
---|
193 | if (client && !flag_u && !flag_r && i>0) |
---|
194 | { |
---|
195 | if (argv[i][0] == 0) |
---|
196 | strlcat(ATX->client_args, "\"", sizeof(ATX->client_args)); |
---|
197 | strlcat (ATX->client_args, argv[i], sizeof(ATX->client_args)); |
---|
198 | if (argv[i][0] == 0) |
---|
199 | strlcat(ATX->client_args, "\"", sizeof(ATX->client_args)); |
---|
200 | strlcat (ATX->client_args, " ", sizeof(ATX->client_args)); |
---|
201 | } |
---|
202 | |
---|
203 | if (!strcmp (argv[i], "--debug")) |
---|
204 | { |
---|
205 | #ifdef DEBUG |
---|
206 | if (DO_DEBUG == 0) |
---|
207 | DO_DEBUG = 1; |
---|
208 | #endif |
---|
209 | continue; |
---|
210 | } |
---|
211 | |
---|
212 | #if defined(DAEMON) && !defined(_DSPAMC_H) |
---|
213 | |
---|
214 | if (!strcmp (argv[i], "--client")) { |
---|
215 | ATX->client_mode = 1; |
---|
216 | continue; |
---|
217 | } |
---|
218 | |
---|
219 | #ifdef TRUSTED_USER_SECURITY |
---|
220 | if (!strcmp (argv[i], "--daemon") && ATX->trusted) |
---|
221 | #else |
---|
222 | if (!strcmp (argv[i], "--daemon")) |
---|
223 | #endif |
---|
224 | { |
---|
225 | ATX->operating_mode = DSM_DAEMON; |
---|
226 | continue; |
---|
227 | } |
---|
228 | #endif |
---|
229 | |
---|
230 | if (!strcmp (argv[i], "--nofork")) { |
---|
231 | ATX->fork = 0; |
---|
232 | continue; |
---|
233 | } |
---|
234 | |
---|
235 | if (!strncmp (argv[i], "--mode=", 7)) |
---|
236 | { |
---|
237 | char *mode = strchr(argv[i], '=')+1; |
---|
238 | if (process_mode(ATX, mode)) |
---|
239 | return EINVAL; |
---|
240 | ATX->flags |= DAF_FIXED_TR_MODE; |
---|
241 | continue; |
---|
242 | } |
---|
243 | |
---|
244 | /* Build RCPT TO list */ |
---|
245 | |
---|
246 | if (flag_r) |
---|
247 | { |
---|
248 | if (argv[i] != NULL && strlen (argv[i]) < MAX_USERNAME_LENGTH) |
---|
249 | { |
---|
250 | char user[MAX_USERNAME_LENGTH]; |
---|
251 | |
---|
252 | if (_ds_match_attribute(agent_config, "Broken", "case")) |
---|
253 | lc(user, argv[i]); |
---|
254 | else |
---|
255 | strcpy(user, argv[i]); |
---|
256 | |
---|
257 | #ifdef TRUSTED_USER_SECURITY |
---|
258 | if (!ATX->trusted && strcmp(user, __pw_name)) { |
---|
259 | LOG(LOG_ERR, ERR_TRUSTED_USER, __pw_uid, __pw_name); |
---|
260 | return EINVAL; |
---|
261 | } |
---|
262 | |
---|
263 | if (ATX->trusted) { |
---|
264 | #endif |
---|
265 | if (_ds_validate_address(user) == 1) { |
---|
266 | nt_add (ATX->recipients, user); |
---|
267 | } else { |
---|
268 | LOG(LOG_ERR, "Invalid email address: %s", user); |
---|
269 | return EINVAL; |
---|
270 | } |
---|
271 | #ifdef TRUSTED_USER_SECURITY |
---|
272 | } |
---|
273 | #endif |
---|
274 | } |
---|
275 | continue; |
---|
276 | } |
---|
277 | |
---|
278 | /* Build process user list */ |
---|
279 | |
---|
280 | if (flag_u) |
---|
281 | { |
---|
282 | if (argv[i] != NULL && strlen (argv[i]) < MAX_USERNAME_LENGTH) |
---|
283 | { |
---|
284 | if (strstr(argv[i], "../") != NULL || strstr(argv[i], "..\\") != NULL) { |
---|
285 | LOG(LOG_ERR, "Illegal username ('../' or '..\\' not allowed in username)"); |
---|
286 | return EINVAL; |
---|
287 | } else { |
---|
288 | char user[MAX_USERNAME_LENGTH]; |
---|
289 | |
---|
290 | if (_ds_match_attribute(agent_config, "Broken", "case")) |
---|
291 | lc(user, argv[i]); |
---|
292 | else |
---|
293 | strcpy(user, argv[i]); |
---|
294 | |
---|
295 | #ifdef TRUSTED_USER_SECURITY |
---|
296 | if (!ATX->trusted && strcmp(user, __pw_name)) { |
---|
297 | LOG(LOG_ERR, ERR_TRUSTED_USER, __pw_uid, __pw_name); |
---|
298 | return EINVAL; |
---|
299 | } |
---|
300 | |
---|
301 | if (ATX->trusted) |
---|
302 | #endif |
---|
303 | nt_add (ATX->users, user); |
---|
304 | } |
---|
305 | } |
---|
306 | continue; |
---|
307 | } |
---|
308 | |
---|
309 | if (!strncmp (argv[i], "--mail-from=", 12)) |
---|
310 | { |
---|
311 | strlcpy(ATX->mailfrom, strchr(argv[i], '=')+1, sizeof(ATX->mailfrom)); |
---|
312 | LOGDEBUG("MAIL FROM: %s", ATX->mailfrom); |
---|
313 | continue; |
---|
314 | } |
---|
315 | |
---|
316 | if (!strncmp (argv[i], "--profile=", 10)) |
---|
317 | { |
---|
318 | #ifdef TRUSTED_USER_SECURITY |
---|
319 | if (!ATX->trusted) { |
---|
320 | LOG(LOG_ERR, ERR_TRUSTED_PRIV, "--profile", |
---|
321 | __pw_uid, __pw_name); |
---|
322 | return EINVAL; |
---|
323 | } |
---|
324 | #endif |
---|
325 | if (!_ds_match_attribute(agent_config, "Profile", argv[i]+10)) { |
---|
326 | LOG(LOG_ERR,ERR_AGENT_NO_SUCH_PROFILE, argv[i]+10); |
---|
327 | return EINVAL; |
---|
328 | } else { |
---|
329 | _ds_overwrite_attribute(agent_config, "DefaultProfile", argv[i]+10); |
---|
330 | } |
---|
331 | continue; |
---|
332 | } |
---|
333 | |
---|
334 | if (!strncmp (argv[i], "--signature=", 12)) |
---|
335 | { |
---|
336 | strlcpy(ATX->signature, strchr(argv[i], '=')+1, sizeof(ATX->signature)); |
---|
337 | continue; |
---|
338 | } |
---|
339 | |
---|
340 | if (!strncmp (argv[i], "--class=", 8)) |
---|
341 | { |
---|
342 | char *ptr = strchr(argv[i], '=')+1; |
---|
343 | char *spam = _ds_read_attribute(agent_config, "ClassAliasSpam"); |
---|
344 | char *nonspam = _ds_read_attribute(agent_config, "ClassAliasNonspam"); |
---|
345 | if (!strcmp(ptr, "spam") || (spam && !strcmp(ptr, spam))) |
---|
346 | { |
---|
347 | ATX->classification = DSR_ISSPAM; |
---|
348 | } else if (!strcmp(ptr, "innocent") || !strcmp(ptr, "nonspam") || |
---|
349 | (nonspam && !strcmp(ptr, nonspam))) |
---|
350 | { |
---|
351 | ATX->classification = DSR_ISINNOCENT; |
---|
352 | } |
---|
353 | else |
---|
354 | { |
---|
355 | LOG(LOG_ERR, ERR_AGENT_NO_SUCH_CLASS, ptr); |
---|
356 | return EINVAL; |
---|
357 | } |
---|
358 | continue; |
---|
359 | } |
---|
360 | |
---|
361 | if (!strncmp (argv[i], "--source=", 9)) |
---|
362 | { |
---|
363 | char *ptr = strchr(argv[i], '=')+1; |
---|
364 | |
---|
365 | if (!strcmp(ptr, "corpus")) |
---|
366 | ATX->source = DSS_CORPUS; |
---|
367 | else if (!strcmp(ptr, "inoculation")) |
---|
368 | ATX->source = DSS_INOCULATION; |
---|
369 | else if (!strcmp(ptr, "error")) |
---|
370 | ATX->source = DSS_ERROR; |
---|
371 | else |
---|
372 | { |
---|
373 | LOG(LOG_ERR, ERR_AGENT_NO_SUCH_SOURCE, ptr); |
---|
374 | return EINVAL; |
---|
375 | } |
---|
376 | continue; |
---|
377 | } |
---|
378 | |
---|
379 | if (!strcmp (argv[i], "--classify")) |
---|
380 | { |
---|
381 | ATX->operating_mode = DSM_CLASSIFY; |
---|
382 | ATX->training_mode = DST_NOTRAIN; |
---|
383 | continue; |
---|
384 | } |
---|
385 | |
---|
386 | if (!strcmp (argv[i], "--process")) |
---|
387 | { |
---|
388 | ATX->operating_mode = DSM_PROCESS; |
---|
389 | continue; |
---|
390 | } |
---|
391 | |
---|
392 | if (!strncmp (argv[i], "--deliver=", 10)) |
---|
393 | { |
---|
394 | char *dup = strdup(strchr(argv[i], '=')+1); |
---|
395 | char *ptr; |
---|
396 | if (dup == NULL) { |
---|
397 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
398 | return EUNKNOWN; |
---|
399 | } |
---|
400 | |
---|
401 | ptr = strtok_r(dup, ",", &ptrptr); |
---|
402 | while(ptr != NULL) { |
---|
403 | if (!strcmp(ptr, "stdout")) { |
---|
404 | ATX->flags |= DAF_DELIVER_SPAM; |
---|
405 | ATX->flags |= DAF_DELIVER_INNOCENT; |
---|
406 | ATX->flags |= DAF_STDOUT; |
---|
407 | } |
---|
408 | else if (!strcmp(ptr, "spam")) |
---|
409 | ATX->flags |= DAF_DELIVER_SPAM; |
---|
410 | else if (!strcmp(ptr, "innocent") || !strcmp(ptr, "nonspam")) |
---|
411 | ATX->flags |= DAF_DELIVER_INNOCENT; |
---|
412 | else if (!strcmp(ptr, "summary")) |
---|
413 | ATX->flags |= DAF_SUMMARY; |
---|
414 | else |
---|
415 | { |
---|
416 | LOG(LOG_ERR, ERR_AGENT_NO_SUCH_DELIVER, ptr); |
---|
417 | free(dup); |
---|
418 | return EINVAL; |
---|
419 | } |
---|
420 | |
---|
421 | ptr = strtok_r(NULL, ",", &ptrptr); |
---|
422 | } |
---|
423 | free(dup); |
---|
424 | continue; |
---|
425 | } |
---|
426 | |
---|
427 | if (!strncmp (argv[i], "--feature=", 10)) |
---|
428 | { |
---|
429 | ATX->feature = 1; |
---|
430 | process_features(ATX, strchr(argv[i], '=')+1); |
---|
431 | continue; |
---|
432 | } |
---|
433 | |
---|
434 | if (!strcmp (argv[i], "--stdout")) |
---|
435 | { |
---|
436 | ATX->flags |= DAF_STDOUT; |
---|
437 | continue; |
---|
438 | } |
---|
439 | |
---|
440 | if (!strcmp (argv[i], "--help")) |
---|
441 | { |
---|
442 | fprintf (stderr, "%s\n", SYNTAX); |
---|
443 | exit(EXIT_SUCCESS); |
---|
444 | } |
---|
445 | |
---|
446 | if (!strcmp (argv[i], "--version")) |
---|
447 | { |
---|
448 | printf ("\nDSPAM Anti-Spam Suite %s (agent/library)\n\n", VERSION); |
---|
449 | printf ("Copyright (C) 2002-2012 DSPAM Project\n"); |
---|
450 | printf ("http://dspam.sourceforge.net.\n\n"); |
---|
451 | printf ("DSPAM may be copied only under the terms of the GNU Affero General Public\n"); |
---|
452 | printf ("License, a copy of which can be found with the DSPAM distribution kit.\n\n"); |
---|
453 | #ifdef TRUSTED_USER_SECURITY |
---|
454 | if (ATX->trusted) { |
---|
455 | #endif |
---|
456 | printf("Configuration parameters: %s\n\n", CONFIGURE_ARGS); |
---|
457 | #ifdef TRUSTED_USER_SECURITY |
---|
458 | } |
---|
459 | #endif |
---|
460 | exit (EXIT_SUCCESS); |
---|
461 | } |
---|
462 | |
---|
463 | /* Append all unknown arguments as mailer args */ |
---|
464 | |
---|
465 | if (i>0 |
---|
466 | #ifdef TRUSTED_USER_SECURITY |
---|
467 | && ATX->trusted |
---|
468 | #endif |
---|
469 | ) |
---|
470 | { |
---|
471 | if (argv[i][0] == 0) |
---|
472 | strlcat (ATX->mailer_args, "\"\"", sizeof (ATX->mailer_args)); |
---|
473 | else |
---|
474 | strlcat (ATX->mailer_args, argv[i], sizeof (ATX->mailer_args)); |
---|
475 | strlcat (ATX->mailer_args, " ", sizeof (ATX->mailer_args)); |
---|
476 | } |
---|
477 | } |
---|
478 | |
---|
479 | return 0; |
---|
480 | } |
---|
481 | |
---|
482 | |
---|
483 | /* |
---|
484 | * process_features(AGENT_CTX *, const char *) |
---|
485 | * |
---|
486 | * DESCRIPTION |
---|
487 | * convert --feature= stdin into agent context values |
---|
488 | * |
---|
489 | * INPUT ARGUMENTS |
---|
490 | * ATX agent context |
---|
491 | * in remainder of --feature= stdin |
---|
492 | * |
---|
493 | * RETURN VALUES |
---|
494 | * returns 0 on success, EINVAL when invalid options specified |
---|
495 | * |
---|
496 | */ |
---|
497 | |
---|
498 | int process_features(AGENT_CTX *ATX, const char *in) { |
---|
499 | char *ptr, *dup, *ptrptr; |
---|
500 | int ret = 0; |
---|
501 | |
---|
502 | if (!in || in[0]==0) |
---|
503 | return 0; |
---|
504 | |
---|
505 | dup = strdup(in); |
---|
506 | if (dup == NULL) { |
---|
507 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
508 | return EUNKNOWN; |
---|
509 | } |
---|
510 | |
---|
511 | ptr = strtok_r(dup, ",", &ptrptr); |
---|
512 | while(ptr != NULL) { |
---|
513 | if (!strncmp(ptr, "no",2)) |
---|
514 | ATX->flags |= DAF_NOISE; |
---|
515 | else if (!strncmp(ptr, "wh", 2)) |
---|
516 | ATX->flags |= DAF_WHITELIST; |
---|
517 | else if (!strncmp(ptr, "tb=", 3)) { |
---|
518 | ATX->training_buffer = atoi(strchr(ptr, '=')+1); |
---|
519 | |
---|
520 | if (ATX->training_buffer < 0 || ATX->training_buffer > 10) { |
---|
521 | LOG(LOG_ERR, ERR_AGENT_TB_INVALID); |
---|
522 | ret = EINVAL; |
---|
523 | } |
---|
524 | } |
---|
525 | else { |
---|
526 | LOG(LOG_ERR, ERR_AGENT_NO_SUCH_FEATURE, ptr); |
---|
527 | ret = EINVAL; |
---|
528 | } |
---|
529 | |
---|
530 | ptr = strtok_r(NULL, ",", &ptrptr); |
---|
531 | } |
---|
532 | free(dup); |
---|
533 | return ret; |
---|
534 | } |
---|
535 | |
---|
536 | /* |
---|
537 | * process_mode(AGENT_CTX *, const char *) |
---|
538 | * |
---|
539 | * DESCRIPTION |
---|
540 | * convert --mode= stdin into training mode |
---|
541 | * |
---|
542 | * INPUT ARGUMENTS |
---|
543 | * ATX agent context |
---|
544 | * mode remainder of --mode= stdin |
---|
545 | * |
---|
546 | * RETURN VALUES |
---|
547 | * returns 0 on success, EINVAL when invalid mode specified |
---|
548 | */ |
---|
549 | |
---|
550 | int process_mode(AGENT_CTX *ATX, const char *mode) { |
---|
551 | |
---|
552 | if (!mode) |
---|
553 | return EINVAL; |
---|
554 | |
---|
555 | if (!strcmp(mode, "toe")) |
---|
556 | ATX->training_mode = DST_TOE; |
---|
557 | else if (!strcmp(mode, "teft")) |
---|
558 | ATX->training_mode = DST_TEFT; |
---|
559 | else if (!strcmp(mode, "tum")) |
---|
560 | ATX->training_mode = DST_TUM; |
---|
561 | else if (!strcmp(mode, "notrain")) |
---|
562 | ATX->training_mode = DST_NOTRAIN; |
---|
563 | else if (!strcmp(mode, "unlearn")) { |
---|
564 | ATX->training_mode = DST_TEFT; |
---|
565 | ATX->flags |= DAF_UNLEARN; |
---|
566 | } else { |
---|
567 | LOG(LOG_ERR, ERR_AGENT_TR_MODE_INVALID, mode); |
---|
568 | return EINVAL; |
---|
569 | } |
---|
570 | |
---|
571 | return 0; |
---|
572 | } |
---|
573 | |
---|
574 | /* |
---|
575 | * apply_defaults(AGENT_CTX *) |
---|
576 | * |
---|
577 | * DESCRIPTION |
---|
578 | * apply default values from dspam.conf in absence of other options |
---|
579 | * |
---|
580 | * INPUT ARGUMENTS |
---|
581 | * ATX agent context |
---|
582 | * |
---|
583 | * RETURN VALUES |
---|
584 | * returns 0 on success |
---|
585 | */ |
---|
586 | |
---|
587 | int apply_defaults(AGENT_CTX *ATX) { |
---|
588 | |
---|
589 | /* Training mode */ |
---|
590 | |
---|
591 | if (!(ATX->flags & DAF_FIXED_TR_MODE)) { |
---|
592 | char *v = _ds_read_attribute(agent_config, "TrainingMode"); |
---|
593 | if (process_mode(ATX, v)) { |
---|
594 | LOG(LOG_ERR, ERR_AGENT_NO_TR_MODE); |
---|
595 | return EINVAL; |
---|
596 | } |
---|
597 | } |
---|
598 | |
---|
599 | /* Default delivery agent */ |
---|
600 | |
---|
601 | if ( ! (ATX->flags & DAF_STDOUT) |
---|
602 | && ATX->operating_mode != DSM_CLASSIFY |
---|
603 | && (ATX->flags & DAF_DELIVER_INNOCENT || ATX->flags & DAF_DELIVER_SPAM)) |
---|
604 | { |
---|
605 | char key[32]; |
---|
606 | #ifdef TRUSTED_USER_SECURITY |
---|
607 | if (!ATX->trusted) |
---|
608 | strcpy(key, "UntrustedDeliveryAgent"); |
---|
609 | else |
---|
610 | #endif |
---|
611 | strcpy(key, "TrustedDeliveryAgent"); |
---|
612 | |
---|
613 | char *value = _ds_read_attribute(agent_config, key); |
---|
614 | |
---|
615 | if (value) { |
---|
616 | char *trimmed_value = ALLTRIM(strdup(value)); |
---|
617 | if (trimmed_value && *trimmed_value == '\0') { |
---|
618 | LOG(LOG_ERR, ERR_AGENT_NO_AGENT, key); |
---|
619 | free(trimmed_value); |
---|
620 | return EINVAL; |
---|
621 | } |
---|
622 | if (trimmed_value) free(trimmed_value); |
---|
623 | char fmt[sizeof(ATX->mailer_args)]; |
---|
624 | snprintf(fmt, sizeof(fmt), "%s ", value); |
---|
625 | #ifdef TRUSTED_USER_SECURITY |
---|
626 | if (ATX->trusted) |
---|
627 | #endif |
---|
628 | strlcat(fmt, ATX->mailer_args, sizeof(fmt)); |
---|
629 | strcpy(ATX->mailer_args, fmt); |
---|
630 | } else if (!_ds_read_attribute(agent_config, "DeliveryHost")) { |
---|
631 | LOG(LOG_ERR, ERR_AGENT_NO_AGENT, key); |
---|
632 | return EINVAL; |
---|
633 | } |
---|
634 | } |
---|
635 | |
---|
636 | /* Default quarantine agent */ |
---|
637 | |
---|
638 | if (_ds_read_attribute(agent_config, "QuarantineAgent")) { |
---|
639 | snprintf(ATX->spam_args, sizeof(ATX->spam_args), "%s ", |
---|
640 | _ds_read_attribute(agent_config, "QuarantineAgent")); |
---|
641 | } else { |
---|
642 | LOGDEBUG("No QuarantineAgent option found. Using standard quarantine."); |
---|
643 | } |
---|
644 | |
---|
645 | /* Features */ |
---|
646 | |
---|
647 | if (!ATX->feature && _ds_find_attribute(agent_config, "Feature")) { |
---|
648 | attribute_t attrib = _ds_find_attribute(agent_config, "Feature"); |
---|
649 | |
---|
650 | while(attrib != NULL) { |
---|
651 | process_features(ATX, attrib->value); |
---|
652 | attrib = attrib->next; |
---|
653 | } |
---|
654 | } |
---|
655 | |
---|
656 | return 0; |
---|
657 | } |
---|
658 | |
---|
659 | /* |
---|
660 | * check_configuration(AGENT_CTX *) |
---|
661 | * |
---|
662 | * DESCRIPTION |
---|
663 | * sanity-check agent configuration |
---|
664 | * |
---|
665 | * INPUT ARGUMENTS |
---|
666 | * ATX agent context |
---|
667 | * |
---|
668 | * RETURN VALUES |
---|
669 | * returns 0 on success, EINVAL on invalid configuration |
---|
670 | */ |
---|
671 | |
---|
672 | int check_configuration(AGENT_CTX *ATX) { |
---|
673 | |
---|
674 | if (ATX->classification != DSR_NONE && ATX->operating_mode == DSM_CLASSIFY) |
---|
675 | { |
---|
676 | LOG(LOG_ERR, ERR_AGENT_CLASSIFY_CLASS); |
---|
677 | return EINVAL; |
---|
678 | } |
---|
679 | |
---|
680 | if (ATX->classification != DSR_NONE && ATX->source == DSS_NONE && |
---|
681 | !(ATX->flags & DAF_UNLEARN)) |
---|
682 | { |
---|
683 | LOG(LOG_ERR, ERR_AGENT_NO_SOURCE); |
---|
684 | return EINVAL; |
---|
685 | } |
---|
686 | |
---|
687 | if (ATX->source != DSS_NONE && ATX->classification == DSR_NONE) |
---|
688 | { |
---|
689 | LOG(LOG_ERR, ERR_AGENT_NO_CLASS); |
---|
690 | return EINVAL; |
---|
691 | } |
---|
692 | |
---|
693 | if (ATX->operating_mode == DSM_NONE) |
---|
694 | { |
---|
695 | LOG(LOG_ERR, ERR_AGENT_NO_OP_MODE); |
---|
696 | return EINVAL; |
---|
697 | } |
---|
698 | |
---|
699 | if (!_ds_match_attribute(agent_config, "ParseToHeaders", "on")) { |
---|
700 | |
---|
701 | if (ATX->users->items == 0) |
---|
702 | { |
---|
703 | LOG(LOG_ERR, ERR_AGENT_USER_UNDEFINED); |
---|
704 | return EINVAL; |
---|
705 | } |
---|
706 | } |
---|
707 | |
---|
708 | return 0; |
---|
709 | } |
---|
710 | |
---|
711 | /* |
---|
712 | * read_stdin(AGENT_CTX *) |
---|
713 | * |
---|
714 | * DESCRIPTION |
---|
715 | * read message from stdin and perform any inline configuration |
---|
716 | * (such as servicing 'ParseToHeaders' functions) |
---|
717 | * |
---|
718 | * INPUT ARGUMENTS |
---|
719 | * ATX agent context |
---|
720 | * |
---|
721 | * RETURN VALUES |
---|
722 | * buffer structure containing the message |
---|
723 | */ |
---|
724 | |
---|
725 | buffer * read_stdin(AGENT_CTX *ATX) { |
---|
726 | int body = 0, line = 1; |
---|
727 | char buf[1024]; |
---|
728 | buffer *msg; |
---|
729 | |
---|
730 | msg = buffer_create(NULL); |
---|
731 | if (msg == NULL) { |
---|
732 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
733 | return NULL; |
---|
734 | } |
---|
735 | |
---|
736 | if (_ds_match_attribute(agent_config, "DataSource", "document")) { |
---|
737 | buffer_cat(msg, ": \n\n"); |
---|
738 | body = 1; |
---|
739 | } |
---|
740 | |
---|
741 | /* Only read the message if no signature was provided on commandline */ |
---|
742 | |
---|
743 | if (ATX->signature[0] == 0) { |
---|
744 | while ((fgets (buf, sizeof (buf), stdin)) != NULL) |
---|
745 | { |
---|
746 | /* Strip CR/LFs for admittedly broken mail servers */ |
---|
747 | |
---|
748 | if (_ds_match_attribute(agent_config, "Broken", "lineStripping")) { |
---|
749 | size_t len = strlen(buf); |
---|
750 | while (len>1 && buf[len-2]==13) { |
---|
751 | buf[len-2] = buf[len-1]; |
---|
752 | buf[len-1] = 0; |
---|
753 | len--; |
---|
754 | } |
---|
755 | } |
---|
756 | |
---|
757 | /* |
---|
758 | * Don't include first line of message if it's a quarantine header added |
---|
759 | * by dspam at time of quarantine |
---|
760 | */ |
---|
761 | |
---|
762 | if (line==1 && !strncmp(buf, "From QUARANTINE", 15)) |
---|
763 | continue; |
---|
764 | |
---|
765 | /* |
---|
766 | * Parse the "To" headers and adjust the operating mode and user when |
---|
767 | * an email is sent to spam-* or notspam-* address. Behavior must be |
---|
768 | * configured in dspam.conf |
---|
769 | */ |
---|
770 | |
---|
771 | if (_ds_match_attribute(agent_config, "ParseToHeaders", "on")) { |
---|
772 | if (buf[0] == 0) |
---|
773 | body = 1; |
---|
774 | |
---|
775 | if (!body && !strncasecmp(buf, "To: ", 4)) |
---|
776 | process_parseto(ATX, buf); |
---|
777 | } |
---|
778 | |
---|
779 | if (buffer_cat (msg, buf)) |
---|
780 | { |
---|
781 | LOG (LOG_CRIT, ERR_MEM_ALLOC); |
---|
782 | goto bail; |
---|
783 | } |
---|
784 | |
---|
785 | /* |
---|
786 | * Use the original user id if we are reversing a false positive |
---|
787 | * (this is only necessary when using shared,managed groups |
---|
788 | */ |
---|
789 | |
---|
790 | if (!strncasecmp (buf, "X-DSPAM-User: ", 14) && |
---|
791 | ATX->operating_mode == DSM_PROCESS && |
---|
792 | ATX->classification == DSR_ISINNOCENT && |
---|
793 | ATX->source == DSS_ERROR) |
---|
794 | { |
---|
795 | char user[MAX_USERNAME_LENGTH]; |
---|
796 | strlcpy (user, buf + 14, sizeof (user)); |
---|
797 | chomp (user); |
---|
798 | nt_destroy (ATX->users); |
---|
799 | ATX->users = nt_create (NT_CHAR); |
---|
800 | if (ATX->users == NULL) { |
---|
801 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
802 | goto bail; |
---|
803 | } |
---|
804 | LOGDEBUG("found username %s in X-DSPAM-User header", user); |
---|
805 | nt_add (ATX->users, user); |
---|
806 | } |
---|
807 | |
---|
808 | line++; |
---|
809 | } |
---|
810 | } |
---|
811 | |
---|
812 | if (!msg->used) |
---|
813 | { |
---|
814 | if (ATX->signature[0] != 0) { |
---|
815 | buffer_cat(msg, "\n\n"); |
---|
816 | } |
---|
817 | else { |
---|
818 | LOG (LOG_INFO, "empty message (no data received)"); |
---|
819 | goto bail; |
---|
820 | } |
---|
821 | } |
---|
822 | |
---|
823 | return msg; |
---|
824 | |
---|
825 | bail: |
---|
826 | LOGDEBUG("read_stdin() failure"); |
---|
827 | buffer_destroy(msg); |
---|
828 | return NULL; |
---|
829 | } |
---|
830 | |
---|
831 | /* |
---|
832 | * process_parseto(AGENT_CTX *, const char *) |
---|
833 | * |
---|
834 | * DESCRIPTION |
---|
835 | * processes the To: line of a message to provide parseto services |
---|
836 | * |
---|
837 | * INPUT ARGUMENTS |
---|
838 | * ATX agent context |
---|
839 | * buf To: line |
---|
840 | * |
---|
841 | * RETURN VALUES |
---|
842 | * returns 0 on success |
---|
843 | */ |
---|
844 | |
---|
845 | int process_parseto(AGENT_CTX *ATX, const char *buf) { |
---|
846 | char *y = NULL; |
---|
847 | char *x; |
---|
848 | char *h = NULL; |
---|
849 | char *buffer; |
---|
850 | char *ptrptr; |
---|
851 | |
---|
852 | if (!buf || strncmp(buf+2,":",1) != 0) |
---|
853 | return EINVAL; |
---|
854 | |
---|
855 | buffer = strdup (buf+3); |
---|
856 | h = strtok_r (buffer, "\n", &ptrptr); |
---|
857 | while (h != NULL) { |
---|
858 | /* check for spam alias */ |
---|
859 | x = strstr(h, "<spam-"); |
---|
860 | if (!x) x = strstr(h, " spam-"); |
---|
861 | if (!x) x = strstr(h, "\tspam-"); |
---|
862 | if (!x) x = strstr(h, ",spam-"); |
---|
863 | if (!x) x = strstr(h, ":spam-"); |
---|
864 | if (x != NULL) { |
---|
865 | y = strdup(x+6); |
---|
866 | if (_ds_match_attribute(agent_config, "ChangeModeOnParse", "on")) { |
---|
867 | ATX->classification = DSR_ISSPAM; |
---|
868 | ATX->source = DSS_ERROR; |
---|
869 | } |
---|
870 | } else { |
---|
871 | /* check for nonspam alias */ |
---|
872 | x = strstr(h, "<notspam-"); |
---|
873 | if (!x) x = strstr(h, " notspam-"); |
---|
874 | if (!x) x = strstr(h, "\tnotspam-"); |
---|
875 | if (!x) x = strstr(h, ",notspam-"); |
---|
876 | if (!x) x = strstr(h, ":notspam-"); |
---|
877 | if (x && strlen(x) >= 9) { |
---|
878 | y = strdup(x+9); |
---|
879 | if (_ds_match_attribute(agent_config, "ChangeModeOnParse", "on")) { |
---|
880 | ATX->classification = DSR_ISINNOCENT; |
---|
881 | ATX->source = DSS_ERROR; |
---|
882 | } |
---|
883 | } |
---|
884 | } |
---|
885 | /* do not continue if we found a spam/nonspam alias */ |
---|
886 | if (y) break; |
---|
887 | |
---|
888 | /* get next line from 'To' header */ |
---|
889 | h = strtok_r (NULL, "\n", &ptrptr); |
---|
890 | if (h && h[0] != 32 && h[0] != 9) { |
---|
891 | /* we are not any more in the 'To' header */ |
---|
892 | break; |
---|
893 | } |
---|
894 | } |
---|
895 | |
---|
896 | free (buffer); |
---|
897 | |
---|
898 | if (y && (_ds_match_attribute(agent_config, |
---|
899 | "ChangeUserOnParse", "on") || |
---|
900 | _ds_match_attribute(agent_config, |
---|
901 | "ChangeUserOnParse", "full") || |
---|
902 | _ds_match_attribute(agent_config, |
---|
903 | "ChangeUserOnParse", "user"))) |
---|
904 | { |
---|
905 | char *z; |
---|
906 | |
---|
907 | if (_ds_match_attribute(agent_config, |
---|
908 | "ChangeUserOnParse", "full")) |
---|
909 | { |
---|
910 | z = strtok_r(y, ">, \t\r\n", &ptrptr); |
---|
911 | } else { |
---|
912 | if (strstr(x, "@")) |
---|
913 | z = strtok_r(y, "@", &ptrptr); |
---|
914 | else |
---|
915 | z = NULL; |
---|
916 | } |
---|
917 | |
---|
918 | if (z) { |
---|
919 | nt_destroy(ATX->users); |
---|
920 | ATX->users = nt_create(NT_CHAR); |
---|
921 | if (!ATX->users) { |
---|
922 | LOG(LOG_CRIT, ERR_MEM_ALLOC); |
---|
923 | return EUNKNOWN; |
---|
924 | } |
---|
925 | nt_add (ATX->users, z); |
---|
926 | } |
---|
927 | } |
---|
928 | |
---|
929 | if (y) free(y); |
---|
930 | return 0; |
---|
931 | } |
---|
932 | |
---|
933 | int |
---|
934 | init_pwent_cache(void) |
---|
935 | { |
---|
936 | struct passwd *pwent; |
---|
937 | pwent = getpwuid(getuid()); |
---|
938 | if (pwent == NULL) { |
---|
939 | return 0; |
---|
940 | } |
---|
941 | else { |
---|
942 | __pw_name = strdup(pwent->pw_name); |
---|
943 | __pw_uid = pwent->pw_uid; |
---|
944 | } |
---|
945 | return 1; |
---|
946 | } |
---|