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 */
53static 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 */
116int 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 const htp_header_t *h_xff = htp_tx_request_header(tx, xff_cfg->header);
143
144 if (h_xff != NULL && htp_header_value_len(h_xff) >= XFF_CHAIN_MINLEN &&
145 htp_header_value_len(h_xff) < XFF_CHAIN_MAXLEN) {
146
147 memcpy(xff_chain, htp_header_value_ptr(h_xff), htp_header_value_len(h_xff));
148 xff_chain[htp_header_value_len(h_xff)] = 0;
149
150 if (xff_cfg->flags & XFF_REVERSE) {
151 /** Get the last IP address from the chain */
152 p_xff = memrchr(xff_chain, ' ', htp_header_value_len(h_xff));
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, ',', htp_header_value_len(h_xff));
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 */
177int 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
195end:
196 return 0; // Not found
197}
198
199/**
200 * \brief Function to return XFF configuration from a configuration node.
201 */
203{
204 BUG_ON(result == NULL);
205
206 SCConfNode *xff_node = NULL;
207
208 if (conf != NULL)
209 xff_node = SCConfNodeLookupChild(conf, "xff");
210
211 if (xff_node != NULL && SCConfNodeChildValueIsTrue(xff_node, "enabled")) {
212 const char *xff_mode = SCConfNodeLookupChildValue(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("The XFF mode hasn't been defined, falling back to extra-data mode");
219 }
220 else if (strcasecmp(xff_mode, "extra-data") != 0) {
222 "The XFF mode %s is invalid, falling back to extra-data mode", xff_mode);
223 }
224 result->flags |= XFF_EXTRADATA;
225 }
226
227 const char *xff_deployment = SCConfNodeLookupChildValue(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("The XFF deployment hasn't been defined, falling back to reverse "
234 "proxy deployment");
235 }
236 else if (strcasecmp(xff_deployment, "reverse") != 0) {
237 SCLogWarning("The XFF mode %s is invalid, falling back to reverse proxy deployment",
238 xff_deployment);
239 }
240 result->flags |= XFF_REVERSE;
241 }
242
243 const char *xff_header = SCConfNodeLookupChildValue(xff_node, "header");
244
245 if (xff_header != NULL) {
246 result->header = (char *) xff_header;
247 } else {
248 SCLogWarning("The XFF header hasn't been defined, using the default %s", XFF_DEFAULT);
249 result->header = XFF_DEFAULT;
250 }
251 } else {
252 result->flags = XFF_DISABLED;
253 }
254}
255
256
257#ifdef UNITTESTS
258static int XFFTest01(void) {
259 char input[] = "1.2.3.4:5678";
260 char output[16];
261 int r = ParseXFFString(input, output, sizeof(output));
262 FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
263 PASS;
264}
265
266static int XFFTest02(void) {
267 char input[] = "[12::34]:1234"; // thanks chort!
268 char output[16];
269 int r = ParseXFFString(input, output, sizeof(output));
270 FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
271 PASS;
272}
273
274static int XFFTest03(void) {
275 char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
276 char output[46];
277 int r = ParseXFFString(input, output, sizeof(output));
278 FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
279 PASS;
280}
281
282static int XFFTest04(void) {
283 char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
284 char output[46];
285 int r = ParseXFFString(input, output, sizeof(output));
286 FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
287 PASS;
288}
289
290static int XFFTest05(void) {
291 char input[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
292 char output[46];
293 int r = ParseXFFString(input, output, sizeof(output));
294 FAIL_IF_NOT(r == 1 && strcmp(output, "::ffff:1.2.3.4") == 0);
295 PASS;
296}
297
298static int XFFTest06(void) {
299 char input[] = "12::34";
300 char output[46];
301 int r = ParseXFFString(input, output, sizeof(output));
302 FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
303 PASS;
304}
305
306static int XFFTest07(void) {
307 char input[] = "1.2.3.4";
308 char output[46];
309 int r = ParseXFFString(input, output, sizeof(output));
310 FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
311 PASS;
312}
313
314static int XFFTest08(void) {
315 char input[] = "[1.2.3.4:1234";
316 char output[46];
317 int r = ParseXFFString(input, output, sizeof(output));
318 FAIL_IF_NOT(r == 0);
319 PASS;
320}
321
322static int XFFTest09(void) {
323 char input[] = "999.999.999.999:1234";
324 char output[46];
325 int r = ParseXFFString(input, output, sizeof(output));
326 FAIL_IF_NOT(r == 0);
327 PASS;
328}
329
330#endif
331
333{
334#ifdef UNITTESTS
335 UtRegisterTest("XFFTest01", XFFTest01);
336 UtRegisterTest("XFFTest02", XFFTest02);
337 UtRegisterTest("XFFTest03", XFFTest03);
338 UtRegisterTest("XFFTest04", XFFTest04);
339 UtRegisterTest("XFFTest05", XFFTest05);
340 UtRegisterTest("XFFTest06", XFFTest06);
341 UtRegisterTest("XFFTest07", XFFTest07);
342 UtRegisterTest("XFFTest08", XFFTest08);
343 UtRegisterTest("XFFTest09", XFFTest09);
344#endif
345}
uint8_t len
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.
#define XFF_CHAIN_MINLEN
#define XFF_CHAIN_MAXLEN
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.
void HttpXFFGetCfg(SCConfNode *conf, HttpXFFCfg *result)
Function to return XFF configuration from a configuration node.
void HTPXFFParserRegisterTests(void)
#define XFF_DEFAULT
#define XFF_FORWARD
#define XFF_EXTRADATA
#define XFF_OVERWRITE
#define XFF_DISABLED
#define XFF_REVERSE
uint64_t AppLayerParserGetTxCnt(const Flow *f, void *alstate)
void * AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id)
@ ALPROTO_HTTP1
SCConfNode * SCConfNodeLookupChild(const SCConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition conf.c:796
int SCConfNodeChildValueIsTrue(const SCConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition conf.c:868
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
Flow data structure.
Definition flow.h:356
uint8_t proto
Definition flow.h:378
const char * header
#define BUG_ON(x)
size_t strlcpy(char *dst, const char *src, size_t siz)
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
void * memrchr(const void *s, int c, size_t n)