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