1 /* $Id: ssp_email.c,v 2.12 2008/04/26 19:50:56 fknobbe Exp $
2  *
3  *
4  * Copyright (c) 2002-2008 Frank Knobbe <frank@knobbe.us>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  *
29  * ssp_email.c
30  *
31  * Purpose:
32  *
33  * This SnortSam plugin sends an email with a notification of the block/unblock.
34  * It connects to a specified mail server and sends to a specified recipient via
35  * standard SMTP (not ESMTP) commands.
36  *
37  * AUTHENTICATION (AUTH) AND/OR ENCRYPTION (OVER SSL) IS NOT SUPPORTED.
38  * If you need to authenticate to your mail server, or prefer SMTP over SSL,
39  * please use a program like swatch to check for changes in the log file and
40  * have a third party mailer send them. (This is recommended for performance
41  * reasons anyway).
42  *
43  *
44  */
45 
46 
47 #ifndef		__SSP_EMAIL_C__
48 #define		__SSP_EMAIL_C__
49 
50 
51 #include "snortsam.h"
52 #include "ssp_email.h"
53 
54 #include <sys/types.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <time.h>
58 #ifdef WIN32
59 #include <winsock.h>
60 #else
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #endif
64 
65 
66 extern unsigned int disablereverselookups;
67 
68 
69 /* This routine parses the email statements in the config file
70  * and builds a list of email servers/recipients.
71 */
EmailParse(char * val,char * file,unsigned long line,DATALIST * plugindatalist)72 void EmailParse(char *val,char *file,unsigned long line,DATALIST *plugindatalist)
73 {	EMAILDATA *emailp;
74 	char *p2,msg[STRBUFSIZE+2],*port=NULL;
75 	struct in_addr emailip;
76 
77 #ifdef FWSAMDEBUG
78 	printf("Debug: [email] Plugin Parsing...\n");
79 #endif
80 
81 	if(*val)
82 	{	p2=val;
83 		while(*p2 && !myisspace(*p2) && *p2!=':')
84 			p2++;
85 		if(*p2==':')
86 		{	*p2++ =0;
87 			while(*p2==':' || myisspace(*p2))
88 				p2++;
89 			port=p2;
90 			while(*p2 && !myisspace(*p2))
91 				p2++;
92 		}
93 		*p2++ =0;
94 
95 		emailip.s_addr=getip(val);
96 		if(emailip.s_addr)			/* If we have a valid IP address */
97 		{	emailp=safemalloc(sizeof(EMAILDATA),"EmailParse","emailp");	/* create new email struct */
98 			plugindatalist->data=emailp;
99 			emailp->ip.s_addr=emailip.s_addr;
100 			emailp->recipient[0]=emailp->sender[0]=0;
101 			emailp->mailsocket=0;
102 			emailp->loggedin=FALSE;
103 			emailp->port=25;
104 			if(port)
105 			{	if(atoi(port)>0)
106 					emailp->port=atoi(port);
107 			}
108 
109 			if(*p2)
110 			{	val=p2;
111 				while(*val && myisspace(*val))	/* now parse the remaining text */
112 					val++;
113 				if(val)
114 				{	p2=val;
115 					while(*p2 && !myisspace(*p2))
116 						p2++;
117 
118 					if(*p2)
119 					{	*p2++ =0;
120 						safecopy(emailp->recipient,val);	/* save recipient */
121 
122 						val=p2;
123 						while(*val && myisspace(*val))	/* has a sender name been specified? */
124 							val++;
125 						if(val)
126 						{	p2=val;
127 							while(*p2 && !myisspace(*p2))
128 								p2++;
129 							*p2=0;
130 							safecopy(emailp->sender,val);	/* save sender */
131 						}
132 					}
133 					else
134 					{	*p2=0;
135 						safecopy(emailp->recipient,val);	/* save recipient */
136 					}
137 				}
138 			}
139 			if(!emailp->recipient[0])
140 			{	snprintf(msg,sizeof(msg)-1,"Error: [%s: %lu] EMAIL defined without recipient!",file,line);
141 				logmessage(1,msg,"email",0);
142 				free(emailp);
143 				plugindatalist->data=NULL;
144 			}
145 		}
146 		else
147 		{	snprintf(msg,sizeof(msg)-1,"Error: [%s: %lu] Invalid EMAIL server '%s' ignored.",file,line,val);
148 			logmessage(1,msg,"email",0);
149 		}
150 	}
151 	else
152 	{	snprintf(msg,sizeof(msg)-1,"Error: [%s: %lu] Empty EMAIL parameter.",file,line);
153 		logmessage(1,msg,"email",0);
154 	}
155 }
156 
157 
158 /* This routine sends the email only on block events, not unblock
159  */
EmailSendBlockOnly(BLOCKINFO * bd,void * data,unsigned long qp)160 void EmailSendBlockOnly(BLOCKINFO *bd,void *data,unsigned long qp)
161 { 	if(bd->block)
162 		EmailSend(bd,data,qp);
163 }
164 
165 /* This routine sends the email
166  */
EmailSend(BLOCKINFO * bd,void * data,unsigned long qp)167 void EmailSend(BLOCKINFO *bd,void *data,unsigned long qp)
168 {   EMAILDATA *emailp;
169 	struct sockaddr_in thissocketaddr,emailsocketaddr;
170 	unsigned long ll;
171 	char emailmsg[4000],serverat[STRBUFSIZE+2],edate[42],msg[STRBUFSIZE+2],msg2[STRBUFSIZE+2],host[STRBUFSIZE+2];
172 	struct tm *timep;
173 	signed int timediff,gth;
174 	struct protoent *protoe;
175 	time_t	notetime;
176 #ifdef FWSAMDEBUG
177 #ifdef WIN32
178 	unsigned long threadid=GetCurrentThreadId();
179 #else
180 	pthread_t threadid=pthread_self();
181 #endif
182 #endif
183 
184 	if(!data)
185 		return;
186 	emailp=(EMAILDATA *)data;
187 
188 #ifdef FWSAMDEBUG
189 	printf("Debug: [email][%lx] Plugin Sending Mail...\n",(unsigned long)threadid);
190 #endif
191 
192 	notetime=bd->blocktime;
193 	if(!bd->block)
194 		notetime+=bd->duration;
195 	timep=gmtime((time_t *)&(notetime));
196 	gth=timep->tm_hour;
197 	timep=localtime((time_t *)&(notetime));
198 	strftime(edate,40,"%a, %d %b %Y %H:%M:%S",timep);
199 	timediff=timep->tm_hour-gth;
200 	if(timediff>12)
201 		timediff-=24;
202 	else if(timediff<-12)
203 		timediff+=24;
204 
205 	snprintf(serverat,sizeof(serverat)-1,"mail server at %s",inettoa(emailp->ip.s_addr));
206 
207 	if(!emailp->mailsocket)
208 	{	emailsocketaddr.sin_port=htons(emailp->port);
209 		emailsocketaddr.sin_addr.s_addr=emailp->ip.s_addr;
210 		emailsocketaddr.sin_family=AF_INET;
211 		thissocketaddr.sin_port=htons(0); /* get a dynamic port  */
212 		thissocketaddr.sin_addr.s_addr=0;
213 		thissocketaddr.sin_family=AF_INET;
214 		/* create socket */
215 		emailp->mailsocket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
216 		if(emailp->mailsocket==INVALID_SOCKET)
217 		{	snprintf(emailmsg,sizeof(emailmsg)-1,"Error: [email] Couldn't create socket!");
218 			logmessage(1,emailmsg,"email",emailp->ip.s_addr);
219 			emailp->mailsocket=0;
220 			return;
221 		}
222 		/* bind it */
223 		if(bind(emailp->mailsocket,(struct sockaddr *)&(thissocketaddr),sizeof(struct sockaddr)))
224 		{	snprintf(emailmsg,sizeof(emailmsg)-1,"Error: [email] Couldn't bind socket!");
225 			logmessage(1,emailmsg,"email",emailp->ip.s_addr);
226 			emailp->mailsocket=0;
227 			return;
228 		}
229 		/* and connect to mail server */
230 		if(connect(emailp->mailsocket,(struct sockaddr *)&emailsocketaddr,sizeof(struct sockaddr)))
231 		{	snprintf(emailmsg,sizeof(emailmsg)-1,"Error: [email] Could not connect to %s! Will try later.",serverat);
232 			logmessage(1,emailmsg,"email",emailp->ip.s_addr);
233 			closesocket(emailp->mailsocket);
234 			emailp->mailsocket=0;
235 		}
236 	}
237 
238 	if(emailp->mailsocket)
239 	{	do
240 		{
241 #ifdef FWSAMDEBUG
242 			printf("Debug: [email][%lx] Connected to %s.\n",(unsigned long)threadid,serverat);
243 #endif
244 			ll=1;
245 			ioctlsocket(emailp->mailsocket,FIONBIO,&ll);	/* set non blocking  */
246 
247 			ll=FALSE;
248 
249 			if(!emailp->loggedin)
250 			{
251 				if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,"","220","waiting for banner on ",serverat))
252 				{	ll=TRUE;
253 					continue;
254 				}
255 				snprintf(emailmsg,sizeof(emailmsg)-1,"HELO %s\r\n",myhostname);	/* send helo */
256 				if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"250","after HELO on ",serverat))
257 				{	ll=TRUE;
258 					continue;
259 				}
260 				emailp->loggedin=TRUE;
261 			}
262 			snprintf(emailmsg,sizeof(emailmsg)-1,"RSET\r\n");	/* send reset */
263 			if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"250","after RSET on ",serverat))
264 			{	ll=TRUE;
265 				continue;
266 			}
267 			if(emailp->sender[0])
268 				snprintf(emailmsg,sizeof(emailmsg)-1,"MAIL FROM:<%s>\r\n",emailp->sender);	/* send mail from */
269 			else
270 				snprintf(emailmsg,sizeof(emailmsg)-1,"MAIL FROM:<SnortSam@%s>\r\n",myhostname);	/* send mail from */
271 			if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"250","after MAIL FROM on ",serverat))
272 			{	ll=TRUE;
273 				continue;
274 			}
275 			snprintf(emailmsg,sizeof(emailmsg)-1,"RCPT TO:<%s>\r\n",emailp->recipient);	/* send rcpt to */
276 			if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"250","after RCPT TO on ",serverat))
277 			{	ll=TRUE;
278 				continue;
279 			}
280 			snprintf(emailmsg,sizeof(emailmsg)-1,"DATA\r\n");	/* send reset */
281 			if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"354","after DATA on ",serverat))
282 			{	ll=TRUE;
283 				continue;
284 			}
285 
286 			if(disablereverselookups)
287 				snprintf(host,sizeof(host)-1,"%s",inettoa(bd->blockip));
288 			else
289 				snprintf(host,sizeof(host)-1,"%s (%s)",inettoa(bd->blockip),gethstname(bd->blockip));
290 
291 			if(bd->block)
292 			{	switch(bd->mode&FWSAM_HOW)
293 				{	case FWSAM_HOW_THIS:
294 						protoe=getprotobynumber(bd->proto);
295 						snprintf(msg,sizeof(msg)-1, "Blocking host %s in connection %s->%s:%d (%s) for %lu seconds.",
296 							host,bd->mode&FWSAM_WHO_SRC?inettoa(bd->blockip):inettoa(bd->peerip),bd->mode&FWSAM_WHO_SRC?inettoa(bd->peerip):inettoa(bd->blockip),bd->port,protoe->p_name,(unsigned long)bd->duration);
297 					break;
298 					case FWSAM_HOW_IN:
299 						snprintf(msg,sizeof(msg)-1,	"Blocking host %s inbound for %lu seconds.",host,(unsigned long)bd->duration);
300 					break;
301 					case FWSAM_HOW_OUT:
302 						snprintf(msg,sizeof(msg)-1, "Blocking host %s outbound for %lu seconds.",host,(unsigned long)bd->duration);
303 					break;
304 					case FWSAM_HOW_INOUT:
305 						snprintf(msg,sizeof(msg)-1, "Blocking host %s completely for %lu seconds.",host,(unsigned long)bd->duration);
306 					break;
307 					default:
308 						snprintf(msg,sizeof(msg)-1, "Blocking host %s in a weird way for %lu seconds. (Let me know if you see this message!)",host,(unsigned long)bd->duration);
309 					break;
310 				}
311 				snprintf(msg2,sizeof(msg2)-1,"This block was triggered by signature ID: %lu",bd->sig_id);
312 				snprintf(emailmsg,sizeof(emailmsg)-1,"From: %s%s\r\nTo: %s\r\nSubject: Blocked IP Address %s\r\nDate: %s %+.2i00\r\n\r\n%s\r\n\r\n%s\r\n\r\n.\r\n",
313 									emailp->sender[0]?emailp->sender:"SnortSam@",emailp->sender[0]?"":myhostname,emailp->recipient,
314 									inettoa(bd->blockip),edate,timediff,msg,msg2);	/* send message */
315 			}
316 			else
317 			{	switch(bd->mode&FWSAM_HOW)
318 				{	case FWSAM_HOW_THIS:
319 						protoe=getprotobynumber(bd->proto);
320 						snprintf(msg,sizeof(msg)-1, "Removing %lu sec block for host %s in connection %s->%s:%d (%s).",
321 							(unsigned long)bd->duration,host,bd->mode&FWSAM_WHO_SRC?inettoa(bd->blockip):inettoa(bd->peerip),bd->mode&FWSAM_WHO_SRC?inettoa(bd->peerip):inettoa(bd->blockip),bd->port,protoe->p_name);
322 					break;
323 					case FWSAM_HOW_IN:
324 						snprintf(msg,sizeof(msg)-1, "Removing %lu sec inbound block for host %s.",(unsigned long)bd->duration,host);
325 					break;
326 					case FWSAM_HOW_OUT:
327 						snprintf(msg,sizeof(msg)-1, "Removing %lu sec outbound block for host %s.",(unsigned long)bd->duration,host);
328 					break;
329 					case FWSAM_HOW_INOUT:
330 						snprintf(msg,sizeof(msg)-1, "Removing %lu sec complete block for host %s.",(unsigned long)bd->duration,host);
331 					break;
332 					default:
333 						snprintf(msg,sizeof(msg)-1, "Removing weird %lu sec block for host %s.",(unsigned long)bd->duration,host);
334 					break;
335 				}
336 				snprintf(msg2,sizeof(msg2)-1,"The block was originally triggered by signature ID: %lu",bd->sig_id);
337 				snprintf(emailmsg,sizeof(emailmsg)-1,"From: %s%s\r\nTo: %s\r\nSubject: Unblocked IP Address %s\r\nDate: %s %+.2i00\r\n\r\n%s\r\n\r\n%s\r\n\r\n.\r\n",
338 									emailp->sender[0]?emailp->sender:"SnortSam@",emailp->sender[0]?"":myhostname,emailp->recipient,
339 									inettoa(bd->blockip),edate,timediff,msg,msg2);	/* send message */
340 			}
341 			if(!sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"250","after message on ",serverat))
342 			{	ll=TRUE;
343 				continue;
344 			}
345 
346 			if(!moreinqueue(qp))
347 			{	snprintf(emailmsg,sizeof(emailmsg)-1,"QUIT\r\n");	/* send reset */
348 				sendreceive(emailp->mailsocket,EMAILNETWAIT,"email",emailp->ip,emailmsg,"","after QUIT on ",serverat);
349 				ll=TRUE;
350 			}
351 		}while(FALSE);
352 
353 		if(ll)
354 		{	closesocket(emailp->mailsocket);
355 			emailp->mailsocket=0;
356 			emailp->loggedin=FALSE;
357 		}
358 	}
359 
360 #ifdef FWSAMDEBUG
361 	printf("Debug: [email][%lx] Email has been sent. Now waiting 10 secs...\n",(unsigned long)threadid);
362 	waitms(10000);
363 	printf("Debug: Done waiting... ending thread %lx.\n",(unsigned long)threadid);
364 #endif
365 }
366 
367 #endif /* __SSP_EMAIL_C__ */
368 
369 
370 
371 
372 
373 
374