1 /****************************************************************************
2 *
3 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 * Copyright (C) 2005-2013 Sourcefire, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License Version 2 as
8 * published by the Free Software Foundation. You may not use, modify or
9 * distribute this program under any other version of the GNU General
10 * Public License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * ***************************************************************************/
22
23 /*
24 * @file snort_stream_ip.c
25 * @author Russ Combs <rcombs@sourcefire.com>
26 *
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "active.h"
34 #include "decode.h"
35 #include "detect.h"
36 #include "mstring.h"
37 #include "parser.h"
38 #include "profiler.h"
39 #include "sfPolicy.h"
40 #include "sfxhash.h"
41 #include "sf_types.h"
42 #include "snort_debug.h"
43 #include "memory_stats.h"
44
45 #include "spp_session.h"
46 #include "session_api.h"
47 #include "snort_session.h"
48
49 #include "snort_stream_ip.h"
50 #include "session_expect.h"
51 #include "stream5_ha.h"
52 #include "util.h"
53
54 #include "reg_test.h"
55
56 #ifdef PERF_PROFILING
57 PreprocStats s5IpPerfStats;
58 #endif
59
60 static SessionCache* ip_lws_cache = NULL;
61
62 //-------------------------------------------------------------------------
63 // private methods
64 //-------------------------------------------------------------------------
65
StreamPrintIpConfig(StreamIpPolicy * policy)66 static void StreamPrintIpConfig (StreamIpPolicy* policy)
67 {
68 LogMessage("Stream IP Policy config:\n");
69 LogMessage(" Timeout: %d seconds\n", policy->session_timeout);
70
71 }
72
StreamParseIpArgs(char * args,StreamIpPolicy * policy)73 static void StreamParseIpArgs (char* args, StreamIpPolicy* policy)
74 {
75 char* *toks;
76 int num_toks;
77 int i;
78
79 policy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT;
80
81 if ( !args || !*args )
82 return;
83
84 toks = mSplit(args, ",", 0, &num_toks, 0);
85
86 for (i = 0; i < num_toks; i++)
87 {
88 int s_toks;
89 char* *stoks = mSplit(toks[i], " ", 2, &s_toks, 0);
90
91 if (s_toks == 0)
92 {
93 ParseError("Missing parameter in Stream IP config.\n");
94 }
95
96 if(!strcasecmp(stoks[0], "timeout"))
97 {
98 char* endPtr = NULL;
99
100 if(stoks[1])
101 {
102 policy->session_timeout = strtoul(stoks[1], &endPtr, 10);
103 }
104
105 if (!stoks[1] || (endPtr == &stoks[1][0]))
106 {
107 ParseError("Invalid timeout in config file. Integer parameter required.\n");
108 }
109
110 if ((policy->session_timeout > STREAM_MAX_SSN_TIMEOUT) ||
111 (policy->session_timeout < STREAM_MIN_SSN_TIMEOUT))
112 {
113 ParseError("Invalid timeout in config file. Must be between %d and %d\n",
114 STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT);
115 }
116 if (s_toks > 2)
117 {
118 ParseError("Invalid Stream IP Policy option. Missing comma?\n");
119 }
120 }
121 else
122 {
123 ParseError("Invalid Stream IP policy option\n");
124 }
125
126 mSplitFree(&stoks, s_toks);
127 }
128
129 mSplitFree(&toks, num_toks);
130 }
131
IpSessionCleanup(void * ssn)132 void IpSessionCleanup (void* ssn)
133 {
134 SessionControlBlock *scb = ( SessionControlBlock * ) ssn;
135
136 if (scb->ha_state.session_flags & SSNFLAG_PRUNED)
137 {
138 CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED);
139 }
140 else if (scb->ha_state.session_flags & SSNFLAG_TIMEDOUT)
141 {
142 CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT);
143 }
144 else
145 {
146 CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY);
147 }
148
149 StreamResetFlowBits(scb);
150 session_api->free_application_data(scb);
151
152 scb->ha_state.session_flags = SSNFLAG_NONE;
153 scb->session_state = STREAM_STATE_NONE;
154
155 scb->expire_time = 0;
156 scb->ha_state.ignore_direction = 0;
157 s5stats.active_ip_sessions--;
158 }
159
160
161 #ifdef ENABLE_HA
162
163 //-------------------------------------------------------------------------
164 // ip ha stuff
165 //-------------------------------------------------------------------------
166
GetLWIpSession(const SessionKey * key)167 SessionControlBlock *GetLWIpSession (const SessionKey *key)
168 {
169 return session_api->get_session_by_key(ip_lws_cache, key);
170 }
171
StreamIPCreateSession(const SessionKey * key)172 static SessionControlBlock *StreamIPCreateSession (const SessionKey *key)
173 {
174 setNapRuntimePolicy(getDefaultPolicy());
175
176 SessionControlBlock *scb = session_api->create_session(ip_lws_cache, NULL, key);
177 if (scb)
178 s5stats.active_ip_sessions++;
179
180 return scb;
181 }
182
StreamIPDeleteSession(const SessionKey * key)183 static int StreamIPDeleteSession (const SessionKey *key)
184 {
185 SessionControlBlock *scb = session_api->get_session_by_key(ip_lws_cache, key);
186
187 if( scb != NULL )
188 {
189 if( StreamSetRuntimeConfiguration(scb, scb->protocol) == 0 )
190 {
191 session_api->delete_session(ip_lws_cache, scb, "ha sync", false);
192 s5stats.active_ip_sessions--;
193 }
194 else
195 WarningMessage(" WARNING: Attempt to delete an IP Session when no valid runtime configuration\n" );
196 }
197
198 return 0;
199 }
200
201
202 static HA_Api ha_ip_api = {
203 /*.get_lws = */ GetLWIpSession,
204
205 /*.create_session = */ StreamIPCreateSession,
206 /*.deactivate_session = */ NULL,
207 /*.delete_session = */ StreamIPDeleteSession,
208 };
209
210 #endif
211
212 //-------------------------------------------------------------------------
213 // public methods
214 //-------------------------------------------------------------------------
215
StreamInitIp(void)216 void StreamInitIp( void )
217 {
218 if(ip_lws_cache == NULL)
219 {
220 ip_lws_cache = session_api->init_session_cache( SESSION_PROTO_IP,
221 0, // NO session control blocks for IP
222 IpSessionCleanup);
223 }
224
225 #ifdef ENABLE_HA
226 ha_set_api(IPPROTO_IP, &ha_ip_api);
227 #endif
228 }
229
StreamResetIp(void)230 void StreamResetIp (void)
231 {
232 session_api->purge_session_cache(ip_lws_cache);
233 }
234
StreamCleanIp(void)235 void StreamCleanIp (void)
236 {
237 if ( ip_lws_cache )
238 s5stats.ip_prunes = session_api->get_session_prune_count( SESSION_PROTO_IP );
239
240 /* Clean up hash table -- delete all sessions */
241 session_api->delete_session_cache( SESSION_PROTO_IP );
242 ip_lws_cache = NULL;
243 }
244
245 //-------------------------------------------------------------------------
246 // public config methods
247 //-------------------------------------------------------------------------
248
StreamIpPolicyInit(StreamIpConfig * config,char * args)249 void StreamIpPolicyInit (StreamIpConfig* config, char* args)
250 {
251 if (config == NULL)
252 return;
253
254 StreamParseIpArgs(args, &config->default_policy);
255 StreamPrintIpConfig(&config->default_policy);
256 }
257
StreamIpConfigFree(StreamIpConfig * config)258 void StreamIpConfigFree (StreamIpConfig* config)
259 {
260 if (config == NULL)
261 return;
262
263 SnortPreprocFree(config, sizeof(StreamIpConfig),
264 PP_STREAM, PP_MEM_CATEGORY_CONFIG);
265 }
266
StreamVerifyIpConfig(StreamIpConfig * config,tSfPolicyId policy_id)267 int StreamVerifyIpConfig (StreamIpConfig* config, tSfPolicyId policy_id)
268 {
269 if (config == NULL)
270 return -1;
271
272 if (!ip_lws_cache)
273 return -1;
274
275 return 0;
276 }
277
278 //-------------------------------------------------------------------------
279 // public access methods
280 //-------------------------------------------------------------------------
281
StreamGetIpPrunes(void)282 uint32_t StreamGetIpPrunes (void)
283 {
284 if( ip_lws_cache )
285 return session_api->get_session_prune_count( SESSION_PROTO_IP );
286 else
287 return s5stats.ip_prunes;
288 }
289
StreamResetIpPrunes(void)290 void StreamResetIpPrunes (void)
291 {
292 session_api->reset_session_prune_count( SESSION_PROTO_IP );
293 }
294
295 //-------------------------------------------------------------------------
296 // private packet processing methods
297 //-------------------------------------------------------------------------
298
InitSession(Packet * p,SessionControlBlock * scb)299 static inline void InitSession (Packet* p, SessionControlBlock *scb)
300 {
301 DEBUG_WRAP(DebugMessage(DEBUG_STREAM,
302 "Stream IP session initialized!\n"););
303
304 s5stats.total_ip_sessions++;
305 s5stats.active_ip_sessions++;
306 IP_COPY_VALUE(scb->client_ip, GET_SRC_IP(p));
307 IP_COPY_VALUE(scb->server_ip, GET_DST_IP(p));
308 }
309
BlockedSession(Packet * p,SessionControlBlock * scb)310 static inline int BlockedSession (Packet* p, SessionControlBlock *scb)
311 {
312 if ( !(scb->ha_state.session_flags & (SSNFLAG_DROP_CLIENT|SSNFLAG_DROP_SERVER)) )
313 return 0;
314
315 if (
316 ((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.session_flags & SSNFLAG_DROP_SERVER)) ||
317 ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.session_flags & SSNFLAG_DROP_CLIENT)) )
318 {
319 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
320 "Blocking %s packet as session was blocked\n",
321 p->packet_flags & PKT_FROM_SERVER ? "server" : "client"););
322
323 DisableDetect( p );
324 if ( scb->ha_state.session_flags & SSNFLAG_FORCE_BLOCK )
325 Active_ForceDropSessionWithoutReset();
326 else
327 Active_DropSessionWithoutReset(p);
328
329 #ifdef ACTIVE_RESPONSE
330 StreamActiveResponse(p, scb);
331 #endif
332 if (pkt_trace_enabled)
333 addPktTraceData(VERDICT_REASON_STREAM, snprintf(trace_line, MAX_TRACE_LINE,
334 "Stream: session was already blocked, %s\n", getPktTraceActMsg()));
335 else addPktTraceData(VERDICT_REASON_STREAM, 0);
336 return 1;
337 }
338 return 0;
339 }
340
IgnoreSession(Packet * p,SessionControlBlock * scb)341 static inline int IgnoreSession (Packet* p, SessionControlBlock *scb)
342 {
343 if (
344 ((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT)) ||
345 ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_SERVER)) )
346 {
347 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
348 "Stream Ignoring packet from %d. Session marked as ignore\n",
349 p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder"););
350
351 session_api->disable_inspection(scb, p);
352 return 1;
353 }
354
355 return 0;
356 }
357
358 #ifdef ENABLE_EXPECTED_IP
CheckExpectedSession(Packet * p,SessionControlBlock * scb)359 static inline int CheckExpectedSession (Packet* p, SessionControlBlock *scb)
360 {
361 int ignore;
362
363 ignore = StreamExpectCheck(p, scb);
364
365 if (ignore)
366 {
367 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
368 "Stream: Ignoring packet from %d. Marking session marked as ignore.\n",
369 p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder"););
370
371 scb->ha_state.ignore_direction = ignore;
372 session_api->disable_inspection(scb, p);
373 return 1;
374 }
375
376 return 0;
377 }
378 #endif
379
UpdateSession(Packet * p,SessionControlBlock * scb)380 static inline void UpdateSession (Packet* p, SessionControlBlock* scb)
381 {
382 MarkupPacketFlags(p, scb);
383
384 if ( !(scb->ha_state.session_flags & SSNFLAG_ESTABLISHED) )
385 {
386
387 if ( p->packet_flags & PKT_FROM_CLIENT )
388 {
389 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
390 "Stream: Updating on packet from client\n"););
391
392 scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT;
393 }
394 else
395 {
396 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
397 "Stream: Updating on packet from server\n"););
398
399 scb->ha_state.session_flags |= SSNFLAG_SEEN_SERVER;
400 }
401
402 if ( (scb->ha_state.session_flags & SSNFLAG_SEEN_CLIENT) &&
403 (scb->ha_state.session_flags & SSNFLAG_SEEN_SERVER) )
404 {
405 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
406 "Stream: session established!\n"););
407
408 scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED;
409
410 #ifdef ACTIVE_RESPONSE
411 SetTTL(scb, p, 0);
412 #endif
413 }
414 }
415
416 // Reset the session timeout.
417 {
418 StreamIpPolicy* policy;
419 policy = (StreamIpPolicy*)scb->proto_policy;
420
421 session_api->set_expire_timer(p, scb, policy->session_timeout);
422 }
423 }
424
425 //-------------------------------------------------------------------------
426 // public packet processing method
427 //-------------------------------------------------------------------------
428
StreamProcessIp(Packet * p,SessionControlBlock * scb,SessionKey * skey)429 int StreamProcessIp( Packet *p, SessionControlBlock *scb, SessionKey *skey )
430 {
431 PROFILE_VARS;
432
433 PREPROC_PROFILE_START( s5IpPerfStats );
434
435 if( scb->proto_policy == NULL )
436 {
437 scb->proto_policy = ( ( StreamConfig * ) scb->stream_config )->ip_config;
438 #if defined(DAQ_CAPA_CST_TIMEOUT)
439 if (Daq_Capa_Timeout)
440 {
441 StreamIpPolicy* policy;
442 uint64_t timeout;
443 policy = (StreamIpPolicy*)scb->proto_policy;
444 GetTimeout(p,&timeout);
445 policy->session_timeout = timeout;
446 }
447 #endif
448 }
449 if( !scb->session_established )
450 {
451 scb->session_established = true;
452 InitSession(p, scb);
453
454 #ifdef ENABLE_EXPECTED_IP
455 if( CheckExpectedSession( p, scb ) )
456 {
457 PREPROC_PROFILE_END( s5IpPerfStats );
458 return 0;
459 }
460 #endif
461 }
462 else
463 {
464 if( ( scb->session_state & STREAM_STATE_TIMEDOUT ) || StreamExpire( p, scb ) )
465 {
466 scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT;
467
468 /* Session is timed out */
469 DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Stream IP session timeout!\n"););
470
471 #ifdef ENABLE_HA
472 /* Notify the HA peer of the session cleanup/reset by way of a deletion notification. */
473 PREPROC_PROFILE_TMPEND( s5IpPerfStats );
474 SessionHANotifyDeletion( scb );
475 scb->ha_flags = ( HA_FLAG_NEW | HA_FLAG_MODIFIED | HA_FLAG_MAJOR_CHANGE );
476 PREPROC_PROFILE_TMPSTART( s5IpPerfStats );
477 #endif
478
479 /* Clean it up */
480 IpSessionCleanup( scb );
481
482 #ifdef ENABLE_EXPECTED_IP
483 if( CheckExpectedSession( p, scb ) )
484 {
485 PREPROC_PROFILE_END( s5IpPerfStats );
486 return 0;
487 }
488 #endif
489 }
490 }
491
492 session_api->set_packet_direction_flag( p, scb );
493 p->ssnptr = scb;
494
495 if( BlockedSession( p, scb ) || IgnoreSession( p, scb ) )
496 {
497 PREPROC_PROFILE_END( s5IpPerfStats );
498 return 0;
499 }
500
501 UpdateSession( p, scb );
502
503 PREPROC_PROFILE_END( s5IpPerfStats );
504
505 return 0;
506 }
507
508 #ifdef SNORT_RELOAD
SessionIPReload(uint32_t max_sessions,uint16_t pruningTimeout,uint16_t nominalTimeout)509 void SessionIPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout)
510 {
511 SessionReload(ip_lws_cache, max_sessions, pruningTimeout, nominalTimeout
512 #ifdef REG_TEST
513 , "IP"
514 #endif
515 );
516 }
517
SessionIPReloadAdjust(unsigned maxWork)518 unsigned SessionIPReloadAdjust(unsigned maxWork)
519 {
520 return SessionProtocolReloadAdjust(ip_lws_cache, session_configuration->max_ip_sessions,
521 maxWork, 0
522 #ifdef REG_TEST
523 , "IP"
524 #endif
525 );
526 }
527 #endif
528
get_ip_used_mempool()529 size_t get_ip_used_mempool()
530 {
531 if (ip_lws_cache && ip_lws_cache->protocol_session_pool)
532 return ip_lws_cache->protocol_session_pool->used_memory;
533
534 return 0;
535 }
536