suricata
app-layer-expectation.c
Go to the documentation of this file.
1 /* Copyright (C) 2017 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 *)FlowGetStorageById(f, AppLayerExpectationGetDataId());
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 "suricata-common.h"
56 #include "debug.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 int g_expectation_id = -1;
66 static int g_expectation_data_id = -1;
67 
68 SC_ATOMIC_DECLARE(uint32_t, expectation_count);
69 
70 #define EXPECTATION_TIMEOUT 30
71 
72 typedef struct Expectation_ {
73  struct timeval ts;
77  int direction;
78  void *data;
79  struct Expectation_ *next;
80 } Expectation;
81 
82 typedef struct ExpectationData_ {
83  /** Start of Expectation Data structure must be a pointer
84  * to free function. Set to NULL to use SCFree() */
85  void (*DFree)(void *);
87 
88 static void ExpectationDataFree(void *e)
89 {
90  SCLogDebug("Free expectation data");
91  ExpectationData *ed = (ExpectationData *) e;
92  if (ed->DFree) {
93  ed->DFree(e);
94  } else {
95  SCFree(e);
96  }
97 }
98 
99 static void ExpectationListFree(void *e)
100 {
101  Expectation *exp = (Expectation *)e;
102  Expectation *lexp;
103  while (exp) {
104  lexp = exp->next;
105  if (exp->data) {
106  ExpectationData *expdata = (ExpectationData *) exp->data;
107  if (expdata->DFree) {
108  expdata->DFree(exp->data);
109  } else {
110  SCFree(exp->data);
111  }
112  }
113  SCFree(exp);
114  exp = lexp;
115  }
116 }
117 
118 uint64_t ExpectationGetCounter(void)
119 {
120  uint64_t x = SC_ATOMIC_GET(expectation_count);
121  return x;
122 }
123 
125 {
126  g_expectation_id = IPPairStorageRegister("expectation", sizeof(void *), NULL, ExpectationListFree);
127  g_expectation_data_id = FlowStorageRegister("expectation", sizeof(void *), NULL, ExpectationDataFree);
128  SC_ATOMIC_INIT(expectation_count);
129 }
130 
131 static inline int GetFlowAddresses(Flow *f, Address *ip_src, Address *ip_dst)
132 {
133  memset(ip_src, 0, sizeof(*ip_src));
134  memset(ip_dst, 0, sizeof(*ip_dst));
135  if (FLOW_IS_IPV4(f)) {
136  FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, ip_src);
137  FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, ip_dst);
138  } else if (FLOW_IS_IPV6(f)) {
139  FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, ip_src);
140  FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, ip_dst);
141  } else {
142  return -1;
143  }
144  return 0;
145 }
146 
147 static Expectation *AppLayerExpectationLookup(Flow *f, int direction, IPPair **ipp)
148 {
149  Address ip_src, ip_dst;
150  if (GetFlowAddresses(f, &ip_src, &ip_dst) == -1)
151  return NULL;
152  *ipp = IPPairLookupIPPairFromHash(&ip_src, &ip_dst);
153  if (*ipp == NULL) {
154  return NULL;
155  }
156 
157  return IPPairGetStorageById(*ipp, g_expectation_id);
158 }
159 
160 /**
161  * Create an entry in expectation list
162  *
163  * Create a expectation from an existing Flow. Currently, only Flow between
164  * the two original IP addresses are supported.
165  *
166  * \param f a pointer to the original Flow
167  * \param direction the direction of the data in the expectation flow
168  * \param src source port of the expected flow, use 0 for any
169  * \param dst destination port of the expected flow, use 0 for any
170  * \param alproto the protocol that need to be set on the expected flow
171  * \param data pointer to data that will be attached to the expected flow
172  *
173  * \return -1 if error
174  * \return 0 if success
175  */
177  AppProto alproto, void *data)
178 {
179  Expectation *iexp = NULL;
180  IPPair *ipp;
181  Address ip_src, ip_dst;
182 
183  Expectation *exp = SCCalloc(1, sizeof(*exp));
184  if (exp == NULL)
185  return -1;
186 
187  exp->sp = src;
188  exp->dp = dst;
189  exp->alproto = alproto;
190  exp->ts = f->lastts;
191  exp->data = data;
192  exp->direction = direction;
193 
194  if (GetFlowAddresses(f, &ip_src, &ip_dst) == -1)
195  goto error;
196  ipp = IPPairGetIPPairFromHash(&ip_src, &ip_dst);
197  if (ipp == NULL)
198  goto error;
199 
200  iexp = IPPairGetStorageById(ipp, g_expectation_id);
201  exp->next = iexp;
202  IPPairSetStorageById(ipp, g_expectation_id, exp);
203 
204  SC_ATOMIC_ADD(expectation_count, 1);
205  /* As we are creating the expectation, we release lock on IPPair without
206  * setting the ref count to 0. This way the IPPair will be kept till
207  * cleanup */
208  IPPairUnlock(ipp);
209  return 0;
210 
211 error:
212  SCFree(exp);
213  return -1;
214 }
215 
216 /**
217  * Return Flow storage identifier corresponding to expectation data
218  *
219  * \return expectation data identifier
220  */
222 {
223  return g_expectation_data_id;
224 }
225 
226 /**
227  *
228  * Remove expectation and return next one
229  *
230  * \param ipp an IPPair
231  * \param pexp pointer to previous Expectation
232  * \param exp pointer to Expectation to remove
233  * \param lexp pointer to head of Expectation ist
234  * \return expectation
235  */
236 static Expectation * RemoveExpectationAndGetNext(IPPair *ipp,
237  Expectation *pexp, Expectation *exp,
238  Expectation *lexp)
239 {
240  /* we remove the object so we get ref count down by 1 to remove reference
241  * hold by the expectation
242  */
243  (void) IPPairDecrUsecnt(ipp);
244  SC_ATOMIC_SUB(expectation_count, 1);
245  if (pexp == NULL) {
246  IPPairSetStorageById(ipp, g_expectation_id, lexp);
247  } else {
248  pexp->next = lexp;
249  }
250  if (exp->data) {
251  ExpectationData *expdata = (ExpectationData *)exp->data;
252  if (expdata->DFree) {
253  expdata->DFree(exp->data);
254  } else {
255  SCFree(exp->data);
256  }
257  }
258  SCFree(exp);
259  return lexp;
260 }
261 
262 /**
263  * Function doing a lookup in expectation list and updating Flow if needed.
264  *
265  * This function lookup for a existing expectation that could match the Flow.
266  * If found and if the expectation contains data it store the data in the
267  * expectation storage of the Flow.
268  *
269  * \return an AppProto value if found
270  * \return ALPROTO_UNKNOWN if not found
271  */
273 {
275  IPPair *ipp = NULL;
276  Expectation *lexp = NULL;
277  Expectation *pexp = NULL;
278 
279  int x = SC_ATOMIC_GET(expectation_count);
280  if (x == 0) {
281  return ALPROTO_UNKNOWN;
282  }
283 
284  /* Call will take reference of the ip pair in 'ipp' */
285  Expectation *exp = AppLayerExpectationLookup(f, direction, &ipp);
286  if (exp == NULL)
287  goto out;
288 
289  time_t ctime = f->lastts.tv_sec;
290 
291  pexp = NULL;
292  while (exp) {
293  lexp = exp->next;
294  if ( (exp->direction & direction) &&
295  ((exp->sp == 0) || (exp->sp == f->sp)) &&
296  ((exp->dp == 0) || (exp->dp == f->dp))) {
297  alproto = exp->alproto;
298  f->alproto_ts = alproto;
299  f->alproto_tc = alproto;
300  void *fdata = FlowGetStorageById(f, g_expectation_id);
301  if (fdata) {
302  /* We already have an expectation so let's clean this one */
303  ExpectationDataFree(exp->data);
304  } else {
305  /* Transfer ownership of Expectation data to the Flow */
306  if (FlowSetStorageById(f, g_expectation_data_id, exp->data) != 0) {
307  SCLogDebug("Unable to set flow storage");
308  }
309  }
310  exp->data = NULL;
311  exp = RemoveExpectationAndGetNext(ipp, pexp, exp, lexp);
312  continue;
313  }
314  /* Cleaning remove old entries */
315  if (exp && (ctime > exp->ts.tv_sec + EXPECTATION_TIMEOUT)) {
316  exp = RemoveExpectationAndGetNext(ipp, pexp, exp, lexp);
317  continue;
318  }
319  pexp = exp;
320  exp = lexp;
321  }
322 
323 out:
324  if (ipp)
325  IPPairRelease(ipp);
326  return alproto;
327 }
328 
329 /**
330  * @}
331  */
#define FLOW_IS_IPV4(f)
Definition: flow.h:134
int IPPairStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
struct Expectation_ Expectation
#define SCLogDebug(...)
Definition: util-debug.h:335
struct ExpectationData_ ExpectationData
AppProto alproto_tc
Definition: flow.h:411
int AppLayerExpectationGetDataId(void)
Port sp
Definition: flow.h:331
#define FLOW_COPY_IPV6_ADDR_TO_PACKET(fa, pa)
Definition: flow.h:149
#define FLOW_COPY_IPV4_ADDR_TO_PACKET(fa, pa)
Definition: flow.h:144
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:107
IPPair * IPPairGetIPPairFromHash(Address *a, Address *b)
Definition: ippair.c:544
uint16_t src
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:124
uint16_t AppProto
#define EXPECTATION_TIMEOUT
IPPair * IPPairLookupIPPairFromHash(Address *a, Address *b)
look up a ippair in the hash
Definition: ippair.c:643
uint64_t ExpectationGetCounter(void)
FlowAddress dst
Definition: flow.h:329
#define SC_ATOMIC_INIT(name)
Initialize the previously declared atomic variable and it&#39;s lock.
Definition: util-atomic.h:81
AppProto alproto_ts
Definition: flow.h:410
uint16_t dst
#define SCCalloc(nm, a)
Definition: util-mem.h:197
#define IPPairDecrUsecnt(h)
Definition: ippair.h:111
SC_ATOMIC_DECLARE(uint32_t, expectation_count)
Definition: ippair.h:58
struct Expectation_ * next
AppProto AppLayerExpectationHandle(Flow *f, int direction)
int FlowSetStorageById(Flow *f, int id, void *ptr)
Definition: flow-storage.c:44
uint16_t Port
Definition: decode.h:233
struct timeval lastts
Definition: flow.h:358
struct timeval ts
void * IPPairGetStorageById(IPPair *h, int id)
int IPPairSetStorageById(IPPair *h, int id, void *ptr)
int FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
Definition: flow-storage.c:65
#define SCFree(a)
Definition: util-mem.h:228
Port dp
Definition: flow.h:338
#define FLOW_IS_IPV6(f)
Definition: flow.h:136
void AppLayerExpectationSetup(void)
int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst, AppProto alproto, void *data)
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:192
void * FlowGetStorageById(Flow *f, int id)
Definition: flow-storage.c:39
FlowAddress src
Definition: flow.h:329
void IPPairUnlock(IPPair *h)
Definition: ippair.c:531
void IPPairRelease(IPPair *h)
Definition: ippair.c:520
Flow data structure.
Definition: flow.h:325
void(* DFree)(void *)