1 /* $Id: ssp_opsec.c,v 2.6 2009/11/27 01:39:40 fknobbe Exp $
2  *
3  *
4  * Copyright (c) 2001-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_opsec.c
30  *
31  * Purpose:
32  *
33  * This SnortSam plugin makes use of the OPSEC libraries of the OPSEC SDK in
34  * order to  communicate with Firewall-1. This implementation makes the process
35  * fully OPSEC compliant.
36  *
37  * Comments:
38  *
39  * - Needs some serious rewrite. The client/server objects should be created at
40  * parse time of the plugin, and then kept in the data list (as opposed to keeping
41  * the config file in list and creating client/server objects at every block).
42  *
43  * - Look into possible thread conflict issue.
44  *
45  */
46 
47 
48 #ifndef		__SSP_OPSEC_C__
49 #define		__SSP_OPSEC_C__
50 
51 #ifdef	ENABLE_OPSEC
52 
53 
54 #include "snortsam.h"
55 #include "ssp_opsec.h"
56 
57 
58 #include <sys/types.h>
59 #include <stdio.h>
60 #include <string.h>
61 #include <time.h>
62 #ifdef WIN32
63 #include <winsock.h>
64 #else
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
67 #endif
68 
69 
70 
71 static BLOCKINFO *opsecblockinfo;  /* Since we're multi-threading, we need to dump the block info somewhere static */
72 
73 /*
74  * Session start handler dummy
75 */
fw_sam_client_session_creator(OpsecSession * session)76 static int fw_sam_client_session_creator(OpsecSession *session)
77 {	return OPSEC_SESSION_OK;
78 }
79 
80 /*
81  * Session end handler dummy
82 */
fw_sam_client_session_deletor(OpsecSession * session)83 static void fw_sam_client_session_deletor(OpsecSession *session)
84 {
85 }
86 
87 /*
88  * Send SAM requests when session is established
89 */
SessionEstablishedHandler(OpsecSession * session)90 static int SessionEstablishedHandler(OpsecSession *session)
91 {	int rc,logtype,action,opmode;
92 	struct in_addr bip,bpip;
93 	static char msg[STRBUFSIZE+2];	/*	This msg buffer needs to be static so that the other
94 								handler can access it. It could also be malloc'ed and
95 								free'd (as in the SDK example), but why make it
96 								complicated and open outselves up for a memory leak?
97 								(as in the SDK...grin). After all, it's just 1 K... */
98 
99 	if(!opsecblockinfo->block)
100 	{	action=SAM_INHIBIT_DROP_AND_CLOSE|SAM_CANCEL;
101 		safecopy(msg,"Cancel Inhibit");
102 	}
103 	else
104 	{	action=SAM_INHIBIT_DROP_AND_CLOSE;
105 /*		safecopy(msg,"Inhibit & Close");     4.x lingo */
106 		safecopy(msg,"Inhibit-Drop");		/* ng lingo */
107 	}
108 	logtype=opsecblockinfo->mode&FWSAM_LOG;
109 /*	...which is cheating since the values match up. If we wanted to be politically correct,
110 	we'd need this:
111 
112 	switch(opsecblockinfo->mode&FWSAM_LOG)
113 	{	default: ;
114 		case FWSAM_LOG_NONE:		logtype=SAM_NOLOG; break;
115 		case FWSAM_LOG_SHORTLOG:	logtype=SAM_SHORT_NOALERT; break;
116 		case FWSAM_LOG_SHORTALERT:	logtype=SAM_SHORT_ALERT; break;
117 		case FWSAM_LOG_LONGLOG:		logtype=SAM_LONG_NOALERT; break;
118 		case FWSAM_LOG_LONGALERT:	logtype=SAM_LONG_ALERT; break;
119 	}
120 */
121 	switch(opsecblockinfo->mode&FWSAM_HOW)
122 	{	case FWSAM_HOW_IN:
123 			strcat(msg," src ip");
124 			opmode=SAM_SRC_IP;
125 			break;
126 		case FWSAM_HOW_OUT:
127 			strcat(msg," dst ip");
128 			opmode=SAM_DST_IP;
129 			break;
130 		default: ;
131 		case FWSAM_HOW_INOUT:
132 			strcat(msg," any ip");
133 			opmode=SAM_ANY_IP;
134 			break;
135 		case FWSAM_HOW_THIS:
136 			strcat(msg," service");
137 			opmode=SAM_SERV_OLD;  /* using old format for backwards compatibility */
138 			break;
139 	}
140 	bip.s_addr=opsecblockinfo->blockip;
141 	snprintf(msg+strlen(msg),sizeof(msg)-1-strlen(msg)," %s",inettoa(bip.s_addr)); /* add IP to message */
142 	if(opsecblockinfo->mode&FWSAM_HOW_THIS)
143 	{	bpip.s_addr=opsecblockinfo->peerip; /* since this ia a service block, we get the peer ip */
144 		snprintf(msg+strlen(msg),sizeof(msg)-1-strlen(msg)," %s %i %i",inettoa(bpip.s_addr),opsecblockinfo->port,opsecblockinfo->proto);
145 		strcat(msg," on All");
146 		rc=sam_client_action(session,action,logtype,"All",msg,SAM_EXPIRE,opsecblockinfo->duration,
147 						 SAM_REQ_TYPE,opmode,opsecblockinfo->blockip,opsecblockinfo->peerip,
148 						 opsecblockinfo->port,opsecblockinfo->proto,NULL);
149 	}
150 	else
151 	{	strcat(msg," on All");
152 		rc=sam_client_action(session,action,logtype,"All",msg,SAM_EXPIRE,opsecblockinfo->duration,
153 						 SAM_REQ_TYPE,opmode,opsecblockinfo->blockip,NULL);
154 	}
155 	if(rc<0)
156 	{	snprintf(msg,sizeof(msg)-1,"Error: OPSEC request '%s' failed (%s)!",msg,opsec_errno_str(opsec_errno));
157 		logmessage(1,msg,"opsec",0);
158 	}
159 	return OPSEC_SESSION_OK;
160 }
161 
162 /*
163  * Handle SAM action request acknowledgement
164 */
AckEventHandler(OpsecSession * session,int n_closed,int sam_status,int fw_index,int fw_total,char * fw_host,void * cb_data)165 static int AckEventHandler(OpsecSession *session,int n_closed,int sam_status,
166 						   int fw_index,int fw_total,char *fw_host,void *cb_data)
167 {	char msg[STRBUFSIZE+2];
168 
169 	switch (sam_status)
170 	{	case SAM_REQUEST_RECEIVED:	/* the request is received */
171 			snprintf(msg,sizeof(msg)-1,"Info: OPSEC request for '%s' acknowledged.",(char *)cb_data);
172 			logmessage(3,msg,"opsec",0);
173 			break;
174 		case SAM_MODULE_DONE:		/* the module from firewall object is processed */
175 			snprintf(msg,sizeof(msg)-1,"Info: OPSEC request on '%s' (%d/%d) successfully completed processing '%s'.",fw_host,fw_index+1,fw_total,(char *)cb_data);
176 			logmessage(3,msg,"opsec",0);
177 			break;
178 		case SAM_MODULE_FAILED:		/* the module processing has failed */
179 			snprintf(msg,sizeof(msg)-1,"Error: OPSEC request on '%s' (%d/%d) failed processing '%s'.",fw_host,fw_index+1,fw_total,(char *)cb_data);
180 			logmessage(1,msg,"opsec",0);
181 			break;
182 		case SAM_REQUEST_DONE:		/* all modules are processed */
183 			snprintf(msg,sizeof(msg)-1,"Info: OPSEC request for '%s' done.",(char *)cb_data);
184 			logmessage(3,msg,"opsec",0);
185 			return OPSEC_SESSION_END;
186 			break;
187 		case SAM_RESOLVE_ERR:		/* resolving error for firewall object */
188 			snprintf(msg,sizeof(msg)-1,"Error: OPSEC could not resolve firewalled object name in '%s'. The SAM request was not enforced.",(char *)cb_data);
189 			logmessage(1,msg,"opsec",0);
190 			return OPSEC_SESSION_END;
191 		case SAM_UNEXPECTED_END_OF_SESSION:	/* unexpected end of session while processing the request */
192 			snprintf(msg,sizeof(msg)-1,"Error: Unexpected end of OPSEC session. It is possible that the SAM request for '%s' was not enforced.",(char *)cb_data);
193 			logmessage(1,msg,"opsec",0);
194 			return OPSEC_SESSION_END;
195 		default:
196 			snprintf(msg,sizeof(msg)-1,"Error: Unexpected OPSEC status '%d'.",sam_status);
197 			logmessage(1,msg,"opsec",0);
198 			return OPSEC_SESSION_ERR;
199 	}
200 	return OPSEC_SESSION_OK;
201 }
202 
203 /*
204  * Monitor handler dummy (since we don't monitor)
205 */
MonitorAckEventHandler(OpsecSession * session,int sam_status,int fw_index,int fw_total,char * fw_host,void * cb_data,opsec_table monitor_data)206 static int MonitorAckEventHandler(OpsecSession *session,int sam_status,int fw_index,int fw_total,
207 								  char *fw_host,void *cb_data,opsec_table monitor_data)
208 {
209 	return OPSEC_SESSION_OK;
210 }
211 
212 /*
213  * Clean environment before exiting
214 */
clean_env(OpsecEnv * env,OpsecEntity * client,OpsecEntity * server)215 static void clean_env(OpsecEnv *env,OpsecEntity *client,OpsecEntity *server)
216 {	if(client)
217 		opsec_destroy_entity(client);
218 	if(server)
219 		opsec_destroy_entity(server);
220 	if(env)
221 		opsec_env_destroy(env);
222 }
223 
224 
225 /* This routine parses the opsec statements in the config file.
226  * It builds a list of config files (i.e. sam.conf, opsec.conf)
227 */
OPSEC_Parse(char * val,char * file,unsigned long line,DATALIST * datalistp)228 void OPSEC_Parse(char *val,char *file,unsigned long line,DATALIST *datalistp)
229 {	OPSECDATA *opsecp;
230 	char tmpstr[FILEBUFSIZE+2],msg[STRBUFSIZE+2];
231 	FILE *fp;
232 
233 #ifdef FWSAMDEBUG
234 	printf("Debug: [opsec] Plugin Parsing...\n");
235 #endif
236     if(*val)
237 	{	remspace(val);
238 		safecopy(tmpstr,val);
239 		if((fp=fopen(tmpstr,"r"))!= NULL)				/* if file exist */
240 		{	opsecp=safemalloc(sizeof(OPSECDATA),"OPSEC_Parse","opsecp");	/* create new cfg file entry */
241 			datalistp->data=opsecp;
242 			safecopy(opsecp->cfgfile,tmpstr);
243 			fclose(fp);
244 			snprintf(msg,sizeof(msg)-1,"OPSEC: Adding configuration file '%s'.",tmpstr);
245 			logmessage(3,msg,"opsec",0);
246 		}
247 		else
248 		{	snprintf(msg,sizeof(msg)-1,"Error: [%s: %lu] OPSEC conf file '%s' not found, parameter ignored.",file,line,tmpstr);
249 			logmessage(1,msg,"opsec",0);
250 		}
251 	}
252 	else
253 	{	snprintf(msg,sizeof(msg)-1,"Error: [%s: %lu] Empty OPSEC parameter.",file,line);
254 		logmessage(1,msg,"opsec",0);
255 	}
256 }
257 
258 
259 /* This routine initiates the block. It walks the list of OPSEC
260  * configuration files and establishes and OPSEC session to
261  * each host defined in the conf files, and sends the SAM
262  * blocking request.
263  */
OPSEC_Block(BLOCKINFO * bd,void * data,unsigned long qp)264 void OPSEC_Block(BLOCKINFO *bd,void *data,unsigned long qp)
265 {   OPSECDATA *opsecp;
266 	int port_type;
267 	OpsecEnv *env;
268 	OpsecEntity *server,*client;
269 	OpsecSession *session;
270 	unsigned long sam_server;
271 	unsigned short port;
272 	char *conf,msg[STRBUFSIZE+2];
273 #ifdef FWSAMDEBUG
274 #ifdef WIN32
275 	unsigned long threadid=GetCurrentThreadId();
276 #else
277 	pthread_t threadid=pthread_self();
278 #endif
279 
280 
281 	printf("Debug: [opsec][%lx] Plugin Blocking...\n",threadid);
282 #endif
283 
284 	opsecp=(OPSECDATA *)data;
285 	opsecblockinfo=bd;
286 
287 	env=NULL;
288 	server=client=NULL;
289 	session=NULL;
290 
291 	/* create SAM Session */
292 	env=opsec_init(OPSEC_CONF_FILE,opsecp->cfgfile,OPSEC_EOL);
293 	if(env==NULL)
294 	{	snprintf(msg,sizeof(msg)-1,"Error: [%s] OPSEC init failed!",opsecp->cfgfile);
295 		logmessage(1,msg,"opsec",0);
296 		getout(1);
297 	}
298 	/* Next, get the port number out of the OPSEC conf file */
299 	conf=opsec_get_conf(env,"sam_server","auth_port",NULL);
300 	if(conf)
301 	{	port=atoi(conf);
302 		port_type=OPSEC_SERVER_AUTH_PORT;
303 	}
304 	else
305 	{	conf=opsec_get_conf(env,"sam_server","port",NULL);
306 		if(conf)
307 		{	port=atoi(conf);
308 			port_type=OPSEC_SERVER_PORT;
309 		}
310 		else
311 		{	snprintf(msg,sizeof(msg)-1,"Error: [%s] File does not define sam_server port! Skipping file.",opsecp->cfgfile);
312 			logmessage(1,msg,"opsec",0);
313 			return;
314 		}
315 	}
316 	/* Here we pick the IP address out of the OPSEC conf file
317 	   (SDK didn't include this... tsk-tsk)  */
318 	conf=opsec_get_conf(env,"sam_server","ip",NULL);
319 	if(conf)
320 	{	sam_server=getip(conf);
321 		if(!sam_server)
322 		{	snprintf(msg,sizeof(msg)-1,"Error: [%s] File does not define valid sam_server ip! Trying localhost.",opsecp->cfgfile);
323 			logmessage(1,msg,"opsec",0);
324 			sam_server=getip("localhost");
325 		}
326 	}
327 	else
328 	{	snprintf(msg,sizeof(msg)-1,"Error: [%s] File does not define sam_server ip! Assuming localhost.",opsecp->cfgfile);
329 		logmessage(1,msg,"opsec",0);
330 		sam_server=getip("localhost");
331 	}
332 
333 	/* create client entity */
334 	client=opsec_init_entity(env,SAM_CLIENT,OPSEC_SESSION_START_HANDLER,fw_sam_client_session_creator,
335 							 OPSEC_SESSION_END_HANDLER,fw_sam_client_session_deletor,
336 							 OPSEC_SESSION_ESTABLISHED_HANDLER,SessionEstablishedHandler,
337 							 SAM_ACK_HANDLER,AckEventHandler,
338 							 SAM_MONITOR_ACK_HANDLER,MonitorAckEventHandler,OPSEC_EOL);
339 
340 	if(client)
341 	{	/* create server entity */
342 		server=opsec_init_entity(env,SAM_SERVER,OPSEC_ENTITY_NAME,"sam_server",
343 								 OPSEC_SERVER_IP,sam_server,port_type,port,OPSEC_EOL);
344 
345 		if(server)
346 		{	session=sam_new_session(client,server); /* establich session and process handlers */
347 			if(session)
348 				opsec_mainloop(env);
349 			else
350 			{	snprintf(msg,sizeof(msg)-1,"Error: [%s] SAM session initialization failed (%s)! The SAM request was not performed.",opsecp->cfgfile,opsec_errno_str(opsec_errno));
351 				logmessage(1,msg,"opsec",0);
352 			}
353 		}
354 		else
355 		{	snprintf(msg,sizeof(msg)-1,"Error: [%s] OPSEC server entity initialization failed! The SAM request was not performed.",opsecp->cfgfile);
356 			logmessage(1,msg,"opsec",0);
357 		}
358 	}
359 	else
360 	{	snprintf(msg,sizeof(msg)-1,"Error: [%s] OPSEC client entity initialization failed! The SAM request was not performed.",opsecp->cfgfile);
361 		logmessage(1,msg,"opsec",0);
362 	}
363 	clean_env(env,client,server);
364 }
365 
366 #endif
367 #endif /* __SSP_OPSEC_C__ */
368 
369