suricata
app-layer-expectation.c
Go to the documentation of this file.
1 /* Copyright (C) 2017-2021 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \defgroup applayerexpectation Application Layer Expectation
20  *
21  * Handling of dynamic parallel connection for application layer similar
22  * to FTP.
23  *
24  * @{
25  *
26  * Some protocols like FTP create dynamic parallel flow (called expectation). In
27  * order to assign a application layer protocol to these expectation, Suricata
28  * needs to parse message of the initial protocol and create and maintain a list
29  * of expected flow.
30  *
31  * Application layers must use the here described API to implement this mechanism.
32  *
33  * When parsing a application layer message describing a parallel flow, the
34  * application layer can call AppLayerExpectationCreate() to declare an
35  * expectation. By doing that the next flow coming with corresponding IP parameters
36  * will be assigned the specified application layer. The resulting Flow will
37  * also have a Flow storage set that can be retrieved at index
38  * AppLayerExpectationGetDataId():
39  *
40  * ```
41  * data = (char *)SCFlowGetStorageById(f, AppLayerExpectationGetFlowId());
42  * ```
43  * This storage can be used to store information that are only available in the
44  * parent connection and could be useful in the parent connection. For instance
45  * this is used by the FTP protocol to propagate information such as file name
46  * and ftp operation to the FTP data connection.
47  */
48 
49 /**
50  * \file
51  *
52  * \author Eric Leblond <eric@regit.org>
53  */
54 
55 #include "queue.h"
56 #include "suricata-common.h"
57 
58 #include "ippair-storage.h"
59 #include "flow-storage.h"
60 
61 #include "app-layer-expectation.h"
62 
63 #include "util-print.h"
64 
65 static SCIPPairStorageId g_ippair_expectation_id = { .id = -1 };
66 static SCFlowStorageId g_flow_expectation_id = { .id = -1 };
67 
68 SC_ATOMIC_DECLARE(uint32_t, expectation_count);
69 
70 #define EXPECTATION_TIMEOUT 30
71 #define EXPECTATION_MAX_LEVEL 10
72 
73 typedef struct Expectation_ {
78  int direction;
79  /* use pointer to Flow as identifier of the Flow the expectation is linked to */
80  void *orig_f;
81  void *data;
82  CIRCLEQ_ENTRY(Expectation_) entries;
84 
85 typedef struct ExpectationData_ {
86  /** Start of Expectation Data structure must be a pointer
87  * to free function. Set to NULL to use SCFree() */
88  void (*DFree)(void *);
90 
91 typedef struct ExpectationList_ {
92  CIRCLEQ_HEAD(EList, Expectation_) list;
93  uint8_t length;
95 
96 static void ExpectationDataFree(void *e)
97 {
98  SCLogDebug("Free expectation data");
99  ExpectationData *ed = (ExpectationData *) e;
100  if (ed->DFree) {
101  ed->DFree(e);
102  } else {
103  SCFree(e);
104  }
105 }
106 
107 /**
108  * Free expectation
109  */
110 static void AppLayerFreeExpectation(Expectation *exp)
111 {
112  if (exp->data) {
113  ExpectationData *expdata = (ExpectationData *)exp->data;
114  if (expdata->DFree) {
115  expdata->DFree(exp->data);
116  } else {
117  SCFree(exp->data);
118  }
119  }
120  SCFree(exp);
121 }
122 
123 static void ExpectationListFree(void *el)
124 {
125  ExpectationList *exp_list = (ExpectationList *)el;
126  if (exp_list == NULL)
127  return;
128 
129  if (exp_list->length > 0) {
130  Expectation *exp = NULL, *pexp = NULL;
131  CIRCLEQ_FOREACH_SAFE(exp, &exp_list->list, entries, pexp) {
132  CIRCLEQ_REMOVE(&exp_list->list, exp, entries);
133  exp_list->length--;
134  AppLayerFreeExpectation(exp);
135  }
136  }
137  SCFree(exp_list);
138 }
139 
140 uint64_t ExpectationGetCounter(void)
141 {
142  uint64_t x = SC_ATOMIC_GET(expectation_count);
143  return x;
144 }
145 
147 {
148  g_ippair_expectation_id = SCIPPairStorageRegister("expectation", ExpectationListFree);
149  g_flow_expectation_id = SCFlowStorageRegister("expectation", ExpectationDataFree);
150  SC_ATOMIC_INIT(expectation_count);
151 }
152 
153 static inline int GetFlowAddresses(Flow *f, Address *ip_src, Address *ip_dst)
154 {
155  memset(ip_src, 0, sizeof(*ip_src));
156  memset(ip_dst, 0, sizeof(*ip_dst));
157  if (FLOW_IS_IPV4(f)) {
158  FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, ip_src);
159  FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, ip_dst);
160  } else if (FLOW_IS_IPV6(f)) {
161  FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, ip_src);
162  FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, ip_dst);
163  } else {
164  return -1;
165  }
166  return 0;
167 }
168 
169 static ExpectationList *AppLayerExpectationLookup(Flow *f, IPPair **ipp)
170 {
171  Address ip_src, ip_dst;
172  if (GetFlowAddresses(f, &ip_src, &ip_dst) == -1)
173  return NULL;
174  *ipp = IPPairLookupIPPairFromHash(&ip_src, &ip_dst);
175  if (*ipp == NULL) {
176  return NULL;
177  }
178 
179  return SCIPPairGetStorageById(*ipp, g_ippair_expectation_id);
180 }
181 
182 
183 static ExpectationList *AppLayerExpectationRemove(IPPair *ipp,
184  ExpectationList *exp_list,
185  Expectation *exp)
186 {
187  CIRCLEQ_REMOVE(&exp_list->list, exp, entries);
188  AppLayerFreeExpectation(exp);
189  IPPairDecrUsecnt(ipp);
190  SC_ATOMIC_SUB(expectation_count, 1);
191  exp_list->length--;
192  if (exp_list->length == 0) {
193  SCIPPairSetStorageById(ipp, g_ippair_expectation_id, NULL);
194  ExpectationListFree(exp_list);
195  exp_list = NULL;
196  }
197  return exp_list;
198 }
199 
200 /**
201  * Create an entry in expectation list
202  *
203  * Create a expectation from an existing Flow. Currently, only Flow between
204  * the two original IP addresses are supported. In case of success, the
205  * ownership of the data pointer is taken. In case of error, the pointer
206  * to data has to be freed by the caller.
207  *
208  * \param f a pointer to the original Flow
209  * \param direction the direction of the data in the expectation flow
210  * \param src source port of the expected flow, use 0 for any
211  * \param dst destination port of the expected flow, use 0 for any
212  * \param alproto the protocol that need to be set on the expected flow
213  * \param data pointer to data that will be attached to the expected flow
214  *
215  * \return -1 if error
216  * \return 0 if success
217  */
218 int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst,
219  AppProto alproto, void *data)
220 {
221  ExpectationList *exp_list = NULL;
222  IPPair *ipp;
223  Address ip_src, ip_dst;
224 
225  Expectation *exp = SCCalloc(1, sizeof(*exp));
226  if (exp == NULL)
227  return -1;
228 
229  exp->sp = src;
230  exp->dp = dst;
231  exp->alproto = alproto;
232  exp->ts = f->lastts;
233  exp->orig_f = (void *)f;
234  exp->data = data;
235  exp->direction = direction;
236 
237  if (GetFlowAddresses(f, &ip_src, &ip_dst) == -1)
238  goto error;
239  ipp = IPPairGetIPPairFromHash(&ip_src, &ip_dst);
240  if (ipp == NULL)
241  goto error;
242 
243  exp_list = SCIPPairGetStorageById(ipp, g_ippair_expectation_id);
244  if (exp_list) {
245  CIRCLEQ_INSERT_HEAD(&exp_list->list, exp, entries);
246  /* In case there is already EXPECTATION_MAX_LEVEL expectations waiting to be fulfilled,
247  * we remove the older expectation to limit the total number of expectations */
248  if (exp_list->length >= EXPECTATION_MAX_LEVEL) {
249  Expectation *last_exp = CIRCLEQ_LAST(&exp_list->list);
250  CIRCLEQ_REMOVE(&exp_list->list, last_exp, entries);
251  AppLayerFreeExpectation(last_exp);
252  /* We keep the same amount of expectation so we fully release
253  * the IP pair */
255  IPPairRelease(ipp);
256  return 0;
257  }
258  } else {
259  exp_list = SCCalloc(1, sizeof(*exp_list));
260  if (exp_list == NULL)
261  goto error;
262  exp_list->length = 0;
263  CIRCLEQ_INIT(&exp_list->list);
264  CIRCLEQ_INSERT_HEAD(&exp_list->list, exp, entries);
265  SCIPPairSetStorageById(ipp, g_ippair_expectation_id, exp_list);
266  }
267 
268  exp_list->length += 1;
269  SC_ATOMIC_ADD(expectation_count, 1);
271  /* As we are creating the expectation, we release lock on IPPair without
272  * setting the ref count to 0. This way the IPPair will be kept till
273  * cleanup */
274  IPPairUnlock(ipp);
275  return 0;
276 
277 error:
278  SCFree(exp);
279  return -1;
280 }
281 
282 /**
283  * Return Flow storage identifier corresponding to expectation data
284  *
285  * \return expectation data identifier
286  */
288 {
289  return g_flow_expectation_id;
290 }
291 
292 /**
293  * Function doing a lookup in expectation list and updating Flow if needed.
294  *
295  * This function lookup for a existing expectation that could match the Flow.
296  * If found and if the expectation contains data it store the data in the
297  * expectation storage of the Flow.
298  *
299  * \return an AppProto value if found
300  * \return ALPROTO_UNKNOWN if not found
301  */
303 {
304  AppProto alproto = ALPROTO_UNKNOWN;
305  IPPair *ipp = NULL;
306  Expectation *lexp = NULL;
307  Expectation *exp = NULL;
308 
309  int x = SC_ATOMIC_GET(expectation_count);
310  if (x == 0) {
311  return ALPROTO_UNKNOWN;
312  }
313 
314  /* Call will take reference of the ip pair in 'ipp' */
315  ExpectationList *exp_list = AppLayerExpectationLookup(f, &ipp);
316  if (exp_list == NULL)
317  goto out;
318 
319  CIRCLEQ_FOREACH_SAFE(exp, &exp_list->list, entries, lexp) {
320  if ((exp->direction & flags) && ((exp->sp == 0) || (exp->sp == f->sp)) &&
321  ((exp->dp == 0) || (exp->dp == f->dp))) {
322  alproto = exp->alproto;
323  if (f->alproto_ts == ALPROTO_UNKNOWN) {
324  f->alproto_ts = alproto;
325  }
326  if (f->alproto_tc == ALPROTO_UNKNOWN) {
327  f->alproto_tc = alproto;
328  }
329  void *fdata = SCFlowGetStorageById(f, g_flow_expectation_id);
330  if (fdata) {
331  /* We already have an expectation so let's clean this one */
332  ExpectationDataFree(exp->data);
333  } else {
334  /* Transfer ownership of Expectation data to the Flow */
335  if (SCFlowSetStorageById(f, g_flow_expectation_id, exp->data) != 0) {
336  SCLogDebug("Unable to set flow storage");
337  }
338  }
339  exp->data = NULL;
340  exp_list = AppLayerExpectationRemove(ipp, exp_list, exp);
341  if (exp_list == NULL)
342  goto out;
343  continue;
344  }
345  /* Cleaning remove old entries */
347  exp_list = AppLayerExpectationRemove(ipp, exp_list, exp);
348  if (exp_list == NULL)
349  goto out;
350  continue;
351  }
352  }
353 
354 out:
355  if (ipp)
356  IPPairRelease(ipp);
357  return alproto;
358 }
359 
361 {
362  IPPair *ipp = NULL;
363  Expectation *exp = NULL;
364  Expectation *pexp = NULL;
365 
366  int x = SC_ATOMIC_GET(expectation_count);
367  if (x == 0) {
368  return;
369  }
370 
371  /* Call will take reference of the ip pair in 'ipp' */
372  ExpectationList *exp_list = AppLayerExpectationLookup(f, &ipp);
373  if (exp_list == NULL)
374  goto out;
375 
376  CIRCLEQ_FOREACH_SAFE(exp, &exp_list->list, entries, pexp) {
377  /* Cleaning remove old entries */
378  if (exp->orig_f == (void *)f) {
379  exp_list = AppLayerExpectationRemove(ipp, exp_list, exp);
380  if (exp_list == NULL)
381  goto out;
382  }
383  }
384 
385 out:
386  if (ipp)
387  IPPairRelease(ipp);
388 }
389 
390 /**
391  * @}
392  */
EXPECTATION_TIMEOUT
#define EXPECTATION_TIMEOUT
Definition: app-layer-expectation.c:70
Expectation_::alproto
AppProto alproto
Definition: app-layer-expectation.c:77
FLOW_HAS_EXPECTATION
#define FLOW_HAS_EXPECTATION
Definition: flow.h:113
CIRCLEQ_FOREACH_SAFE
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:106
ExpectationGetCounter
uint64_t ExpectationGetCounter(void)
Definition: app-layer-expectation.c:140
FLOW_IS_IPV6
#define FLOW_IS_IPV6(f)
Definition: flow.h:170
Flow_::flags
uint64_t flags
Definition: flow.h:403
Expectation_::data
void * data
Definition: app-layer-expectation.c:81
SCIPPairStorageId
Definition: ippair-storage.h:31
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
AppLayerExpectationSetup
void AppLayerExpectationSetup(void)
Definition: app-layer-expectation.c:146
IPPairRelease
void IPPairRelease(IPPair *h)
Definition: ippair.c:505
CIRCLEQ_HEAD
#define CIRCLEQ_HEAD(name, type)
Definition: queue.h:76
Expectation_::orig_f
void * orig_f
Definition: app-layer-expectation.c:80
Expectation
struct Expectation_ Expectation
SCFlowGetStorageById
void * SCFlowGetStorageById(const Flow *f, SCFlowStorageId id)
Definition: flow-storage.c:40
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
ExpectationData_::DFree
void(* DFree)(void *)
Definition: app-layer-expectation.c:88
AppProto
uint16_t AppProto
Definition: app-layer-protos.h:87
SCIPPairGetStorageById
void * SCIPPairGetStorageById(IPPair *h, SCIPPairStorageId id)
Definition: ippair-storage.c:35
Flow_
Flow data structure.
Definition: flow.h:354
SCIPPairStorageRegister
SCIPPairStorageId SCIPPairStorageRegister(const char *name, void(*Free)(void *))
Definition: ippair-storage.c:51
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
SCFlowStorageId
Definition: flow-storage.h:31
IPPairDecrUsecnt
#define IPPairDecrUsecnt(h)
Definition: ippair.h:110
Address_
Definition: decode.h:113
ExpectationData
struct ExpectationData_ ExpectationData
ExpectationData_
Definition: app-layer-expectation.c:85
SCIPPairSetStorageById
int SCIPPairSetStorageById(IPPair *h, SCIPPairStorageId id, void *ptr)
Definition: ippair-storage.c:40
Flow_::dp
Port dp
Definition: flow.h:370
AppLayerExpectationClean
void AppLayerExpectationClean(Flow *f)
Definition: app-layer-expectation.c:360
SC_ATOMIC_DECLARE
SC_ATOMIC_DECLARE(uint32_t, expectation_count)
FLOW_COPY_IPV6_ADDR_TO_PACKET
#define FLOW_COPY_IPV6_ADDR_TO_PACKET(fa, pa)
Definition: flow.h:183
SCFlowStorageRegister
SCFlowStorageId SCFlowStorageRegister(const char *name, void(*Free)(void *))
Definition: flow-storage.c:61
Flow_::dst
FlowAddress dst
Definition: flow.h:357
Expectation_::sp
Port sp
Definition: app-layer-expectation.c:75
app-layer-expectation.h
AppLayerExpectationHandle
AppProto AppLayerExpectationHandle(Flow *f, uint8_t flags)
Definition: app-layer-expectation.c:302
length
uint16_t length
Definition: decode-sctp.h:2
Flow_::lastts
SCTime_t lastts
Definition: flow.h:418
ExpectationList
struct ExpectationList_ ExpectationList
util-print.h
SCIPPairStorageId::id
int id
Definition: ippair-storage.h:32
SCFlowSetStorageById
int SCFlowSetStorageById(Flow *f, SCFlowStorageId id, void *ptr)
Definition: flow-storage.c:45
IPPairGetIPPairFromHash
IPPair * IPPairGetIPPairFromHash(Address *a, Address *b)
Definition: ippair.c:524
FLOW_IS_IPV4
#define FLOW_IS_IPV4(f)
Definition: flow.h:168
IPPairUnlock
void IPPairUnlock(IPPair *h)
Definition: ippair.c:511
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
EXPECTATION_MAX_LEVEL
#define EXPECTATION_MAX_LEVEL
Definition: app-layer-expectation.c:71
AppLayerExpectationCreate
int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst, AppProto alproto, void *data)
Definition: app-layer-expectation.c:218
CIRCLEQ_ENTRY
#define CIRCLEQ_ENTRY(type)
Definition: queue.h:85
Port
uint16_t Port
Definition: decode.h:219
SCTime_t
Definition: util-time.h:40
queue.h
ExpectationList_
Definition: app-layer-expectation.c:91
Flow_::src
FlowAddress src
Definition: flow.h:357
flow-storage.h
flags
uint8_t flags
Definition: decode-gre.h:0
suricata-common.h
IPPair_
Definition: ippair.h:58
SCTIME_SECS
#define SCTIME_SECS(t)
Definition: util-time.h:57
CIRCLEQ_REMOVE
#define CIRCLEQ_REMOVE(head, elm, field)
Definition: queue.h:171
CIRCLEQ_INSERT_HEAD
#define CIRCLEQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:151
SCFree
#define SCFree(p)
Definition: util-mem.h:61
Flow_::alproto_ts
AppProto alproto_ts
Definition: flow.h:451
CIRCLEQ_INIT
#define CIRCLEQ_INIT(head)
Definition: queue.h:126
Expectation_::ts
SCTime_t ts
Definition: app-layer-expectation.c:74
src
uint16_t src
Definition: app-layer-dnp3.h:5
ALPROTO_UNKNOWN
@ ALPROTO_UNKNOWN
Definition: app-layer-protos.h:29
AppLayerExpectationGetFlowId
SCFlowStorageId AppLayerExpectationGetFlowId(void)
Definition: app-layer-expectation.c:287
IPPairLookupIPPairFromHash
IPPair * IPPairLookupIPPairFromHash(Address *a, Address *b)
look up a ippair in the hash
Definition: ippair.c:623
SCFlowStorageId::id
int id
Definition: flow-storage.h:32
Expectation_::dp
Port dp
Definition: app-layer-expectation.c:76
Flow_::sp
Port sp
Definition: flow.h:359
dst
uint16_t dst
Definition: app-layer-dnp3.h:4
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
Flow_::alproto_tc
AppProto alproto_tc
Definition: flow.h:452
ippair-storage.h
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
Expectation_
Definition: app-layer-expectation.c:73
Expectation_::direction
int direction
Definition: app-layer-expectation.c:78
FLOW_COPY_IPV4_ADDR_TO_PACKET
#define FLOW_COPY_IPV4_ADDR_TO_PACKET(fa, pa)
Definition: flow.h:178
CIRCLEQ_LAST
#define CIRCLEQ_LAST(head)
Definition: queue.h:95