suricata
app-layer-htp-xff.c
Go to the documentation of this file.
1 /* Copyright (C) 2014 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  * \file
20  *
21  * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
22  * \author Duarte Silva <duarte.silva@serializing.me>
23  */
24 
25 #include "suricata-common.h"
26 #include "conf.h"
27 
28 #include "app-layer-parser.h"
29 #include "app-layer-htp.h"
30 #include "app-layer-htp-xff.h"
31 
32 #include "util-misc.h"
33 #include "util-memrchr.h"
34 #include "util-unittest.h"
35 
36 /** XFF header value minimal length */
37 #define XFF_CHAIN_MINLEN 7
38 /** XFF header value maximum length */
39 #define XFF_CHAIN_MAXLEN 256
40 /** Default XFF header name */
41 #define XFF_DEFAULT "X-Forwarded-For"
42 
43 /** \internal
44  * \brief parse XFF string
45  * \param input input string, might be modified
46  * \param output output buffer
47  * \param output_size size of output buffer
48  * \retval bool 1 ok, 0 fail
49  */
50 static int ParseXFFString(char *input, char *output, int output_size)
51 {
52  size_t len = strlen(input);
53  if (len == 0)
54  return 0;
55 
56  if (input[0] == '[') {
57  char *end = strchr(input, ']');
58  if (end == NULL) // malformed, not closed
59  return 0;
60 
61  if (end != input+(len - 1)) {
62  SCLogDebug("data after closing bracket");
63  // if we ever want to parse the port, we can do it here
64  }
65 
66  /* done, lets wrap up */
67  input++; // skip past [
68  *end = '\0'; // overwrite ], ignore anything after
69 
70  } else {
71  /* lets see if the xff string ends in a port */
72  int c = 0;
73  int d = 0;
74  char *p = input;
75  while (*p != '\0') {
76  if (*p == ':')
77  c++;
78  if (*p == '.')
79  d++;
80  p++;
81  }
82  /* 3 dots: ipv4, one ':' port */
83  if (d == 3 && c == 1) {
84  SCLogDebug("XFF w port %s", input);
85  char *x = strchr(input, ':');
86  if (x) {
87  *x = '\0';
88  SCLogDebug("XFF w/o port %s", input);
89  // if we ever want to parse the port, we can do it here
90  }
91  }
92  }
93 
94  SCLogDebug("XFF %s", input);
95 
96  /** Sanity check on extracted IP for IPv4 and IPv6 */
97  uint32_t ip[4];
98  if (inet_pton(AF_INET, input, ip) == 1 ||
99  inet_pton(AF_INET6, input, ip) == 1)
100  {
101  strlcpy(output, input, output_size);
102  return 1; // OK
103  }
104  return 0;
105 }
106 
107 /**
108  * \brief Function to return XFF IP if any in the selected transaction. The
109  * caller needs to lock the flow.
110  * \retval 1 if the IP has been found and returned in dstbuf
111  * \retval 0 if the IP has not being found or error
112  */
113 int HttpXFFGetIPFromTx(const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg,
114  char *dstbuf, int dstbuflen)
115 {
116  uint8_t xff_chain[XFF_CHAIN_MAXLEN];
117  HtpState *htp_state = NULL;
118  htp_tx_t *tx = NULL;
119  uint64_t total_txs = 0;
120  uint8_t *p_xff = NULL;
121 
122  htp_state = (HtpState *)FlowGetAppState(f);
123 
124  if (htp_state == NULL) {
125  SCLogDebug("no http state, XFF IP cannot be retrieved");
126  return 0;
127  }
128 
129  total_txs = AppLayerParserGetTxCnt(f, htp_state);
130  if (tx_id >= total_txs)
131  return 0;
132 
133  tx = AppLayerParserGetTx(f->proto, ALPROTO_HTTP, htp_state, tx_id);
134  if (tx == NULL) {
135  SCLogDebug("tx is NULL, XFF cannot be retrieved");
136  return 0;
137  }
138 
139  htp_header_t *h_xff = NULL;
140  if (tx->request_headers != NULL) {
141  h_xff = htp_table_get_c(tx->request_headers, xff_cfg->header);
142  }
143 
144  if (h_xff != NULL && bstr_len(h_xff->value) >= XFF_CHAIN_MINLEN &&
145  bstr_len(h_xff->value) < XFF_CHAIN_MAXLEN) {
146 
147  memcpy(xff_chain, bstr_ptr(h_xff->value), bstr_len(h_xff->value));
148  xff_chain[bstr_len(h_xff->value)]=0;
149 
150  if (xff_cfg->flags & XFF_REVERSE) {
151  /** Get the last IP address from the chain */
152  p_xff = memrchr(xff_chain, ' ', bstr_len(h_xff->value));
153  if (p_xff == NULL) {
154  p_xff = xff_chain;
155  } else {
156  p_xff++;
157  }
158  }
159  else {
160  /** Get the first IP address from the chain */
161  p_xff = memchr(xff_chain, ',', bstr_len(h_xff->value));
162  if (p_xff != NULL) {
163  *p_xff = 0;
164  }
165  p_xff = xff_chain;
166  }
167  return ParseXFFString((char *)p_xff, dstbuf, dstbuflen);
168  }
169  return 0;
170 }
171 
172 /**
173  * \brief Function to return XFF IP if any. The caller needs to lock the flow.
174  * \retval 1 if the IP has been found and returned in dstbuf
175  * \retval 0 if the IP has not being found or error
176  */
177 int HttpXFFGetIP(const Flow *f, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
178 {
179  HtpState *htp_state = NULL;
180  uint64_t tx_id = 0;
181  uint64_t total_txs = 0;
182 
183  htp_state = (HtpState *)FlowGetAppState(f);
184  if (htp_state == NULL) {
185  SCLogDebug("no http state, XFF IP cannot be retrieved");
186  goto end;
187  }
188 
189  total_txs = AppLayerParserGetTxCnt(f, htp_state);
190  for (; tx_id < total_txs; tx_id++) {
191  if (HttpXFFGetIPFromTx(f, tx_id, xff_cfg, dstbuf, dstbuflen) == 1)
192  return 1;
193  }
194 
195 end:
196  return 0; // Not found
197 }
198 
199 /**
200  * \brief Function to return XFF configuration from a configuration node.
201  */
202 void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result)
203 {
204  BUG_ON(result == NULL);
205 
206  ConfNode *xff_node = NULL;
207 
208  if (conf != NULL)
209  xff_node = ConfNodeLookupChild(conf, "xff");
210 
211  if (xff_node != NULL && ConfNodeChildValueIsTrue(xff_node, "enabled")) {
212  const char *xff_mode = ConfNodeLookupChildValue(xff_node, "mode");
213 
214  if (xff_mode != NULL && strcasecmp(xff_mode, "overwrite") == 0) {
215  result->flags |= XFF_OVERWRITE;
216  } else {
217  if (xff_mode == NULL) {
218  SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The XFF mode hasn't been defined, falling back to extra-data mode");
219  }
220  else if (strcasecmp(xff_mode, "extra-data") != 0) {
221  SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The XFF mode %s is invalid, falling back to extra-data mode",
222  xff_mode);
223  }
224  result->flags |= XFF_EXTRADATA;
225  }
226 
227  const char *xff_deployment = ConfNodeLookupChildValue(xff_node, "deployment");
228 
229  if (xff_deployment != NULL && strcasecmp(xff_deployment, "forward") == 0) {
230  result->flags |= XFF_FORWARD;
231  } else {
232  if (xff_deployment == NULL) {
233  SCLogWarning(SC_WARN_XFF_INVALID_DEPLOYMENT, "The XFF deployment hasn't been defined, falling back to reverse proxy deployment");
234  }
235  else if (strcasecmp(xff_deployment, "reverse") != 0) {
236  SCLogWarning(SC_WARN_XFF_INVALID_DEPLOYMENT, "The XFF mode %s is invalid, falling back to reverse proxy deployment",
237  xff_deployment);
238  }
239  result->flags |= XFF_REVERSE;
240  }
241 
242  const char *xff_header = ConfNodeLookupChildValue(xff_node, "header");
243 
244  if (xff_header != NULL) {
245  result->header = (char *) xff_header;
246  } else {
247  SCLogWarning(SC_WARN_XFF_INVALID_HEADER, "The XFF header hasn't been defined, using the default %s",
248  XFF_DEFAULT);
249  result->header = XFF_DEFAULT;
250  }
251  }
252  else {
253  result->flags = XFF_DISABLED;
254  }
255 }
256 
257 
258 #ifdef UNITTESTS
259 static int XFFTest01(void) {
260  char input[] = "1.2.3.4:5678";
261  char output[16];
262  int r = ParseXFFString(input, output, sizeof(output));
263  if (r == 1 && strcmp(output, "1.2.3.4") == 0) {
264  return 1;
265  }
266  return 0;
267 }
268 
269 static int XFFTest02(void) {
270  char input[] = "[12::34]:1234"; // thanks chort!
271  char output[16];
272  int r = ParseXFFString(input, output, sizeof(output));
273  if (r == 1 && strcmp(output, "12::34") == 0) {
274  return 1;
275  }
276  return 0;
277 }
278 
279 static int XFFTest03(void) {
280  char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
281  char output[46];
282  int r = ParseXFFString(input, output, sizeof(output));
283  if (r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0) {
284  return 1;
285  }
286  return 0;
287 }
288 
289 static int XFFTest04(void) {
290  char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
291  char output[46];
292  int r = ParseXFFString(input, output, sizeof(output));
293  if (r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0) {
294  return 1;
295  }
296  return 0;
297 }
298 
299 static int XFFTest05(void) {
300  char input[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
301  char output[46];
302  int r = ParseXFFString(input, output, sizeof(output));
303  if (r == 1 && strcmp(output, "::ffff:1.2.3.4") == 0) {
304  return 1;
305  }
306  return 0;
307 }
308 
309 static int XFFTest06(void) {
310  char input[] = "12::34";
311  char output[46];
312  int r = ParseXFFString(input, output, sizeof(output));
313  if (r == 1 && strcmp(output, "12::34") == 0) {
314  return 1;
315  }
316  return 0;
317 }
318 
319 static int XFFTest07(void) {
320  char input[] = "1.2.3.4";
321  char output[46];
322  int r = ParseXFFString(input, output, sizeof(output));
323  if (r == 1 && strcmp(output, "1.2.3.4") == 0) {
324  return 1;
325  }
326  return 0;
327 }
328 
329 static int XFFTest08(void) {
330  char input[] = "[1.2.3.4:1234";
331  char output[46];
332  int r = ParseXFFString(input, output, sizeof(output));
333  if (r == 0) {
334  return 1;
335  }
336  return 0;
337 }
338 
339 static int XFFTest09(void) {
340  char input[] = "999.999.999.999:1234";
341  char output[46];
342  int r = ParseXFFString(input, output, sizeof(output));
343  if (r == 0) {
344  return 1;
345  }
346  return 0;
347 }
348 
349 #endif
350 
352 {
353 #ifdef UNITTESTS
354  UtRegisterTest("XFFTest01", XFFTest01);
355  UtRegisterTest("XFFTest02", XFFTest02);
356  UtRegisterTest("XFFTest03", XFFTest03);
357  UtRegisterTest("XFFTest04", XFFTest04);
358  UtRegisterTest("XFFTest05", XFFTest05);
359  UtRegisterTest("XFFTest06", XFFTest06);
360  UtRegisterTest("XFFTest07", XFFTest07);
361  UtRegisterTest("XFFTest08", XFFTest08);
362  UtRegisterTest("XFFTest09", XFFTest09);
363 #endif
364 }
#define SCLogDebug(...)
Definition: util-debug.h:335
int HttpXFFGetIP(const Flow *f, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
Function to return XFF IP if any. The caller needs to lock the flow.
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
#define BUG_ON(x)
uint8_t proto
Definition: flow.h:343
void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result)
Function to return XFF configuration from a configuration node.
uint64_t AppLayerParserGetTxCnt(const Flow *f, void *alstate)
ConfNode * ConfNodeLookupChild(const ConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:815
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition: conf.c:888
const char * header
#define XFF_REVERSE
void * AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id)
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:843
void * memrchr(const void *s, int c, size_t n)
Definition: util-memrchr.c:30
void HTPXFFParserRegisterTests(void)
int HttpXFFGetIPFromTx(const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
Function to return XFF IP if any in the selected transaction. The caller needs to lock the flow...
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define XFF_CHAIN_MINLEN
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
#define XFF_CHAIN_MAXLEN
Definition: conf.h:32
#define XFF_DEFAULT
#define XFF_OVERWRITE
uint16_t tx_id
void * FlowGetAppState(const Flow *f)
Definition: flow.c:1065
#define XFF_DISABLED
uint8_t len
#define XFF_FORWARD
Flow data structure.
Definition: flow.h:324
#define XFF_EXTRADATA