blob: 03f12a2727db8b9a487b75845e187d873d2f45c4 [file] [log] [blame]
Dragan Dosen59bb97a2017-06-02 12:03:16 +02001/*
2 * Mod Defender for HAProxy
3 *
4 * Copyright 2017 HAProxy Technologies, Dragan Dosen <ddosen@haproxy.com>
5 *
6 * Mod Defender
7 * Copyright (c) 2017 Annihil (https://github.com/VultureProject/mod_defender)
8 *
9 * Parts of code based on Apache HTTP Server source
10 * Copyright 2015 The Apache Software Foundation (http://www.apache.org/)
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 3 of the License, or (at your option) any later version.
16 *
17 */
18#include <limits.h>
19#include <stdio.h>
20#include <stdarg.h>
21
22#include <common/defaults.h>
23#include <common/standard.h>
24#include <common/chunk.h>
25#include <common/time.h>
26
27#include <proto/spoe.h>
28
29#include <http_core.h>
30#include <http_main.h>
31#include <http_log.h>
32#include <http_request.h>
33
34#include <apr_pools.h>
35#include <apr_strings.h>
36
37#include "spoa.h"
38#include "standalone.h"
39#include "defender.h"
40
41#define DEFENDER_NAME "defender"
42#define DEFENDER_INPUT_FILTER "DEFENDER_IN"
43#define DEFENDER_DEFAULT_UNIQUE_ID "unique_id"
44#define DEFENDER_BRIGADE_REQUEST "defender-brigade-request"
45
46extern module AP_MODULE_DECLARE_DATA defender_module;
47
48DECLARE_HOOK(int,post_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp,server_rec *s))
49DECLARE_HOOK(int,fixups,(request_rec *r))
50DECLARE_HOOK(int,header_parser,(request_rec *r))
51
52char *defender_name = DEFENDER_NAME;
53const char *defender_argv[] = { DEFENDER_NAME, NULL };
54const char *defender_unknown_hostname = "";
55
56void *defender_module_config = NULL;
57static server_rec *server = NULL;
58apr_pool_t *defender_pool = NULL;
59
60char hostname[MAX_HOSTNAME_LEN];
61char defender_cwd[MAXPATHLEN];
62
63static apr_status_t defender_bucket_read(apr_bucket *b, const char **str,
64 apr_size_t *len, apr_read_type_e block);
65static void defender_bucket_destroy(void *data);
66
67static const apr_bucket_type_t apr_bucket_type_defender = {
68 "defender", 8, APR_BUCKET_DATA,
69 defender_bucket_destroy,
70 defender_bucket_read,
71 apr_bucket_setaside_noop,
72 apr_bucket_shared_split,
73 apr_bucket_shared_copy
74};
75
76struct apr_bucket_defender {
77 apr_bucket_refcount refcount;
Willy Tarreau83061a82018-07-13 11:56:34 +020078 struct buffer buf;
Dragan Dosen59bb97a2017-06-02 12:03:16 +020079};
80
81static apr_status_t defender_bucket_read(apr_bucket *b, const char **str,
82 apr_size_t *len, apr_read_type_e block)
83{
84 struct apr_bucket_defender *d = b->data;
85
Willy Tarreau843b7cb2018-07-13 10:54:26 +020086 *str = d->buf.area;
87 *len = d->buf.data;
Dragan Dosen59bb97a2017-06-02 12:03:16 +020088
89 return APR_SUCCESS;
90}
91
92static void defender_bucket_destroy(void *data)
93{
94 struct apr_bucket_defender *d = data;
95
96 if (apr_bucket_shared_destroy(d))
97 apr_bucket_free(d);
98}
99
Willy Tarreau83061a82018-07-13 11:56:34 +0200100static apr_bucket *defender_bucket_make(apr_bucket *b,
101 const struct buffer *buf)
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200102{
103 struct apr_bucket_defender *d;
104
105 d = apr_bucket_alloc(sizeof(*d), b->list);
106
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200107 d->buf.area = buf->area;
108 d->buf.data = buf->data;
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200109 d->buf.size = 0;
110
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200111 b = apr_bucket_shared_make(b, d, 0, buf->data);
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200112 b->type = &apr_bucket_type_defender;
113 return b;
114}
115
Willy Tarreau83061a82018-07-13 11:56:34 +0200116static apr_bucket *defender_bucket_create(const struct buffer *buf,
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200117 apr_bucket_alloc_t *list)
118{
119 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
120
121 APR_BUCKET_INIT(b);
122 b->free = apr_bucket_free;
123 b->list = list;
124 return defender_bucket_make(b, buf);
125}
126
127static void defender_logger(int level, char *str)
128{
129 LOG(&null_worker, "%s", str);
130}
131
132static char *defender_strdup(apr_pool_t *pool, const char *src, uint64_t len)
133{
134 char *dst;
135
136 if (!(dst = apr_pcalloc(pool, len + 1)))
137 return NULL;
138
139 memcpy(dst, src, len);
140 dst[len] = '\0';
141
142 return dst;
143}
144
145static char *defender_printf(apr_pool_t *pool, const char *fmt, ...)
146{
147 va_list argp;
148 char *dst;
149 int len;
150
151 va_start(argp, fmt);
152 len = vsnprintf(NULL, 0, fmt, argp);
Dragan Dosenccf61002017-09-18 08:20:33 +0200153 va_end(argp);
154
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200155 if (len < 0)
156 return NULL;
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200157
158 if (!(dst = apr_pcalloc(pool, len + 1)))
159 return NULL;
160
161 va_start(argp, fmt);
162 len = vsnprintf(dst, len + 1, fmt, argp);
163 va_end(argp);
164
165 return dst;
166}
167
168static char *defender_addr2str(apr_pool_t *pool, struct sample *addr)
169{
170 sa_family_t family;
171 const void *src;
172 char *dst;
173
174 switch (addr->data.type) {
175 case SMP_T_IPV4:
176 src = &addr->data.u.ipv4;
177 family = AF_INET;
178 break;
179 case SMP_T_IPV6:
180 src = &addr->data.u.ipv6;
181 family = AF_INET6;
182 break;
183 default:
184 return NULL;
185 }
186
187 if (!(dst = apr_pcalloc(pool, INET6_ADDRSTRLEN + 1)))
188 return NULL;
189
190 if (inet_ntop(family, src, dst, INET6_ADDRSTRLEN))
191 return dst;
192
193 return NULL;
194}
195
196static void defender_pre_config()
197{
198 apr_pool_t *ptemp = NULL;
199
200 defender_module.module_index = 0;
201 defender_module.register_hooks(defender_pool);
202
203 apr_pool_create(&ptemp, defender_pool);
204 run_ap_hook_post_config(defender_pool, defender_pool, ptemp, server);
205 apr_pool_destroy(ptemp);
206}
207
208static const char *defender_read_config(const char *file)
209{
210 apr_pool_t *ptemp = NULL;
211 const char *err;
212 const char *fullname;
213
214 defender_module_config = defender_module.create_dir_config(defender_pool, "/");
215 if (defender_module_config == NULL) {
216 return "cannot allocate space for the configuration structure";
217 }
218
219 apr_pool_create(&ptemp, defender_pool);
220
221 fullname = ap_server_root_relative(ptemp, file);
222
223 err = read_module_config(server, defender_module_config,
224 defender_module.cmds,
225 defender_pool, ptemp, fullname);
226
227 apr_pool_destroy(ptemp);
228
229 return err;
230}
231
232static void defender_post_config()
233{
234 apr_pool_t *ptemp = NULL;
235
236 apr_pool_create(&ptemp, defender_pool);
237 run_ap_hook_post_config(defender_pool, defender_pool, ptemp, server);
238 apr_pool_destroy(ptemp);
239}
240
241static const char *defender_set_logger(const char *file)
242{
243 char *logname;
244
245 logger = defender_logger;
246
247 if (file == NULL)
248 return NULL;
249
250 logname = ap_server_root_relative(defender_pool, file);
251
252 if (apr_file_open(&server->error_log, logname,
253 APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE,
254 APR_OS_DEFAULT, defender_pool) != APR_SUCCESS) {
255 return apr_pstrcat(defender_pool, "Cannot open log file, ",
256 logname, NULL);
257 }
258 server->error_fname = logname;
259
260 return NULL;
261}
262
263static apr_status_t defender_input_filter(ap_filter_t *f,
264 apr_bucket_brigade *new_bb,
265 ap_input_mode_t mode,
266 apr_read_type_e block,
267 apr_off_t readbytes)
268{
269 apr_bucket_brigade *bb = NULL;
270 apr_bucket *b = NULL, *a = NULL;
271 apr_status_t rv;
272
273 bb = (apr_bucket_brigade *)apr_table_get(f->r->notes, DEFENDER_BRIGADE_REQUEST);
274
275 if (bb == NULL || (bb && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)))) {
276 b = apr_bucket_eos_create(f->c->bucket_alloc);
277 APR_BRIGADE_INSERT_TAIL(new_bb, b);
278 if (bb == NULL)
279 return APR_SUCCESS;
280 }
281
282 rv = apr_brigade_partition(bb, readbytes, &a);
283 if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
284 return rv;
285
286 b = APR_BRIGADE_FIRST(bb);
287
288 while (b != a) {
289 if (APR_BUCKET_IS_EOS(b))
290 ap_remove_input_filter(f);
291
292 APR_BUCKET_REMOVE(b);
293 APR_BRIGADE_INSERT_TAIL(new_bb, b);
294 b = APR_BRIGADE_FIRST(bb);
295 }
296
297 return APR_SUCCESS;
298}
299
300static conn_rec *defender_create_conn()
301{
302 conn_rec *c = NULL;
303 apr_pool_t *ptrans = NULL;
304
305 apr_pool_create(&ptrans, defender_pool);
306
307 c = apr_pcalloc(ptrans, sizeof(conn_rec));
308
309 c->pool = ptrans;
310 c->local_ip = "127.0.0.1";
311 c->local_addr = server->addrs->host_addr;
312 c->local_host = defender_name;
313 c->client_addr = server->addrs->host_addr;
314 c->remote_host = defender_name;
315
316 c->id = 1;
317 c->base_server = server;
318 c->bucket_alloc = apr_bucket_alloc_create(ptrans);
319
320 return c;
321}
322
323static request_rec *defender_create_request(conn_rec *conn)
324{
325 request_rec *r = NULL;
326 apr_pool_t *p = NULL;
327 struct ap_logconf *l;
328
329 apr_pool_create(&p, conn->pool);
330
331 r = apr_pcalloc(p, sizeof(request_rec));
332
333 r->pool = p;
334 r->connection = conn;
335 r->server = conn->base_server;
336
337 r->headers_in = apr_table_make(p, 25);
338 r->headers_out = apr_table_make(p, 12);
339 r->subprocess_env = apr_table_make(p, 25);
340 r->err_headers_out = apr_table_make(p, 5);
341 r->notes = apr_table_make(p, 5);
342
343 r->request_config = apr_palloc(p, sizeof(void *));
344 r->per_dir_config = apr_palloc(p, sizeof(void *));
345 ((void **)r->per_dir_config)[0] = defender_module_config;
346
347 r->handler = defender_name;
348
349 r->parsed_uri.scheme = "http";
350 r->parsed_uri.is_initialized = 1;
351 r->parsed_uri.port = 80;
352 r->parsed_uri.port_str = "80";
353 r->parsed_uri.fragment = "";
354
355 r->input_filters = NULL;
356 r->output_filters = NULL;
357
358 l = apr_pcalloc(p, sizeof(struct ap_logconf));
359 l->level = APLOG_DEBUG;
360 r->log = l;
361
362 return r;
363}
364
365static int defender_process_headers(request_rec *r)
366{
367 return run_ap_hook_header_parser(r);
368}
369
370static int defender_process_body(request_rec *r)
371{
372 ap_add_input_filter(DEFENDER_INPUT_FILTER, NULL, r, r->connection);
373 return run_ap_hook_fixups(r);
374}
375
376int defender_init(const char *config_file, const char *log_file)
377{
378 apr_status_t rv;
379 const char *msg;
380
381 if (!config_file) {
382 LOG(&null_worker, "Mod Defender configuration file not specified.\n");
383 return 0;
384 }
385
386 apr_initialize();
387 apr_pool_create(&defender_pool, NULL);
388 apr_hook_global_pool = defender_pool;
389
390 ap_server_root = getcwd(defender_cwd, APR_PATH_MAX);
391
392 server = (server_rec *) apr_palloc(defender_pool, sizeof(server_rec));
393 server->process = apr_palloc(defender_pool, sizeof(process_rec));
394 server->process->argc = 1;
395 server->process->argv = defender_argv;
396 server->process->short_name = defender_name;
397 server->process->pconf = defender_pool;
398 server->process->pool = defender_pool;
399
400 server->addrs = apr_palloc(defender_pool, sizeof(server_addr_rec));
401 rv = apr_sockaddr_info_get(&server->addrs->host_addr,
402 "127.0.0.1", APR_UNSPEC, 0, 0,
403 defender_pool);
404 if (rv != APR_SUCCESS) {
405 LOG(&null_worker, "Mod Defender getaddrinfo failed.\n");
406 return 0;
407 }
408
409 server->path = "/";
410 server->pathlen = strlen(server->path);
411 server->port = 0;
412 server->server_admin = defender_name;
413 server->server_scheme = "";
414 server->error_fname = NULL;
415 server->error_log = NULL;
416 server->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
417 server->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
418 server->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
419 server->timeout = apr_time_from_sec(DEFAULT_TIMEOUT);
420
421 memset(hostname, 0, sizeof(hostname));
422 gethostname(hostname, sizeof(hostname) - 1);
423 server->server_hostname = hostname;
424
425 server->addrs->host_port = 0;
426 server->names = server->wild_names = NULL;
427 server->is_virtual = 0;
428
429 server->lookup_defaults = NULL;
430 server->module_config = NULL;
431
432 msg = defender_set_logger(log_file);
433 if (msg != NULL) {
434 LOG(&null_worker, "Mod Defender init failed: %s\n", msg);
435 return 0;
436 }
437
438 ap_register_input_filter(DEFENDER_INPUT_FILTER, defender_input_filter,
439 NULL, AP_FTYPE_RESOURCE);
440
441 defender_pre_config();
442
443 msg = defender_read_config(config_file);
444 if (msg != NULL) {
445 LOG(&null_worker, "Mod Defender configuration failed: %s\n", msg);
446 return 0;
447 }
448
449 defender_post_config();
450
451 return 1;
452}
453
454int defender_process_request(struct worker *worker, struct defender_request *request)
455{
456 struct conn_rec *c = NULL;
457 struct request_rec *r = NULL;
458
459 struct apr_bucket_brigade *bb = NULL;
460 struct apr_bucket *d = NULL, *e = NULL;
461
Willy Tarreau83061a82018-07-13 11:56:34 +0200462 struct buffer *method;
463 struct buffer *path;
464 struct buffer *query;
465 struct buffer *version;
466 struct buffer *body;
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200467
468 struct defender_header hdr;
469 char *hdr_ptr, *hdr_end;
470
471 const char *ptr;
472
473 int status = DECLINED;
474
475 if (!(c = defender_create_conn()))
476 goto out;
477
478 if (!(r = defender_create_request(c)))
479 goto out;
480
481 /* request */
482 r->request_time = apr_time_now();
483
484 if (request->clientip.data.type != SMP_T_IPV4 &&
485 request->clientip.data.type != SMP_T_IPV6)
486 goto out;
487
488 if (!(r->useragent_ip = defender_addr2str(r->pool, &request->clientip)))
489 goto out;
490
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200491 if (request->id.data.u.str.area && request->id.data.u.str.data > 0) {
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200492 apr_table_setn(r->subprocess_env, "UNIQUE_ID",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200493 defender_strdup(r->pool, request->id.data.u.str.area,
494 request->id.data.u.str.data));
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200495 }
496 else {
497 apr_table_setn(r->subprocess_env, "UNIQUE_ID",
498 DEFENDER_DEFAULT_UNIQUE_ID);
499 }
500
501 method = &request->method.data.u.str;
502 path = &request->path.data.u.str;
503 query = &request->query.data.u.str;
504 version = &request->version.data.u.str;
505
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200506 r->method_number = lookup_builtin_method(method->area, method->data);
507 if (!(r->method = defender_strdup(r->pool, method->area, method->data)))
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200508 goto out;
509
510 r->unparsed_uri = defender_printf(r->pool, "%.*s%s%.*s",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200511 path->data, path->area,
512 query->data > 0 ? "?" : "",
513 query->data, query->area);
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200514 if (!r->unparsed_uri)
515 goto out;
516
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200517 if (!(r->uri = defender_strdup(r->pool, path->area, path->data)))
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200518 goto out;
519
520 r->parsed_uri.path = r->filename = r->uri;
521
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200522 if (!(r->args = defender_strdup(r->pool, query->area, query->data)))
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200523 goto out;
524
525 r->parsed_uri.query = r->args;
526
527 r->protocol = defender_printf(r->pool, "%s%.*s",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200528 version->data > 0 ? "HTTP/" : "",
529 version->data, version->area);
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200530 if (!r->protocol)
531 goto out;
532
533 r->the_request = defender_printf(r->pool, "%.*s %s%s%s",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200534 method->data, method->area,
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200535 r->unparsed_uri,
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200536 version->data > 0 ? " " : "",
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200537 r->protocol);
538 if (!r->the_request)
539 goto out;
540
541 /* headers */
542 if (request->headers.data.type != SMP_T_BIN)
543 goto misc;
544
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200545 hdr_ptr = request->headers.data.u.str.area;
546 hdr_end = hdr_ptr + request->headers.data.u.str.data;
Dragan Dosen59bb97a2017-06-02 12:03:16 +0200547
548 while (1) {
549 memset(&hdr, 0, sizeof(hdr));
550
551 if (decode_varint(&hdr_ptr, hdr_end, &hdr.name.len) == -1)
552 goto out;
553 if (!(hdr.name.str = defender_strdup(r->pool, hdr_ptr, hdr.name.len)))
554 goto out;
555
556 hdr_ptr += hdr.name.len;
557 if (hdr_ptr > hdr_end)
558 goto out;
559
560 if (decode_varint(&hdr_ptr, hdr_end, &hdr.value.len) == -1)
561 goto out;
562 if (!(hdr.value.str = defender_strdup(r->pool, hdr_ptr, hdr.value.len)))
563 goto out;
564
565 hdr_ptr += hdr.value.len;
566 if (hdr_ptr > hdr_end)
567 goto out;
568
569 if (!hdr.name.len && !hdr.value.len)
570 break;
571
572 apr_table_setn(r->headers_in, hdr.name.str, hdr.value.str);
573 }
574
575misc:
576
577 r->hostname = apr_table_get(r->headers_in, "Host");
578 if (!r->hostname)
579 r->hostname = defender_unknown_hostname;
580 r->parsed_uri.hostname = (char *)r->hostname;
581
582 r->content_type = apr_table_get(r->headers_in, "Content-Type");
583 r->content_encoding = apr_table_get(r->headers_in, "Content-Encoding");
584 ptr = apr_table_get(r->headers_in, "Content-Length");
585 if (ptr)
586 r->clength = strtol(ptr, NULL, 10);
587
588 /* body */
589 body = &request->body.data.u.str;
590
591 bb = apr_brigade_create(r->pool, c->bucket_alloc);
592 if (bb == NULL)
593 goto out;
594
595 d = defender_bucket_create(body, c->bucket_alloc);
596 if (d == NULL)
597 goto out;
598
599 APR_BRIGADE_INSERT_TAIL(bb, d);
600
601 e = apr_bucket_eos_create(c->bucket_alloc);
602 APR_BRIGADE_INSERT_TAIL(bb, e);
603
604 apr_table_setn(r->notes, DEFENDER_BRIGADE_REQUEST, (char *)bb);
605
606 /* process */
607 status = defender_process_headers(r);
608
609 if (status == DECLINED)
610 status = defender_process_body(r);
611
612 apr_brigade_cleanup(bb);
613
614 /* success */
615 if (status == DECLINED)
616 status = OK;
617
618out:
619
620 if (r && r->pool) {
621 apr_table_clear(r->headers_in);
622 apr_table_clear(r->headers_out);
623 apr_table_clear(r->subprocess_env);
624 apr_table_clear(r->err_headers_out);
625 apr_table_clear(r->notes);
626 apr_pool_destroy(r->pool);
627 }
628
629 if (c && c->pool) {
630 apr_bucket_alloc_destroy(c->bucket_alloc);
631 apr_pool_destroy(c->pool);
632 }
633
634 return status;
635}