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 static int HttpXFFGetIPFromTxAux(
111  const Flow *f, htp_tx_t *tx, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
112 {
113  uint8_t xff_chain[XFF_CHAIN_MAXLEN];
114  uint8_t *p_xff = NULL;
115 
116  const htp_header_t *h_xff = htp_tx_request_header(tx, xff_cfg->header);
117 
118  if (h_xff != NULL && htp_header_value_len(h_xff) >= XFF_CHAIN_MINLEN &&
119  htp_header_value_len(h_xff) < XFF_CHAIN_MAXLEN) {
120 
121  memcpy(xff_chain, htp_header_value_ptr(h_xff), htp_header_value_len(h_xff));
122  xff_chain[htp_header_value_len(h_xff)] = 0;
123 
124  if (xff_cfg->flags & XFF_REVERSE) {
125  /** Get the last IP address from the chain */
126  p_xff = memrchr(xff_chain, ' ', htp_header_value_len(h_xff));
127  if (p_xff == NULL) {
128  p_xff = xff_chain;
129  } else {
130  p_xff++;
131  }
132  }
133  else {
134  /** Get the first IP address from the chain */
135  p_xff = memchr(xff_chain, ',', htp_header_value_len(h_xff));
136  if (p_xff != NULL) {
137  *p_xff = 0;
138  }
139  p_xff = xff_chain;
140  }
141  return ParseXFFString((char *)p_xff, dstbuf, dstbuflen);
142  }
143  return 0;
144 }
145 
146 /**
147  * \brief Function to return XFF IP if any in the selected transaction. The
148  * caller needs to lock the flow.
149  * \retval 1 if the IP has been found and returned in dstbuf
150  * \retval 0 if the IP has not being found or error
151  */
153  const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
154 {
155  HtpState *htp_state = NULL;
156  uint64_t total_txs = 0;
157  htp_tx_t *tx = NULL;
158 
159  htp_state = (HtpState *)FlowGetAppState(f);
160 
161  if (htp_state == NULL) {
162  SCLogDebug("no http state, XFF IP cannot be retrieved");
163  return 0;
164  }
165 
166  total_txs = AppLayerParserGetTxCnt(f, htp_state);
167  if (tx_id >= total_txs)
168  return 0;
169 
170  tx = AppLayerParserGetTx(f->proto, ALPROTO_HTTP1, htp_state, tx_id);
171  if (tx == NULL) {
172  SCLogDebug("tx is NULL, XFF cannot be retrieved");
173  return 0;
174  }
175  return HttpXFFGetIPFromTxAux(f, tx, xff_cfg, dstbuf, dstbuflen);
176 }
177 
178 /**
179  * \brief Function to return XFF IP if any. The caller needs to lock the flow.
180  * \retval 1 if the IP has been found and returned in dstbuf
181  * \retval 0 if the IP has not being found or error
182  */
183 int HttpXFFGetIP(const Flow *f, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
184 {
185  HtpState *htp_state = (HtpState *)FlowGetAppState(f);
186  if (htp_state == NULL) {
187  SCLogDebug("no http state, XFF IP cannot be retrieved");
188  goto end;
189  }
190 
191  uint64_t tx_id = AppLayerParserGetMinId(f->alparser);
192  const uint64_t total_txs = AppLayerParserGetTxCnt(f, htp_state);
195  memset(&state, 0, sizeof(state));
196 
197  while (1) {
199  IterFunc(f->proto, f->alproto, f->alstate, tx_id, total_txs, &state);
200  if (ires.tx_ptr == NULL)
201  break;
202 
203  if (HttpXFFGetIPFromTxAux(f, ires.tx_ptr, xff_cfg, dstbuf, dstbuflen) == 1)
204  return 1;
205 
206  tx_id = ires.tx_id + 1;
207  }
208 
209 end:
210  return 0; // Not found
211 }
212 
213 /**
214  * \brief Function to return XFF configuration from a configuration node.
215  */
216 void HttpXFFGetCfg(SCConfNode *conf, HttpXFFCfg *result)
217 {
218  BUG_ON(result == NULL);
219 
220  SCConfNode *xff_node = NULL;
221 
222  if (conf != NULL)
223  xff_node = SCConfNodeLookupChild(conf, "xff");
224 
225  if (xff_node != NULL && SCConfNodeChildValueIsTrue(xff_node, "enabled")) {
226  const char *xff_mode = SCConfNodeLookupChildValue(xff_node, "mode");
227 
228  if (xff_mode != NULL && strcasecmp(xff_mode, "overwrite") == 0) {
229  result->flags |= XFF_OVERWRITE;
230  } else {
231  if (xff_mode == NULL) {
232  SCLogWarning("The XFF mode hasn't been defined, falling back to extra-data mode");
233  }
234  else if (strcasecmp(xff_mode, "extra-data") != 0) {
235  SCLogWarning(
236  "The XFF mode %s is invalid, falling back to extra-data mode", xff_mode);
237  }
238  result->flags |= XFF_EXTRADATA;
239  }
240 
241  const char *xff_deployment = SCConfNodeLookupChildValue(xff_node, "deployment");
242 
243  if (xff_deployment != NULL && strcasecmp(xff_deployment, "forward") == 0) {
244  result->flags |= XFF_FORWARD;
245  } else {
246  if (xff_deployment == NULL) {
247  SCLogWarning("The XFF deployment hasn't been defined, falling back to reverse "
248  "proxy deployment");
249  }
250  else if (strcasecmp(xff_deployment, "reverse") != 0) {
251  SCLogWarning("The XFF mode %s is invalid, falling back to reverse proxy deployment",
252  xff_deployment);
253  }
254  result->flags |= XFF_REVERSE;
255  }
256 
257  const char *xff_header = SCConfNodeLookupChildValue(xff_node, "header");
258 
259  if (xff_header != NULL) {
260  result->header = (char *) xff_header;
261  } else {
262  SCLogWarning("The XFF header hasn't been defined, using the default %s", XFF_DEFAULT);
263  result->header = XFF_DEFAULT;
264  }
265  } else {
266  result->flags = XFF_DISABLED;
267  }
268 }
269 
270 
271 #ifdef UNITTESTS
272 static int XFFTest01(void) {
273  char input[] = "1.2.3.4:5678";
274  char output[16];
275  int r = ParseXFFString(input, output, sizeof(output));
276  FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
277  PASS;
278 }
279 
280 static int XFFTest02(void) {
281  char input[] = "[12::34]:1234"; // thanks chort!
282  char output[16];
283  int r = ParseXFFString(input, output, sizeof(output));
284  FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
285  PASS;
286 }
287 
288 static int XFFTest03(void) {
289  char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
290  char output[46];
291  int r = ParseXFFString(input, output, sizeof(output));
292  FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
293  PASS;
294 }
295 
296 static int XFFTest04(void) {
297  char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
298  char output[46];
299  int r = ParseXFFString(input, output, sizeof(output));
300  FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
301  PASS;
302 }
303 
304 static int XFFTest05(void) {
305  char input[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
306  char output[46];
307  int r = ParseXFFString(input, output, sizeof(output));
308  FAIL_IF_NOT(r == 1 && strcmp(output, "::ffff:1.2.3.4") == 0);
309  PASS;
310 }
311 
312 static int XFFTest06(void) {
313  char input[] = "12::34";
314  char output[46];
315  int r = ParseXFFString(input, output, sizeof(output));
316  FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
317  PASS;
318 }
319 
320 static int XFFTest07(void) {
321  char input[] = "1.2.3.4";
322  char output[46];
323  int r = ParseXFFString(input, output, sizeof(output));
324  FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
325  PASS;
326 }
327 
328 static int XFFTest08(void) {
329  char input[] = "[1.2.3.4:1234";
330  char output[46];
331  int r = ParseXFFString(input, output, sizeof(output));
332  FAIL_IF_NOT(r == 0);
333  PASS;
334 }
335 
336 static int XFFTest09(void) {
337  char input[] = "999.999.999.999:1234";
338  char output[46];
339  int r = ParseXFFString(input, output, sizeof(output));
340  FAIL_IF_NOT(r == 0);
341  PASS;
342 }
343 
344 #endif
345 
347 {
348 #ifdef UNITTESTS
349  UtRegisterTest("XFFTest01", XFFTest01);
350  UtRegisterTest("XFFTest02", XFFTest02);
351  UtRegisterTest("XFFTest03", XFFTest03);
352  UtRegisterTest("XFFTest04", XFFTest04);
353  UtRegisterTest("XFFTest05", XFFTest05);
354  UtRegisterTest("XFFTest06", XFFTest06);
355  UtRegisterTest("XFFTest07", XFFTest07);
356  UtRegisterTest("XFFTest08", XFFTest08);
357  UtRegisterTest("XFFTest09", XFFTest09);
358 #endif
359 }
len
uint8_t len
Definition: app-layer-dnp3.h:2
XFF_REVERSE
#define XFF_REVERSE
Definition: app-layer-htp-xff.h:35
XFF_EXTRADATA
#define XFF_EXTRADATA
Definition: app-layer-htp-xff.h:31
HttpXFFGetCfg
void HttpXFFGetCfg(SCConfNode *conf, HttpXFFCfg *result)
Function to return XFF configuration from a configuration node.
Definition: app-layer-htp-xff.c:216
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
AppLayerGetTxIterator
AppLayerGetTxIteratorFunc AppLayerGetTxIterator(const uint8_t ipproto, const AppProto alproto)
Definition: app-layer-parser.c:711
Flow_::proto
uint8_t proto
Definition: flow.h:376
Flow_
Flow data structure.
Definition: flow.h:354
XFF_CHAIN_MAXLEN
#define XFF_CHAIN_MAXLEN
Definition: app-layer-htp-xff.c:42
SCConfNodeChildValueIsTrue
int SCConfNodeChildValueIsTrue(const SCConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition: conf.c:922
XFF_FORWARD
#define XFF_FORWARD
Definition: app-layer-htp-xff.h:37
p
Packet * p
Definition: fuzz_iprep.c:21
SCConfNodeLookupChildValue
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:878
AppLayerGetTxIterTuple::tx_ptr
void * tx_ptr
Definition: app-layer-parser.h:151
util-unittest.h
HtpState_
Definition: app-layer-htp.h:183
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:183
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
Flow_::alparser
AppLayerParserState * alparser
Definition: flow.h:478
app-layer-htp.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
AppLayerGetTxIterTuple::tx_id
uint64_t tx_id
Definition: app-layer-parser.h:152
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:262
app-layer-parser.h
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:325
AppLayerGetTxIterState
Definition: app-layer-parser.h:139
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
AppLayerGetTxIterTuple
Definition: app-layer-parser.h:150
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:1124
SCConfNodeLookupChild
SCConfNode * SCConfNodeLookupChild(const SCConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:850
HTPXFFParserRegisterTests
void HTPXFFParserRegisterTests(void)
Definition: app-layer-htp-xff.c:346
suricata-common.h
ALPROTO_HTTP1
@ ALPROTO_HTTP1
Definition: app-layer-protos.h:36
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
Flow_::alstate
void * alstate
Definition: flow.h:479
AppLayerGetTxIteratorFunc
AppLayerGetTxIterTuple(* AppLayerGetTxIteratorFunc)(const uint8_t ipproto, const AppProto alproto, void *alstate, uint64_t min_tx_id, uint64_t max_tx_id, AppLayerGetTxIterState *state)
tx iterator prototype
Definition: app-layer-parser.h:213
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:152
util-misc.h
AppLayerParserGetMinId
uint64_t AppLayerParserGetMinId(AppLayerParserState *pstate)
Definition: app-layer-parser.c:726
Flow_::alproto
AppProto alproto
application level protocol
Definition: flow.h:450
SCConfNode_
Definition: conf.h:37
AppLayerParserGetTxCnt
uint64_t AppLayerParserGetTxCnt(const Flow *f, void *alstate)
Definition: app-layer-parser.c:1117
XFF_DISABLED
#define XFF_DISABLED
Definition: app-layer-htp-xff.h:29
util-memrchr.h