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