new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_maild / maild.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation.
8  */
9
10 #include "shared.h"
11 #include "maild.h"
12 #include "mail_list.h"
13
14 #ifndef ARGV0
15 #define ARGV0 "ossec-maild"
16 #endif
17
18 /* Global variables */
19 unsigned int mail_timeout;
20 unsigned int   _g_subject_level;
21 char _g_subject[SUBJECT_SIZE + 2];
22
23 /* Prototypes */
24 static void OS_Run(MailConfig *mail) __attribute__((nonnull)) __attribute__((noreturn));
25 static void help_maild(void) __attribute__((noreturn));
26
27
28 /* Print help statement */
29 static void help_maild()
30 {
31     print_header();
32     print_out("  %s: -[Vhdtf] [-u user] [-g group] [-c config] [-D dir]", ARGV0);
33     print_out("    -V          Version and license message");
34     print_out("    -h          This help message");
35     print_out("    -d          Execute in debug mode. This parameter");
36     print_out("                can be specified multiple times");
37     print_out("                to increase the debug level.");
38     print_out("    -t          Test configuration");
39     print_out("    -f          Run in foreground");
40     print_out("    -u <user>   User to run as (default: %s)", MAILUSER);
41     print_out("    -g <group>  Group to run as (default: %s)", GROUPGLOBAL);
42     print_out("    -c <config> Configuration file to use (default: %s)", DEFAULTCPATH);
43     print_out("    -D <dir>    Directory to chroot into (default: %s)", DEFAULTDIR);
44     print_out(" ");
45     exit(1);
46 }
47
48 int main(int argc, char **argv)
49 {
50     int c, test_config = 0, run_foreground = 0;
51     uid_t uid;
52     gid_t gid;
53     const char *dir  = DEFAULTDIR;
54     const char *user = MAILUSER;
55     const char *group = GROUPGLOBAL;
56     const char *cfg = DEFAULTCPATH;
57
58     /* Mail Structure */
59     MailConfig mail;
60
61     /* Set the name */
62     OS_SetName(ARGV0);
63
64     while ((c = getopt(argc, argv, "Vdhtfu:g:D:c:")) != -1) {
65         switch (c) {
66             case 'V':
67                 print_version();
68                 break;
69             case 'h':
70                 help_maild();
71                 break;
72             case 'd':
73                 nowDebug();
74                 break;
75             case 'f':
76                 run_foreground = 1;
77                 break;
78             case 'u':
79                 if (!optarg) {
80                     ErrorExit("%s: -u needs an argument", ARGV0);
81                 }
82                 user = optarg;
83                 break;
84             case 'g':
85                 if (!optarg) {
86                     ErrorExit("%s: -g needs an argument", ARGV0);
87                 }
88                 group = optarg;
89                 break;
90             case 'D':
91                 if (!optarg) {
92                     ErrorExit("%s: -D needs an argument", ARGV0);
93                 }
94                 dir = optarg;
95                 break;
96             case 'c':
97                 if (!optarg) {
98                     ErrorExit("%s: -c needs an argument", ARGV0);
99                 }
100                 cfg = optarg;
101                 break;
102             case 't':
103                 test_config = 1;
104                 break;
105             default:
106                 help_maild();
107                 break;
108         }
109     }
110
111     /* Start daemon */
112     debug1(STARTED_MSG, ARGV0);
113
114     /* Check if the user/group given are valid */
115     uid = Privsep_GetUser(user);
116     gid = Privsep_GetGroup(group);
117     if (uid == (uid_t) - 1 || gid == (gid_t) - 1) {
118         ErrorExit(USER_ERROR, ARGV0, user, group);
119     }
120
121     /* Read configuration */
122     if (MailConf(test_config, cfg, &mail) < 0) {
123         ErrorExit(CONFIG_ERROR, ARGV0, cfg);
124     }
125
126     /* Read internal options */
127     mail.strict_checking = getDefine_Int("maild",
128                                          "strict_checking",
129                                          0, 1);
130
131     /* Get groupping */
132     mail.groupping = getDefine_Int("maild",
133                                    "groupping",
134                                    0, 1);
135
136     /* Get subject type */
137     mail.subject_full = getDefine_Int("maild",
138                                       "full_subject",
139                                       0, 1);
140
141 #ifdef LIBGEOIP_ENABLED
142     /* Get GeoIP */
143     mail.geoip = getDefine_Int("maild",
144                                "geoip",
145                                0, 1);
146 #endif
147
148     /* Exit here if test config is set */
149     if (test_config) {
150         exit(0);
151     }
152
153     if (!run_foreground) {
154         nowDaemon();
155         goDaemon();
156     }
157
158     /* Privilege separation */
159     if (Privsep_SetGroup(gid) < 0) {
160         ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
161     }
162
163     if (mail.smtpserver[0] != '/') {
164         /* chroot */
165         if (Privsep_Chroot(dir) < 0) {
166             ErrorExit(CHROOT_ERROR, ARGV0, dir, errno, strerror(errno));
167         }
168         nowChroot();
169         debug1(CHROOT_MSG, ARGV0, dir);
170     }
171
172     /* Change user */
173     if (Privsep_SetUser(uid) < 0) {
174         ErrorExit(SETUID_ERROR, ARGV0, user, errno, strerror(errno));
175     }
176
177     debug1(PRIVSEP_MSG, ARGV0, user);
178
179     /* Signal manipulation */
180     StartSIG(ARGV0);
181
182     /* Create PID files */
183     if (CreatePID(ARGV0, getpid()) < 0) {
184         ErrorExit(PID_ERROR, ARGV0);
185     }
186
187     /* Start up message */
188     verbose(STARTUP_MSG, ARGV0, (int)getpid());
189
190     /* The real daemon now */
191     OS_Run(&mail);
192 }
193
194 /* Read the queue and send the appropriate alerts
195  * Not supposed to return
196  */
197 static void OS_Run(MailConfig *mail)
198 {
199     MailMsg *msg;
200     MailMsg *s_msg = NULL;
201     MailMsg *msg_sms = NULL;
202
203     time_t tm;
204     struct tm *p;
205
206     int i = 0;
207     int mailtosend = 0;
208     int childcount = 0;
209     int thishour = 0;
210
211     int n_errs = 0;
212
213     file_queue *fileq;
214
215     /* Get current time before starting */
216     tm = time(NULL);
217     p = localtime(&tm);
218     thishour = p->tm_hour;
219
220     /* Initialize file queue */
221     i = 0;
222     i |= CRALERT_MAIL_SET;
223     os_calloc(1, sizeof(file_queue), fileq);
224     Init_FileQueue(fileq, p, i);
225
226     /* Create the list */
227     OS_CreateMailList(MAIL_LIST_SIZE);
228
229     /* Set default timeout */
230     mail_timeout = DEFAULT_TIMEOUT;
231
232     /* Clear global variables */
233     _g_subject_level = 0;
234     memset(_g_subject, '\0', SUBJECT_SIZE + 2);
235
236     while (1) {
237         tm = time(NULL);
238         p = localtime(&tm);
239
240
241         /* If mail_timeout == NEXTMAIL_TIMEOUT, we will try to get
242          * more messages, before sending anything
243          */
244         if ((mail_timeout == NEXTMAIL_TIMEOUT) && (p->tm_hour == thishour)) {
245             /* Get more messages */
246         }
247
248         /* Hour changed: send all suppressed mails */
249         else if (((mailtosend < mail->maxperhour) && (mailtosend != 0)) ||
250                  ((p->tm_hour != thishour) && (childcount < MAXCHILDPROCESS))) {
251             MailNode *mailmsg;
252             pid_t pid;
253
254             /* Check if we have anything to send */
255             mailmsg = OS_CheckLastMail();
256             if (mailmsg == NULL) {
257                 /* Don't fork in here */
258                 goto snd_check_hour;
259             }
260
261             fflush(fileq->fp);
262             pid = fork();
263             if (pid < 0) {
264                 merror(FORK_ERROR, ARGV0, errno, strerror(errno));
265                 sleep(30);
266                 continue;
267             } else if (pid == 0) {
268                 if (OS_Sendmail(mail, p) < 0) {
269                     merror(SNDMAIL_ERROR, ARGV0, mail->smtpserver);
270                 }
271
272                 exit(0);
273             }
274
275             /* Clean the memory */
276             mailmsg = OS_PopLastMail();
277             do {
278                 FreeMail(mailmsg);
279                 mailmsg = OS_PopLastMail();
280             } while (mailmsg);
281
282             /* Increase child count */
283             childcount++;
284
285             /* Clear global variables */
286             _g_subject[0] = '\0';
287             _g_subject[SUBJECT_SIZE - 1] = '\0';
288             _g_subject_level = 0;
289
290             /* Clean up set values */
291             if (mail->gran_to) {
292                 i = 0;
293                 while (mail->gran_to[i] != NULL) {
294                     if (s_msg && mail->gran_set[i] == DONOTGROUP) {
295                         mail->gran_set[i] = FULL_FORMAT;
296                     } else {
297                         mail->gran_set[i] = 0;
298                     }
299                     i++;
300                 }
301             }
302
303 snd_check_hour:
304             /* If we sent everything */
305             if (p->tm_hour != thishour) {
306                 thishour = p->tm_hour;
307
308                 mailtosend = 0;
309             }
310         }
311
312         /* Saved message for the do_not_group option */
313         if (s_msg) {
314             /* Set the remaining do no group to full format */
315             if (mail->gran_to) {
316                 i = 0;
317                 while (mail->gran_to[i] != NULL) {
318                     if (mail->gran_set[i] == DONOTGROUP) {
319                         mail->gran_set[i] = FULL_FORMAT;
320                     }
321                     i++;
322                 }
323             }
324
325             OS_AddMailtoList(s_msg);
326
327             s_msg = NULL;
328             mailtosend++;
329             continue;
330         }
331
332         /* Receive message from queue */
333         if ((msg = OS_RecvMailQ(fileq, p, mail, &msg_sms)) != NULL) {
334             /* If the e-mail priority is do_not_group,
335              * flush all previous entries and then send it.
336              * Use s_msg to hold the pointer to the message while we flush it.
337              */
338             if (mail->priority == DONOTGROUP) {
339                 s_msg = msg;
340             } else {
341                 OS_AddMailtoList(msg);
342             }
343
344             /* Change timeout to see if any new message is coming shortly */
345             if (mail->groupping) {
346                 /* If priority is set, send email now */
347                 if (mail->priority) {
348                     mail_timeout = DEFAULT_TIMEOUT;
349
350                     /* If do_not_group is set, we do not increase the list count */
351                     if (mail->priority != DONOTGROUP) {
352                         mailtosend++;
353                     }
354                 } else {
355                     /* 5 seconds only */
356                     mail_timeout = NEXTMAIL_TIMEOUT;
357                 }
358             } else {
359                 /* Send message by itself */
360                 mailtosend++;
361             }
362         } else {
363             if (mail_timeout == NEXTMAIL_TIMEOUT) {
364                 mailtosend++;
365
366                 /* Default timeout */
367                 mail_timeout = DEFAULT_TIMEOUT;
368             }
369         }
370
371         /* Wait for the children */
372         while (childcount) {
373             int wp;
374             int p_status;
375             wp = waitpid((pid_t) - 1, &p_status, WNOHANG);
376             if (wp < 0) {
377                 merror(WAITPID_ERROR, ARGV0, errno, strerror(errno));
378                 n_errs++;
379             }
380
381             /* if = 0, we still need to wait for the child process */
382             else if (wp == 0) {
383                 break;
384             } else {
385                 if (p_status != 0) {
386                     merror(CHLDWAIT_ERROR, ARGV0, p_status);
387                     merror(SNDMAIL_ERROR, ARGV0, mail->smtpserver);
388                     n_errs++;
389                 }
390                 childcount--;
391             }
392
393             /* Too many errors */
394             if (n_errs > 6) {
395                 merror(TOOMANY_WAIT_ERROR, ARGV0);
396                 merror(SNDMAIL_ERROR, ARGV0, mail->smtpserver);
397                 exit(1);
398             }
399         }
400
401     }
402 }
403