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