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
initialize_atx(AGENT_CTX * ATX)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
process_arguments(AGENT_CTX * ATX,int argc,char ** argv)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
process_features(AGENT_CTX * ATX,const char * in)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
process_mode(AGENT_CTX * ATX,const char * mode)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
apply_defaults(AGENT_CTX * ATX)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
check_configuration(AGENT_CTX * ATX)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
read_stdin(AGENT_CTX * ATX)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
process_parseto(AGENT_CTX * ATX,const char * buf)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
init_pwent_cache(void)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 }
947