1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License Version 2 as
4  * published by the Free Software Foundation.  You may not use, modify or
5  * distribute this program under any other version of the GNU General
6  * Public License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  * Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
18  *
19  * Authors: Jeffrey Gu <jgu@cisco.com>, Pradeep Damodharan <prdamodh@cisco.com>
20  *
21  * Dynamic preprocessor for the S7commplus protocol
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif /* HAVE_CONFIG_H */
28 
29 #include <assert.h>
30 #include <string.h>
31 
32 #include "sf_types.h"
33 #include "sf_snort_packet.h"
34 #include "sf_dynamic_preprocessor.h"
35 #include "sf_snort_plugin_api.h"
36 #include "snort_debug.h"
37 
38 #include "preprocids.h"
39 #include "spp_s7comm.h"
40 #include "sf_preproc_info.h"
41 
42 #include "profiler.h"
43 #ifdef PERF_PROFILING
44 PreprocStats s7commplusPerfStats;
45 #endif
46 
47 #include "sf_types.h"
48 #include "s7comm_decode.h"
49 #include "s7comm_roptions.h"
50 #include "s7comm_paf.h"
51 
52 const int MAJOR_VERSION = 1;
53 const int MINOR_VERSION = 0;
54 const int BUILD_VERSION = 1;
55 const char *PREPROC_NAME = "SF_S7COMMPLUS";
56 
57 #define SetupS7commplus DYNAMIC_PREPROC_SETUP
58 
59 /* Preprocessor config objects */
60 static tSfPolicyUserContextId s7commplus_context_id = NULL;
61 static s7commplus_config_t *s7commplus_eval_config = NULL;
62 
63 /* Target-based app ID */
64 #ifdef TARGET_BASED
65 int16_t s7commplus_app_id = SFTARGET_UNKNOWN_PROTOCOL;
66 #endif
67 
68 /* Prototypes */
69 static void S7commplusInit(struct _SnortConfig *, char *);
70 static inline void S7commplusOneTimeInit(struct _SnortConfig *);
71 static inline s7commplus_config_t * S7commplusPerPolicyInit(struct _SnortConfig *, tSfPolicyUserContextId);
72 
73 static void ProcessS7commplus(void *, void *);
74 
75 #ifdef SNORT_RELOAD
76 static void S7commplusReload(struct _SnortConfig *, char *, void **);
77 static int S7commplusReloadVerify(struct _SnortConfig *, void *);
78 static void * S7commplusReloadSwap(struct _SnortConfig *, void *);
79 static void S7commplusReloadSwapFree(void *);
80 #endif
81 
82 static void registerPortsForDispatch( struct _SnortConfig *sc, s7commplus_config_t *policy );
83 static void registerPortsForReassembly( s7commplus_config_t *policy, int direction );
84 static void _addPortsToStreamFilter(struct _SnortConfig *, s7commplus_config_t *, tSfPolicyId);
85 #ifdef TARGET_BASED
86 static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId);
87 #endif
88 
89 static void S7commplusFreeConfig(tSfPolicyUserContextId context_id);
90 static void FreeS7commplusData(void *);
91 static int S7commplusCheckConfig(struct _SnortConfig *);
92 static void S7commplusCleanExit(int, void *);
93 
94 static void ParseS7commplusArgs(s7commplus_config_t *config, char *args);
95 static void S7commplusPrintConfig(s7commplus_config_t *config);
96 
97 static int S7commplusPortCheck(s7commplus_config_t *config, SFSnortPacket *packet);
98 static s7commplus_session_data_t * S7commplusCreateSessionData(SFSnortPacket *);
99 
100 /* Register init callback */
SetupS7commplus(void)101 void SetupS7commplus(void)
102 {
103 #ifndef SNORT_RELOAD
104     _dpd.registerPreproc("s7commplus", S7commplusInit);
105 #else
106     _dpd.registerPreproc("s7commplus", S7commplusInit, S7commplusReload,
107                          S7commplusReloadVerify, S7commplusReloadSwap,
108                          S7commplusReloadSwapFree);
109 #endif
110 }
111 
112 /* Allocate memory for preprocessor config, parse the args, set up callbacks */
S7commplusInit(struct _SnortConfig * sc,char * argp)113 static void S7commplusInit(struct _SnortConfig *sc, char *argp)
114 {
115     s7commplus_config_t *s7commplus_policy = NULL;
116 
117     if (s7commplus_context_id == NULL)
118     {
119         S7commplusOneTimeInit(sc);
120     }
121 
122     s7commplus_policy = S7commplusPerPolicyInit(sc, s7commplus_context_id);
123 
124     ParseS7commplusArgs(s7commplus_policy, argp);
125 
126     /* Can't add ports until they've been parsed... */
127     S7commplusAddPortsToPaf(sc, s7commplus_policy, _dpd.getParserPolicy(sc));
128 #ifdef TARGET_BASED
129     S7commplusAddServiceToPaf(sc, s7commplus_app_id, _dpd.getParserPolicy(sc));
130 #endif
131     // register ports with session and stream
132     registerPortsForDispatch(sc, s7commplus_policy );
133     registerPortsForReassembly( s7commplus_policy, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT );
134 
135     S7commplusPrintConfig(s7commplus_policy);
136 }
137 
S7commplusOneTimeInit(struct _SnortConfig * sc)138 static inline void S7commplusOneTimeInit(struct _SnortConfig *sc)
139 {
140     /* context creation & error checking */
141     s7commplus_context_id = sfPolicyConfigCreate();
142     if (s7commplus_context_id == NULL)
143     {
144         _dpd.fatalMsg("%s(%d) Failed to allocate memory for "
145                       "S7commplus config.\n", *_dpd.config_file, *_dpd.config_line);
146     }
147 
148     if (_dpd.streamAPI == NULL)
149     {
150         _dpd.fatalMsg("%s(%d) SetupS7commplus(): The Stream preprocessor "
151                       "must be enabled.\n", *_dpd.config_file, *_dpd.config_line);
152     }
153 
154     /* callback registration */
155     _dpd.addPreprocConfCheck(sc, S7commplusCheckConfig);
156     _dpd.addPreprocExit(S7commplusCleanExit, NULL, PRIORITY_LAST, PP_S7COMMPLUS);
157 
158 #ifdef PERF_PROFILING
159     _dpd.addPreprocProfileFunc("s7commplus", (void *)&s7commplusPerfStats, 0, _dpd.totalPerfStats, NULL);
160 #endif
161 
162     /* Set up target-based app id */
163 #ifdef TARGET_BASED
164     s7commplus_app_id = _dpd.findProtocolReference("cotp");
165     if (s7commplus_app_id == SFTARGET_UNKNOWN_PROTOCOL)
166         s7commplus_app_id = _dpd.addProtocolReference("s7commplus");
167 
168 // register with session to handle applications
169     _dpd.sessionAPI->register_service_handler( PP_S7COMMPLUS, s7commplus_app_id );
170 
171 #endif
172 }
173 
174 /* Responsible for allocating a S7commplus policy. Never returns NULL. */
S7commplusPerPolicyInit(struct _SnortConfig * sc,tSfPolicyUserContextId context_id)175 static inline s7commplus_config_t * S7commplusPerPolicyInit(struct _SnortConfig *sc, tSfPolicyUserContextId context_id)
176 {
177     tSfPolicyId policy_id = _dpd.getParserPolicy(sc);
178     s7commplus_config_t *s7commplus_policy = NULL;
179 
180     /* Check for existing policy & bail if found */
181     sfPolicyUserPolicySet(context_id, policy_id);
182     s7commplus_policy = (s7commplus_config_t *)sfPolicyUserDataGetCurrent(context_id);
183     if (s7commplus_policy != NULL)
184     {
185         _dpd.fatalMsg("%s(%d) S7commplus preprocessor can only be "
186                       "configured once.\n", *_dpd.config_file, *_dpd.config_line);
187     }
188 
189     /* Allocate new policy */
190     s7commplus_policy = (s7commplus_config_t *)calloc(1, sizeof(s7commplus_config_t));
191     if (!s7commplus_policy)
192     {
193         _dpd.fatalMsg("%s(%d) Could not allocate memory for "
194                       "s7commplus preprocessor configuration.\n"
195                       , *_dpd.config_file, *_dpd.config_line);
196     }
197 
198     sfPolicyUserDataSetCurrent(context_id, s7commplus_policy);
199 
200     /* Register callbacks that are done for each policy */
201     _dpd.addPreproc(sc, ProcessS7commplus, PRIORITY_APPLICATION, PP_S7COMMPLUS, PROTO_BIT__TCP);
202    _addPortsToStreamFilter(sc, s7commplus_policy, policy_id);
203 #ifdef TARGET_BASED
204     _addServicesToStreamFilter(sc, policy_id);
205 #endif
206 
207     /* Add preprocessor rule options here */
208     _dpd.preprocOptRegister(sc, S7COMMPLUS_OPCODE_NAME, S7commplusOpcodeInit, S7commplusRuleEval, free, NULL, NULL, NULL, NULL);
209     _dpd.preprocOptRegister(sc, S7COMMPLUS_FUNC_NAME, S7commplusFuncInit, S7commplusRuleEval, free, NULL, NULL, NULL, NULL);
210     _dpd.preprocOptRegister(sc, S7COMMPLUS_CONTENT_NAME, S7commplusContentInit, S7commplusRuleEval, free, NULL, NULL, NULL, NULL);
211 
212     return s7commplus_policy;
213 }
214 
ParseSinglePort(s7commplus_config_t * config,char * token)215 static void ParseSinglePort(s7commplus_config_t *config, char *token)
216 {
217     /* single port number */
218     char *endptr;
219     unsigned long portnum = _dpd.SnortStrtoul(token, &endptr, 10);
220 
221     if ((*endptr != '\0') || (portnum >= MAX_PORTS))
222     {
223         _dpd.fatalMsg("%s(%d) Bad s7commplus port number: %s\n"
224                       "Port number must be an integer between 0 and 65535.\n",
225                       *_dpd.config_file, *_dpd.config_line, token);
226     }
227 
228     /* Good port number! */
229     config->ports[PORT_INDEX(portnum)] |= CONV_PORT(portnum);
230 }
231 
ParseS7commplusArgs(s7commplus_config_t * config,char * args)232 static void ParseS7commplusArgs(s7commplus_config_t *config, char *args)
233 {
234     char *saveptr;
235     char *token;
236 
237     /* Set default port */
238     config->ports[PORT_INDEX(S7COMMPLUS_PORT)] |= CONV_PORT(S7COMMPLUS_PORT);
239 
240     /* No args? Stick to the default. */
241     if (args == NULL)
242         return;
243 
244     token = strtok_r(args, " ", &saveptr);
245     while (token != NULL)
246     {
247         if (strcmp(token, "ports") == 0)
248         {
249             unsigned nPorts = 0;
250 
251             /* Un-set the default port */
252             config->ports[PORT_INDEX(S7COMMPLUS_PORT)] = 0;
253 
254             /* Parse ports */
255             token = strtok_r(NULL, " ", &saveptr);
256 
257             if (token == NULL)
258             {
259                 _dpd.fatalMsg("%s(%d) Missing argument for S7commplus preprocessor "
260                               "'ports' option.\n", *_dpd.config_file, *_dpd.config_line);
261             }
262 
263             if (isdigit(token[0]))
264             {
265                 ParseSinglePort(config, token);
266                 nPorts++;
267             }
268 
269             else if (*token == '{')
270             {
271                 /* list of ports */
272                 token = strtok_r(NULL, " ", &saveptr);
273                 while (token != NULL && *token != '}')
274                 {
275                     ParseSinglePort(config, token);
276                     nPorts++;
277                     token = strtok_r(NULL, " ", &saveptr);
278                 }
279             }
280 
281             else
282             {
283                 nPorts = 0;
284             }
285             if ( nPorts == 0 )
286             {
287                 _dpd.fatalMsg("%s(%d) Bad S7commplus 'ports' argument: '%s'\n"
288                               "Argument to S7commplus 'ports' must be an integer, or a list "
289                               "enclosed in { } braces.\n", *_dpd.config_file, *_dpd.config_line, token);
290             }
291         }
292         else
293         {
294             _dpd.fatalMsg("%s(%d) Failed to parse s7commplus argument: %s\n",
295                           *_dpd.config_file, *_dpd.config_line, token);
296         }
297 
298         token = strtok_r(NULL, " ", &saveptr);
299     }
300 
301 }
302 
303 /* Print a S7commplus config */
S7commplusPrintConfig(s7commplus_config_t * config)304 static void S7commplusPrintConfig(s7commplus_config_t *config)
305 {
306     int index;
307     int newline = 1;
308 
309     if (config == NULL)
310         return;
311 
312     _dpd.logMsg("S7commplus config: \n");
313     _dpd.logMsg("    Ports:\n");
314 
315     /* Loop through port array & print, 5 ports per line */
316     for (index = 0; index < MAX_PORTS; index++)
317     {
318         if (config->ports[PORT_INDEX(index)] & CONV_PORT(index))
319         {
320             _dpd.logMsg("\t%d", index);
321             if ( !((newline++) % 5) )
322             {
323                 _dpd.logMsg("\n");
324             }
325         }
326     }
327     _dpd.logMsg("\n");
328 }
329 
330 /* Main runtime entry point */
ProcessS7commplus(void * ipacketp,void * contextp)331 static void ProcessS7commplus(void *ipacketp, void *contextp)
332 {
333     SFSnortPacket *packetp = (SFSnortPacket *)ipacketp;
334     s7commplus_session_data_t *sessp;
335     PROFILE_VARS;
336 
337     // preconditions - what we registered for
338     assert(IsTCP(packetp) && packetp->payload && packetp->payload_size);
339 
340     PREPROC_PROFILE_START(s7commplusPerfStats);
341 
342     /* Fetch me a preprocessor config to use with this VLAN/subnet/etc.! */
343     s7commplus_eval_config = sfPolicyUserDataGetCurrent(s7commplus_context_id);
344 
345     /* Look for a previously-allocated session data. */
346     sessp = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_S7COMMPLUS);
347 
348     if (sessp == NULL)
349     {
350         /* No existing session. Check those ports. */
351         if (S7commplusPortCheck(s7commplus_eval_config, packetp) != true)
352         {
353             PREPROC_PROFILE_END(s7commplusPerfStats);
354             return;
355         }
356     }
357 
358     if ( !PacketHasFullPDU(packetp) && S7commplusIsPafActive(packetp) )
359     {
360         /* If a packet is rebuilt, but not a full PDU, then it's garbage that
361            got flushed at the end of a stream. */
362         if ( packetp->flags & (FLAG_REBUILT_STREAM|FLAG_PDU_HEAD) )
363         {
364             _dpd.alertAdd(GENERATOR_SPP_S7COMMPLUS, S7COMMPLUS_BAD_LENGTH, 1, 0, 3,
365                           S7COMMPLUS_BAD_LENGTH_STR, 0);
366         }
367 
368         PREPROC_PROFILE_END(s7commplusPerfStats);
369         return;
370     }
371 
372     if (sessp == NULL)
373     {
374         /* Create session data and attach it to the Stream session */
375         sessp = S7commplusCreateSessionData(packetp);
376 
377         if ( !sessp )
378         {
379             PREPROC_PROFILE_END(s7commplusPerfStats);
380             return;
381         }
382     }
383 
384     /* When pipelined S7commplus PDUs appear in a single TCP segment, the
385        detection engine caches the results of the rule options after
386        evaluating on the first PDU. Setting this flag stops the caching. */
387     packetp->flags |= FLAG_ALLOW_MULTIPLE_DETECT;
388 
389     /* Do preprocessor-specific detection stuff here */
390     S7commplusDecode(s7commplus_eval_config, packetp);
391 
392     /* That's the end! */
393     PREPROC_PROFILE_END(s7commplusPerfStats);
394 }
395 
396 /* Check ports & services */
S7commplusPortCheck(s7commplus_config_t * config,SFSnortPacket * packet)397 static int S7commplusPortCheck(s7commplus_config_t *config, SFSnortPacket *packet)
398 {
399 #ifdef TARGET_BASED
400     int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session);
401 
402     /* call to get_application_protocol_id gave an error */
403     if (app_id == SFTARGET_UNKNOWN_PROTOCOL)
404         return false;
405 
406     /* this is identified as non-s7commplus */
407     if (app_id && (app_id != s7commplus_app_id))
408         return false;
409 
410     /* this is identified as s7commplus */
411     if (app_id == s7commplus_app_id)
412         return true;
413 
414     /* fall back to port check */
415 #endif
416 
417     if (config->ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port))
418         return true;
419 
420     if (config->ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port))
421         return true;
422 
423     return false;
424 }
425 
S7commplusCreateSessionData(SFSnortPacket * packet)426 static s7commplus_session_data_t* S7commplusCreateSessionData(SFSnortPacket *packet)
427 {
428     s7commplus_session_data_t *data = NULL;
429 
430     /* Sanity Check */
431     if (!packet || !packet->stream_session)
432         return NULL;
433 
434     data = (s7commplus_session_data_t *)calloc(1, sizeof(s7commplus_session_data_t));
435 
436     if (!data)
437         return NULL;
438 
439     /* Attach to Stream session */
440     _dpd.sessionAPI->set_application_data(packet->stream_session, PP_S7COMMPLUS,
441         data, FreeS7commplusData);
442 
443     /* This reference counting stuff got from old preprocs */
444     data->policy_id = _dpd.getNapRuntimePolicy();
445     data->context_id = s7commplus_context_id;
446     ((s7commplus_config_t *)sfPolicyUserDataGetCurrent(s7commplus_context_id))->ref_count++;
447 
448     return data;
449 }
450 
451 /* Reload functions */
452 #ifdef SNORT_RELOAD
453 /* Almost like S7commplusInit, but not quite. */
S7commplusReload(struct _SnortConfig * sc,char * args,void ** new_config)454 static void S7commplusReload(struct _SnortConfig *sc, char *args, void **new_config)
455 {
456     tSfPolicyUserContextId s7commplus_swap_context_id = (tSfPolicyUserContextId)*new_config;
457     s7commplus_config_t *s7commplus_policy = NULL;
458 
459     if (s7commplus_swap_context_id == NULL)
460     {
461         s7commplus_swap_context_id = sfPolicyConfigCreate();
462         if (s7commplus_swap_context_id == NULL)
463         {
464             _dpd.fatalMsg("Failed to allocate memory "
465                                             "for S7commplus config.\n");
466         }
467 
468         if (_dpd.streamAPI == NULL)
469         {
470             _dpd.fatalMsg("SetupS7commplus(): The Stream preprocessor "
471                                             "must be enabled.\n");
472         }
473         *new_config = (void *)s7commplus_swap_context_id;
474     }
475 
476     s7commplus_policy = S7commplusPerPolicyInit(sc, s7commplus_swap_context_id);
477 
478     ParseS7commplusArgs(s7commplus_policy, args);
479 
480     /* Can't add ports until they've been parsed... */
481     S7commplusAddPortsToPaf(sc, s7commplus_policy, _dpd.getParserPolicy(sc));
482 
483     S7commplusPrintConfig(s7commplus_policy);
484 }
485 
S7commplusReloadVerify(struct _SnortConfig * sc,void * swap_config)486 static int S7commplusReloadVerify(struct _SnortConfig *sc, void *swap_config)
487 {
488     if (!_dpd.isPreprocEnabled(sc, PP_STREAM))
489     {
490         _dpd.errMsg("SetupS7commplus(): The Stream preprocessor must be enabled.\n");
491         return -1;
492     }
493 
494     return 0;
495 }
496 
S7commplusFreeUnusedConfigPolicy(tSfPolicyUserContextId context_id,tSfPolicyId policy_id,void * data)497 static int S7commplusFreeUnusedConfigPolicy(
498     tSfPolicyUserContextId context_id,
499     tSfPolicyId policy_id,
500     void *data
501     )
502 {
503     s7commplus_config_t *s7commplus_config = (s7commplus_config_t *)data;
504 
505     /* do any housekeeping before freeing s7commplus config */
506     if (s7commplus_config->ref_count == 0)
507     {
508         sfPolicyUserDataClear(context_id, policy_id);
509         free(s7commplus_config);
510     }
511 
512     return 0;
513 }
514 
S7commplusReloadSwap(struct _SnortConfig * sc,void * swap_config)515 static void * S7commplusReloadSwap(struct _SnortConfig *sc, void *swap_config)
516 {
517     tSfPolicyUserContextId s7commplus_swap_context_id = (tSfPolicyUserContextId)swap_config;
518     tSfPolicyUserContextId old_context_id = s7commplus_context_id;
519 
520     if (s7commplus_swap_context_id == NULL)
521         return NULL;
522 
523     s7commplus_context_id = s7commplus_swap_context_id;
524 
525     sfPolicyUserDataFreeIterate(old_context_id, S7commplusFreeUnusedConfigPolicy);
526 
527     if (sfPolicyUserPolicyGetActive(old_context_id) == 0)
528     {
529         /* No more outstanding configs - free the config array */
530         return (void *)old_context_id;
531     }
532 
533     return NULL;
534 }
535 
S7commplusReloadSwapFree(void * data)536 static void S7commplusReloadSwapFree(void *data)
537 {
538     if (data == NULL)
539         return;
540 
541     S7commplusFreeConfig( (tSfPolicyUserContextId)data );
542 }
543 #endif   //Reload functions ends here
544 
registerPortsForDispatch(struct _SnortConfig * sc,s7commplus_config_t * policy)545 static void registerPortsForDispatch( struct _SnortConfig *sc, s7commplus_config_t *policy )
546 {
547     uint32_t port;
548 
549     for ( port = 0; port < MAX_PORTS; port++ )
550     {
551         if( isPortEnabled( policy->ports, port ) )
552             _dpd.sessionAPI->enable_preproc_for_port( sc, PP_S7COMMPLUS, PROTO_BIT__TCP, port );
553     }
554 }
555 
registerPortsForReassembly(s7commplus_config_t * policy,int direction)556 static void registerPortsForReassembly( s7commplus_config_t *policy, int direction )
557 {
558     uint32_t port;
559 
560     for ( port = 0; port < MAX_PORTS; port++ )
561     {
562         if( isPortEnabled( policy->ports, port ) )
563             _dpd.streamAPI->register_reassembly_port( NULL, port, direction );
564     }
565 }
566 
567 /* Stream filter functions */
_addPortsToStreamFilter(struct _SnortConfig * sc,s7commplus_config_t * config,tSfPolicyId policy_id)568 static void _addPortsToStreamFilter(struct _SnortConfig *sc, s7commplus_config_t *config, tSfPolicyId policy_id)
569 {
570     if (config == NULL)
571         return;
572 
573     if (_dpd.streamAPI)
574     {
575         int portNum;
576 
577         for (portNum = 0; portNum < MAX_PORTS; portNum++)
578         {
579             if(config->ports[(portNum/8)] & (1<<(portNum%8)))
580             {
581                 //Add port the port
582                 _dpd.streamAPI->set_port_filter_status( sc, IPPROTO_TCP, (uint16_t)portNum,
583                                                         PORT_MONITOR_SESSION, policy_id, 1 );
584             }
585         }
586     }
587 
588 }
589 
590 #ifdef TARGET_BASED
_addServicesToStreamFilter(struct _SnortConfig * sc,tSfPolicyId policy_id)591 static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id)
592 {
593     _dpd.streamAPI->set_service_filter_status(sc, s7commplus_app_id, PORT_MONITOR_SESSION, policy_id, 1);
594 }
595 #endif
596 
S7commplusFreeConfigPolicy(tSfPolicyUserContextId context_id,tSfPolicyId policy_id,void * data)597 static int S7commplusFreeConfigPolicy(
598     tSfPolicyUserContextId context_id,
599     tSfPolicyId policy_id,
600     void *data
601     )
602 {
603     s7commplus_config_t *s7commplus_config = (s7commplus_config_t *)data;
604 
605     /* do any housekeeping before freeing s7commplus_config */
606 
607     sfPolicyUserDataClear(context_id, policy_id);
608     free(s7commplus_config);
609     return 0;
610 }
611 
S7commplusFreeConfig(tSfPolicyUserContextId context_id)612 static void S7commplusFreeConfig(tSfPolicyUserContextId context_id)
613 {
614     if (context_id == NULL)
615         return;
616 
617     sfPolicyUserDataFreeIterate(context_id, S7commplusFreeConfigPolicy);
618     sfPolicyConfigDelete(context_id);
619 }
620 
S7commplusCheckPolicyConfig(struct _SnortConfig * sc,tSfPolicyUserContextId context_id,tSfPolicyId policy_id,void * data)621 static int S7commplusCheckPolicyConfig(
622     struct _SnortConfig *sc,
623     tSfPolicyUserContextId context_id,
624     tSfPolicyId policy_id,
625     void *data
626     )
627 {
628     _dpd.setParserPolicy(sc, policy_id);
629 
630     if (!_dpd.isPreprocEnabled(sc, PP_STREAM))
631     {
632         _dpd.errMsg("%s(%d) S7commplusCheckPolicyConfig(): The Stream preprocessor "
633                       "must be enabled.\n", *_dpd.config_file, *_dpd.config_line);
634         return -1;
635     }
636     return 0;
637 }
638 
S7commplusCheckConfig(struct _SnortConfig * sc)639 static int S7commplusCheckConfig(struct _SnortConfig *sc)
640 {
641     int rval;
642 
643     if ((rval = sfPolicyUserDataIterate(sc, s7commplus_context_id, S7commplusCheckPolicyConfig)))
644         return rval;
645 
646     return 0;
647 }
648 
S7commplusCleanExit(int signal,void * data)649 static void S7commplusCleanExit(int signal, void *data)
650 {
651     if (s7commplus_context_id != NULL)
652     {
653         S7commplusFreeConfig(s7commplus_context_id);
654         s7commplus_context_id = NULL;
655     }
656 }
657 
FreeS7commplusData(void * data)658 static void FreeS7commplusData(void *data)
659 {
660     s7commplus_session_data_t *session = (s7commplus_session_data_t *)data;
661     s7commplus_config_t *config = NULL;
662 
663     if (session == NULL)
664         return;
665 
666     if (session->context_id != NULL)
667     {
668         config = (s7commplus_config_t *)sfPolicyUserDataGet(session->context_id, session->policy_id);
669     }
670 
671     if (config != NULL)
672     {
673         config->ref_count--;
674         if ((config->ref_count == 0) &&
675             (session->context_id != s7commplus_context_id))
676         {
677             sfPolicyUserDataClear(session->context_id, session->policy_id);
678             free(config);
679 
680             if (sfPolicyUserPolicyGetActive(session->context_id) == 0)
681             {
682                 /* No more outstanding configs - free the config array */
683                 S7commplusFreeConfig(session->context_id);
684             }
685         }
686     }
687     free(session);
688 }
689