1 /****************************************************************************
2  *
3  * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4  * Copyright (C) 2004-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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "sf_types.h"
28 #include "snort_debug.h"
29 #include "decode.h"
30 #include "mstring.h"
31 #include "sfxhash.h"
32 #include "util.h"
33 #include "memory_stats.h"
34 
35 #include "spp_session.h"
36 #include "session_api.h"
37 #include "snort_session.h"
38 
39 #include "stream_common.h"
40 #include "snort_stream_tcp.h"
41 #include "snort_stream_udp.h"
42 #include "snort_stream_icmp.h"
43 
44 #include "parser.h"
45 
46 #include "reg_test.h"
47 
48 #include "profiler.h"
49 #include "sfPolicy.h"
50 #ifdef PERF_PROFILING
51 PreprocStats s5IcmpPerfStats;
52 #endif
53 
54 /* client/server ip/port dereference */
55 #define icmp_sender_ip lwSsn->client_ip
56 #define icmp_responder_ip lwSsn->server_ip
57 
58 /*  D A T A  S T R U C T U R E S  ***********************************/
59 typedef struct _IcmpSession
60 {
61     SessionControlBlock *lwSsn;
62 
63     uint32_t   echo_count;
64 
65     struct timeval ssn_time;
66 
67 } IcmpSession;
68 
69 
70 /*  G L O B A L S  **************************************************/
71 static SessionCache* icmp_lws_cache = NULL;
72 
73 /*  P R O T O T Y P E S  ********************************************/
74 static void StreamParseIcmpArgs(char *, StreamIcmpPolicy *);
75 static void StreamPrintIcmpConfig(StreamIcmpPolicy *);
76 static int ProcessIcmpUnreach(Packet *p);
77 static int ProcessIcmpEcho(Packet *p);
78 
StreamInitIcmp(void)79 void StreamInitIcmp( void )
80 {
81     /* Finally ICMP */
82     if(icmp_lws_cache == NULL)
83     {
84         icmp_lws_cache = session_api->init_session_cache( SESSION_PROTO_ICMP,
85                                                           sizeof( IcmpSession ),
86                                                           NULL );
87     }
88 }
89 
StreamIcmpPolicyInit(StreamIcmpConfig * config,char * args)90 void StreamIcmpPolicyInit(StreamIcmpConfig *config, char *args)
91 {
92     if (config == NULL)
93         return;
94 
95     config->num_policies++;
96 
97     StreamParseIcmpArgs(args, &config->default_policy);
98 
99     StreamPrintIcmpConfig(&config->default_policy);
100 }
101 
StreamParseIcmpArgs(char * args,StreamIcmpPolicy * s5IcmpPolicy)102 static void StreamParseIcmpArgs(char *args, StreamIcmpPolicy *s5IcmpPolicy)
103 {
104     char **toks;
105     int num_toks;
106     int i;
107     char **stoks = NULL;
108     int s_toks;
109     char *endPtr = NULL;
110 
111     s5IcmpPolicy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT;
112     //s5IcmpPolicy->flags = 0;
113 
114     if(args != NULL && strlen(args) != 0)
115     {
116         toks = mSplit(args, ",", 0, &num_toks, 0);
117 
118         for (i = 0; i < num_toks; i++)
119         {
120             stoks = mSplit(toks[i], " ", 2, &s_toks, 0);
121 
122             if (s_toks == 0)
123             {
124                 FatalError("%s(%d) => Missing parameter in Stream ICMP config.\n",
125                     file_name, file_line);
126             }
127 
128             if(!strcasecmp(stoks[0], "timeout"))
129             {
130                 if(stoks[1])
131                 {
132                     s5IcmpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10);
133                 }
134 
135                 if (!stoks[1] || (endPtr == &stoks[1][0]) || *endPtr)
136                 {
137                     FatalError("%s(%d) => Invalid timeout in config file.  Integer parameter required.\n",
138                             file_name, file_line);
139                 }
140 
141                 if ((s5IcmpPolicy->session_timeout > STREAM_MAX_SSN_TIMEOUT) ||
142                     (s5IcmpPolicy->session_timeout < STREAM_MIN_SSN_TIMEOUT))
143                 {
144                     FatalError("%s(%d) => Invalid timeout in config file.  "
145                         "Must be between %d and %d\n",
146                         file_name, file_line,
147                         STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT);
148                 }
149                 if (s_toks > 2)
150                 {
151                     FatalError("%s(%d) => Invalid Stream ICMP Policy option.  Missing comma?\n",
152                         file_name, file_line);
153                 }
154             }
155             else
156             {
157                 FatalError("%s(%d) => Invalid Stream ICMP policy option\n",
158                             file_name, file_line);
159             }
160 
161             mSplitFree(&stoks, s_toks);
162         }
163 
164         mSplitFree(&toks, num_toks);
165     }
166 }
167 
StreamPrintIcmpConfig(StreamIcmpPolicy * s5IcmpPolicy)168 static void StreamPrintIcmpConfig(StreamIcmpPolicy *s5IcmpPolicy)
169 {
170     LogMessage("Stream ICMP Policy config:\n");
171     LogMessage("    Timeout: %d seconds\n", s5IcmpPolicy->session_timeout);
172     //LogMessage("    Flags: 0x%X\n", s5UdpPolicy->flags);
173     //IpAddrSetPrint("    Bound Addresses:", s5UdpPolicy->bound_addrs);
174 
175 }
176 
IcmpSessionCleanup(SessionControlBlock * ssn)177 void IcmpSessionCleanup(SessionControlBlock *ssn)
178 {
179     IcmpSession *icmpssn = NULL;
180 
181     if (ssn->ha_state.session_flags & SSNFLAG_PRUNED)
182     {
183         CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED);
184     }
185     else if (ssn->ha_state.session_flags & SSNFLAG_TIMEDOUT)
186     {
187         CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT);
188     }
189     else
190     {
191         CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY);
192     }
193 
194     if (ssn->proto_specific_data)
195         icmpssn = ssn->proto_specific_data->data;
196 
197     if (!icmpssn)
198     {
199         /* Huh? */
200         return;
201     }
202 
203     /* Cleanup the proto specific data */
204     session_api->free_protocol_session_pool( SESSION_PROTO_ICMP, ssn );
205     ssn->proto_specific_data = NULL;
206 
207     StreamResetFlowBits(ssn);
208     session_api->free_application_data(ssn);
209 
210     s5stats.icmp_sessions_released++;
211     s5stats.active_icmp_sessions--;
212 }
213 
StreamGetIcmpPrunes(void)214 uint32_t StreamGetIcmpPrunes(void)
215 {
216     if( icmp_lws_cache )
217         return session_api->get_session_prune_count( SESSION_PROTO_ICMP );
218     else
219         return s5stats.icmp_prunes;
220 }
221 
StreamResetIcmpPrunes(void)222 void StreamResetIcmpPrunes(void)
223 {
224     session_api->reset_session_prune_count( SESSION_PROTO_ICMP );
225 }
226 
StreamResetIcmp(void)227 void StreamResetIcmp(void)
228 {
229     session_api->purge_session_cache(icmp_lws_cache);
230     session_api->clean_protocol_session_pool( SESSION_PROTO_ICMP );
231 }
232 
StreamCleanIcmp(void)233 void StreamCleanIcmp(void)
234 {
235     if ( icmp_lws_cache )
236         s5stats.icmp_prunes = session_api->get_session_prune_count( SESSION_PROTO_ICMP );
237 
238     /* Clean up session cache */
239     session_api->delete_session_cache( SESSION_PROTO_ICMP );
240     icmp_lws_cache = NULL;
241 }
242 
StreamIcmpConfigFree(StreamIcmpConfig * config)243 void StreamIcmpConfigFree(StreamIcmpConfig *config)
244 {
245     if (config == NULL)
246         return;
247 
248     SnortPreprocFree(config, sizeof(StreamIcmpConfig),
249                      PP_STREAM, PP_MEM_CATEGORY_CONFIG);
250 }
251 
StreamVerifyIcmpConfig(StreamIcmpConfig * config,tSfPolicyId policy_id)252 int StreamVerifyIcmpConfig(StreamIcmpConfig *config, tSfPolicyId policy_id)
253 {
254     if (config == NULL)
255         return -1;
256 
257     if (!icmp_lws_cache)
258         return -1;
259 
260     if (config->num_policies == 0)
261         return -1;
262 
263     return 0;
264 }
265 
StreamProcessIcmp(Packet * p)266 int StreamProcessIcmp(Packet *p)
267 {
268     int status = 0;
269 
270     switch (p->icmph->type)
271     {
272     case ICMP_DEST_UNREACH:
273         s5stats.icmp_unreachable++;
274         if (p->icmph->code != ICMP_FRAG_NEEDED)
275         {
276             status = ProcessIcmpUnreach(p);
277         }
278         else
279         {
280             s5stats.icmp_unreachable_code4++;
281         }
282         break;
283 
284     case ICMP_ECHO:
285     case ICMP_ECHOREPLY:
286         status = ProcessIcmpEcho(p);
287         break;
288 
289     default:
290         /* We only handle the above ICMP messages with stream5 */
291         break;
292     }
293 
294     return status;
295 }
296 
ProcessIcmpUnreach(Packet * p)297 static int ProcessIcmpUnreach(Packet *p)
298 {
299     /* Handle ICMP unreachable */
300     SessionKey skey;
301     SessionControlBlock *ssn = NULL;
302     uint16_t sport;
303     uint16_t dport;
304     sfaddr_t *src;
305     sfaddr_t *dst;
306 
307     /* No "orig" IP Header */
308     if (!p->orig_iph)
309         return 0;
310 
311     /* Get TCP/UDP/ICMP session from original protocol/port info
312      * embedded in the ICMP Unreach message.  This is already decoded
313      * in p->orig_foo.  TCP/UDP ports are decoded as p->orig_sp/dp.
314      */
315     skey.protocol = GET_ORIG_IPH_PROTO(p);
316     sport = p->orig_sp;
317     dport = p->orig_dp;
318 
319     src = GET_ORIG_SRC(p);
320     dst = GET_ORIG_DST(p);
321 
322     if (sfip_fast_lt6(src, dst))
323     {
324         COPY4(skey.ip_l, sfaddr_get_ip6_ptr(src));
325         skey.port_l = sport;
326         COPY4(skey.ip_h, sfaddr_get_ip6_ptr(dst));
327         skey.port_h = dport;
328     }
329     else if (IP_EQUALITY(src, dst))
330     {
331         COPY4(skey.ip_l, sfaddr_get_ip6_ptr(src));
332         COPY4(skey.ip_h, skey.ip_l);
333         if (sport < dport)
334         {
335             skey.port_l = sport;
336             skey.port_h = dport;
337         }
338         else
339         {
340             skey.port_l = dport;
341             skey.port_h = sport;
342         }
343     }
344     else
345     {
346         COPY4(skey.ip_l, sfaddr_get_ip6_ptr(dst));
347         COPY4(skey.ip_h, sfaddr_get_ip6_ptr(src));
348         skey.port_l = dport;
349         skey.port_h = sport;
350     }
351 
352     if (p->vh)
353         skey.vlan_tag = (uint16_t)VTH_VLAN(p->vh);
354     else
355         skey.vlan_tag = 0;
356 
357     switch (skey.protocol)
358     {
359     case IPPROTO_TCP:
360         /* Lookup a TCP session */
361         ssn = GetLWTcpSession(&skey);
362         break;
363     case IPPROTO_UDP:
364         /* Lookup a UDP session */
365         ssn = GetLWUdpSession(&skey);
366         break;
367     case IPPROTO_ICMP:
368         /* Lookup a ICMP session */
369         ssn = session_api->get_session_by_key(icmp_lws_cache, &skey);
370         break;
371     }
372 
373     if (ssn)
374     {
375         /* Mark this session as dead. */
376         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
377             "Marking session as dead, per ICMP Unreachable!\n"););
378         ssn->ha_state.session_flags |= SSNFLAG_DROP_CLIENT;
379         ssn->ha_state.session_flags |= SSNFLAG_DROP_SERVER;
380         ssn->session_state |= STREAM_STATE_UNREACH;
381         s5stats.active_icmp_sessions++;
382     }
383 
384     return 0;
385 }
386 
ProcessIcmpEcho(Packet * p)387 static int ProcessIcmpEcho(Packet *p)
388 {
389     //SessionKey skey;
390     //SessionControlBlock *ssn = NULL;
391     s5stats.active_icmp_sessions++;
392 
393     return 0;
394 }
395 
IcmpUpdateDirection(SessionControlBlock * ssn,char dir,sfaddr_t * ip,uint16_t port)396 void IcmpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port)
397 {
398     IcmpSession *icmpssn = ssn->proto_specific_data->data;
399     sfaddr_t tmpIp;
400 
401     if (!icmpssn)
402     {
403         /* Huh? */
404         return;
405     }
406 
407     if (IP_EQUALITY(&icmpssn->icmp_sender_ip, ip))
408     {
409         if ((dir == SSN_DIR_FROM_SENDER) && (ssn->ha_state.direction == SSN_DIR_FROM_SENDER))
410         {
411             /* Direction already set as SENDER */
412             return;
413         }
414     }
415     else if (IP_EQUALITY(&icmpssn->icmp_responder_ip, ip))
416     {
417         if ((dir == SSN_DIR_FROM_RESPONDER) && (ssn->ha_state.direction == SSN_DIR_FROM_RESPONDER))
418         {
419             /* Direction already set as RESPONDER */
420             return;
421         }
422     }
423 
424     /* Swap them -- leave ssn->ha_state.direction the same */
425     tmpIp = icmpssn->icmp_sender_ip;
426     icmpssn->icmp_sender_ip = icmpssn->icmp_responder_ip;
427     icmpssn->icmp_responder_ip = tmpIp;
428 }
429 
430 #ifdef SNORT_RELOAD
SessionICMPReload(uint32_t max_sessions,uint16_t pruningTimeout,uint16_t nominalTimeout)431 void SessionICMPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout)
432 {
433     SessionReload(icmp_lws_cache, max_sessions, pruningTimeout, nominalTimeout
434 #ifdef REG_TEST
435                   , "ICMP"
436 #endif
437                   );
438 }
439 
SessionICMPReloadAdjust(unsigned maxWork)440 unsigned SessionICMPReloadAdjust(unsigned maxWork)
441 {
442     return SessionProtocolReloadAdjust(icmp_lws_cache, session_configuration->max_icmp_sessions,
443                                        maxWork, 0
444 #ifdef REG_TEST
445                                        , "ICMP"
446 #endif
447                                        );
448 }
449 #endif
450 
get_icmp_used_mempool()451 size_t get_icmp_used_mempool()
452 {
453     if (icmp_lws_cache && icmp_lws_cache->protocol_session_pool)
454         return icmp_lws_cache->protocol_session_pool->used_memory;
455 
456     return 0;
457 }
458