]> git.ipfire.org Git - people/ms/suricata.git/blob - src/app-layer-htp-xff.c
core: Remove unneeded consts
[people/ms/suricata.git] / src / app-layer-htp-xff.c
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_HTTP1, 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 FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
264 PASS;
265 }
266
267 static int XFFTest02(void) {
268 char input[] = "[12::34]:1234"; // thanks chort!
269 char output[16];
270 int r = ParseXFFString(input, output, sizeof(output));
271 FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
272 PASS;
273 }
274
275 static int XFFTest03(void) {
276 char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
277 char output[46];
278 int r = ParseXFFString(input, output, sizeof(output));
279 FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
280 PASS;
281 }
282
283 static int XFFTest04(void) {
284 char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
285 char output[46];
286 int r = ParseXFFString(input, output, sizeof(output));
287 FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
288 PASS;
289 }
290
291 static int XFFTest05(void) {
292 char input[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
293 char output[46];
294 int r = ParseXFFString(input, output, sizeof(output));
295 FAIL_IF_NOT(r == 1 && strcmp(output, "::ffff:1.2.3.4") == 0);
296 PASS;
297 }
298
299 static int XFFTest06(void) {
300 char input[] = "12::34";
301 char output[46];
302 int r = ParseXFFString(input, output, sizeof(output));
303 FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
304 PASS;
305 }
306
307 static int XFFTest07(void) {
308 char input[] = "1.2.3.4";
309 char output[46];
310 int r = ParseXFFString(input, output, sizeof(output));
311 FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
312 PASS;
313 }
314
315 static int XFFTest08(void) {
316 char input[] = "[1.2.3.4:1234";
317 char output[46];
318 int r = ParseXFFString(input, output, sizeof(output));
319 FAIL_IF_NOT(r == 0);
320 PASS;
321 }
322
323 static int XFFTest09(void) {
324 char input[] = "999.999.999.999:1234";
325 char output[46];
326 int r = ParseXFFString(input, output, sizeof(output));
327 FAIL_IF_NOT(r == 0);
328 PASS;
329 }
330
331 #endif
332
333 void HTPXFFParserRegisterTests(void)
334 {
335 #ifdef UNITTESTS
336 UtRegisterTest("XFFTest01", XFFTest01);
337 UtRegisterTest("XFFTest02", XFFTest02);
338 UtRegisterTest("XFFTest03", XFFTest03);
339 UtRegisterTest("XFFTest04", XFFTest04);
340 UtRegisterTest("XFFTest05", XFFTest05);
341 UtRegisterTest("XFFTest06", XFFTest06);
342 UtRegisterTest("XFFTest07", XFFTest07);
343 UtRegisterTest("XFFTest08", XFFTest08);
344 UtRegisterTest("XFFTest09", XFFTest09);
345 #endif
346 }