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