new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_maild / sendmail.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 /* Basic e-mailing operations */
11
12 #include "shared.h"
13 #include "os_net/os_net.h"
14 #include "maild.h"
15 #include "mail_list.h"
16
17 /* Return codes (from SMTP server) */
18 #define VALIDBANNER     "220"
19 #define VALIDMAIL       "250"
20 #define VALIDDATA       "354"
21
22 /* Default values used to connect */
23 #define SMTP_DEFAULT_PORT   "25"
24 #define MAILFROM            "Mail From: <%s>\r\n"
25 #define RCPTTO              "Rcpt To: <%s>\r\n"
26 #define DATAMSG             "DATA\r\n"
27 #define FROM                "From: OSSEC HIDS <%s>\r\n"
28 #define TO                  "To: <%s>\r\n"
29 #define REPLYTO             "Reply-To: OSSEC HIDS <%s>\r\n"
30 /*#define CC                "Cc: <%s>\r\n"*/
31 #define SUBJECT             "Subject: %s\r\n"
32 #define ENDHEADER           "\r\n"
33 #define ENDDATA             "\r\n.\r\n"
34 #define QUITMSG             "QUIT\r\n"
35 #define XHEADER             "X-IDS-OSSEC: %s\r\n"
36
37 /* Error messages - Can be translated */
38 #define INTERNAL_ERROR  "os_maild (1760): ERROR: Memory/configuration error"
39 #define BANNER_ERROR    "os_sendmail(1762): WARN: Banner not received from server"
40 #define HELO_ERROR      "os_sendmail(1763): WARN: Hello not accepted by server"
41 #define FROM_ERROR      "os_sendmail(1764): WARN: Mail from not accepted by server"
42 #define TO_ERROR        "os_sendmail(1765): WARN: RCPT TO not accepted by server - '%s'."
43 #define DATA_ERROR      "os_sendmail(1766): WARN: DATA not accepted by server"
44 #define END_DATA_ERROR  "os_sendmail(1767): WARN: End of DATA not accepted by server"
45
46 #define MAIL_DEBUG_FLAG     0
47 #define MAIL_DEBUG(x,y,z) if(MAIL_DEBUG_FLAG) merror(x,y,z)
48
49
50 int OS_Sendmail(MailConfig *mail, struct tm *p)
51 {
52     FILE *sendmail = NULL;
53     int socket = -1;
54     unsigned int i = 0;
55     char *msg;
56     char snd_msg[128];
57
58     MailNode *mailmsg;
59
60     /* If there is no sms message, attempt to get from the email list */
61     mailmsg = OS_PopLastMail();
62
63     if (mailmsg == NULL) {
64         merror("%s: No email to be sent. Inconsistent state.", ARGV0);
65         return (OS_INVALID);
66     }
67
68     if (mail->smtpserver[0] == '/') {
69         sendmail = popen(mail->smtpserver, "w");
70         if (!sendmail) {
71             return (OS_INVALID);
72         }
73     } else {
74         /* Connect to the SMTP server */
75         socket = OS_ConnectTCP(SMTP_DEFAULT_PORT, mail->smtpserver);
76         if (socket < 0) {
77             return (socket);
78         }
79
80         /* Receive the banner */
81         msg = OS_RecvTCP(socket, OS_SIZE_1024);
82         if ((msg == NULL) || (!OS_Match(VALIDBANNER, msg))) {
83             merror(BANNER_ERROR);
84             if (msg) {
85                 free(msg);
86             }
87             close(socket);
88             return (OS_INVALID);
89         }
90         MAIL_DEBUG("DEBUG: Received banner: '%s' %s", msg, "");
91         free(msg);
92
93         /* Send HELO message */
94         memset(snd_msg, '\0', 128);
95         if (mail->heloserver) {
96             snprintf(snd_msg, 127, "Helo %s\r\n", mail->heloserver);
97         } else {
98             snprintf(snd_msg, 127, "Helo %s\r\n", "notify.ossec.net");
99         }
100         OS_SendTCP(socket, snd_msg);
101         msg = OS_RecvTCP(socket, OS_SIZE_1024);
102         if ((msg == NULL) || (!OS_Match(VALIDMAIL, msg))) {
103             if (msg) {
104                 /* In some cases (with virus scans in the middle)
105                  * we may get two banners. Check for that in here.
106                  */
107                 if (OS_Match(VALIDBANNER, msg)) {
108                     free(msg);
109
110                     /* Try again */
111                     msg = OS_RecvTCP(socket, OS_SIZE_1024);
112                     if ((msg == NULL) || (!OS_Match(VALIDMAIL, msg))) {
113                         merror("%s:%s", HELO_ERROR, msg != NULL ? msg : "null");
114                         if (msg) {
115                             free(msg);
116                         }
117                         close(socket);
118                         return (OS_INVALID);
119                     }
120                 } else {
121                     merror("%s:%s", HELO_ERROR, msg);
122                     free(msg);
123                     close(socket);
124                     return (OS_INVALID);
125                 }
126             } else {
127                 merror("%s:%s", HELO_ERROR, "null");
128                 close(socket);
129                 return (OS_INVALID);
130             }
131         }
132
133         MAIL_DEBUG("DEBUG: Sent '%s', received: '%s'", snd_msg, msg);
134         free(msg);
135
136         /* Build "Mail from" msg */
137         memset(snd_msg, '\0', 128);
138         snprintf(snd_msg, 127, MAILFROM, mail->from);
139         OS_SendTCP(socket, snd_msg);
140         msg = OS_RecvTCP(socket, OS_SIZE_1024);
141         if ((msg == NULL) || (!OS_Match(VALIDMAIL, msg))) {
142             merror(FROM_ERROR);
143             if (msg) {
144                 free(msg);
145             }
146             close(socket);
147             return (OS_INVALID);
148         }
149         MAIL_DEBUG("DEBUG: Sent '%s', received: '%s'", snd_msg, msg);
150         free(msg);
151
152         /* Build "RCPT TO" msg */
153         while (1) {
154             if (mail->to[i] == NULL) {
155                 if (i == 0) {
156                     merror(INTERNAL_ERROR);
157                     close(socket);
158                     return (OS_INVALID);
159                 }
160                 break;
161             }
162             memset(snd_msg, '\0', 128);
163             snprintf(snd_msg, 127, RCPTTO, mail->to[i++]);
164             OS_SendTCP(socket, snd_msg);
165             msg = OS_RecvTCP(socket, OS_SIZE_1024);
166             if ((msg == NULL) || (!OS_Match(VALIDMAIL, msg))) {
167                 merror(TO_ERROR, mail->to[i - 1]);
168                 if (msg) {
169                     free(msg);
170                 }
171                 close(socket);
172                 return (OS_INVALID);
173             }
174             MAIL_DEBUG("DEBUG: Sent '%s', received: '%s'", snd_msg, msg);
175             free(msg);
176         }
177
178         /* Additional RCPT to */
179         if (mail->gran_to) {
180             i = 0;
181             while (mail->gran_to[i] != NULL) {
182                 if (mail->gran_set[i] != FULL_FORMAT) {
183                     i++;
184                     continue;
185                 }
186
187                 memset(snd_msg, '\0', 128);
188                 snprintf(snd_msg, 127, RCPTTO, mail->gran_to[i]);
189                 OS_SendTCP(socket, snd_msg);
190                 msg = OS_RecvTCP(socket, OS_SIZE_1024);
191                 if ((msg == NULL) || (!OS_Match(VALIDMAIL, msg))) {
192                     merror(TO_ERROR, mail->gran_to[i]);
193                     if (msg) {
194                         free(msg);
195                     }
196
197                     i++;
198                     continue;
199                 }
200
201                 MAIL_DEBUG("DEBUG: Sent '%s', received: '%s'", snd_msg, msg);
202                 free(msg);
203                 i++;
204                 continue;
205             }
206         }
207
208         /* Send the "DATA" msg */
209         OS_SendTCP(socket, DATAMSG);
210         msg = OS_RecvTCP(socket, OS_SIZE_1024);
211         if ((msg == NULL) || (!OS_Match(VALIDDATA, msg))) {
212             merror(DATA_ERROR);
213             if (msg) {
214                 free(msg);
215             }
216             close(socket);
217             return (OS_INVALID);
218         }
219         MAIL_DEBUG("DEBUG: Sent '%s', received: '%s'", DATAMSG, msg);
220         free(msg);
221     }
222
223     /* Building "From" and "To" in the e-mail header */
224     memset(snd_msg, '\0', 128);
225     snprintf(snd_msg, 127, TO, mail->to[0]);
226
227     if (sendmail) {
228         fprintf(sendmail, "%s", snd_msg);
229     } else {
230         OS_SendTCP(socket, snd_msg);
231     }
232
233     memset(snd_msg, '\0', 128);
234     snprintf(snd_msg, 127, FROM, mail->from);
235
236     if (sendmail) {
237         fprintf(sendmail, "%s", snd_msg);
238     } else {
239         OS_SendTCP(socket, snd_msg);
240     }
241
242     /* Send reply-to if set */
243     if (mail->reply_to){
244         memset(snd_msg, '\0', 128);
245         snprintf(snd_msg, 127, REPLYTO, mail->reply_to);
246         if (sendmail) {
247             fprintf(sendmail, "%s", snd_msg);
248         } else {
249             OS_SendTCP(socket, snd_msg);
250         }
251     }
252
253     /* Add CCs */
254     if (mail->to[1]) {
255         i = 1;
256         while (1) {
257             if (mail->to[i] == NULL) {
258                 break;
259             }
260
261             memset(snd_msg, '\0', 128);
262             snprintf(snd_msg, 127, TO, mail->to[i]);
263
264             if (sendmail) {
265                 fprintf(sendmail, "%s", snd_msg);
266             } else {
267                 OS_SendTCP(socket, snd_msg);
268             }
269
270             i++;
271         }
272     }
273
274     /* More CCs - from granular options */
275     if (mail->gran_to) {
276         i = 0;
277         while (mail->gran_to[i] != NULL) {
278             if (mail->gran_set[i] != FULL_FORMAT) {
279                 i++;
280                 continue;
281             }
282
283             memset(snd_msg, '\0', 128);
284             snprintf(snd_msg, 127, TO, mail->gran_to[i]);
285
286             if (sendmail) {
287                 fprintf(sendmail, "%s", snd_msg);
288             } else {
289                 OS_SendTCP(socket, snd_msg);
290             }
291
292             i++;
293             continue;
294         }
295     }
296
297     /* Send date */
298     memset(snd_msg, '\0', 128);
299
300     /* Solaris doesn't have the "%z", so we set the timezone to 0 */
301 #ifdef SOLARIS
302     strftime(snd_msg, 127, "Date: %a, %d %b %Y %T -0000\r\n", p);
303 #else
304     strftime(snd_msg, 127, "Date: %a, %d %b %Y %T %z\r\n", p);
305 #endif
306
307     if (sendmail) {
308         fprintf(sendmail, "%s", snd_msg);
309     } else {
310         OS_SendTCP(socket, snd_msg);
311     }
312
313     if (mail->idsname) {
314         /* Send server name header */
315         memset(snd_msg, '\0', 128);
316         snprintf(snd_msg, 127, XHEADER, mail->idsname);
317
318         if (sendmail) {
319             fprintf(sendmail, "%s", snd_msg);
320         } else {
321             OS_SendTCP(socket, snd_msg);
322         }
323     }
324
325     /* Send subject */
326     memset(snd_msg, '\0', 128);
327
328     /* Check if global subject is available */
329     if ((_g_subject_level != 0) && (_g_subject[0] != '\0')) {
330         snprintf(snd_msg, 127, SUBJECT, _g_subject);
331
332         /* Clear global values */
333         _g_subject[0] = '\0';
334         _g_subject_level = 0;
335     } else {
336         snprintf(snd_msg, 127, SUBJECT, mailmsg->mail->subject);
337     }
338
339     if (sendmail) {
340         fprintf(sendmail, "%s", snd_msg);
341         fprintf(sendmail, ENDHEADER);
342     } else {
343         OS_SendTCP(socket, snd_msg);
344         OS_SendTCP(socket, ENDHEADER);
345     }
346
347     /* Send body */
348
349     /* Send multiple emails together if we have to */
350     do {
351         if (sendmail) {
352             fprintf(sendmail, "%s", mailmsg->mail->body);
353         } else {
354             OS_SendTCP(socket, mailmsg->mail->body);
355         }
356         mailmsg = OS_PopLastMail();
357     } while (mailmsg);
358
359     if (sendmail) {
360         if (pclose(sendmail) == -1) {
361             merror(WAITPID_ERROR, ARGV0, errno, strerror(errno));
362         }
363     } else {
364         /* Send end of data \r\n.\r\n */
365         OS_SendTCP(socket, ENDDATA);
366         msg = OS_RecvTCP(socket, OS_SIZE_1024);
367         if (mail->strict_checking && ((msg == NULL) || (!OS_Match(VALIDMAIL, msg)))) {
368             merror(END_DATA_ERROR);
369             if (msg) {
370                 free(msg);
371             }
372             close(socket);
373             return (OS_INVALID);
374         }
375
376         /* Check msg, since it may be null */
377         if (msg) {
378             free(msg);
379         }
380
381         /* Quit and close socket */
382         OS_SendTCP(socket, QUITMSG);
383         msg = OS_RecvTCP(socket, OS_SIZE_1024);
384
385         if (msg) {
386             free(msg);
387         }
388
389         close(socket);
390     }
391
392     memset_secure(snd_msg, '\0', 128);
393     return (0);
394 }