blob: c86d25e0a82b62b0fb158fab20a603d30b83a327 [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;
78 struct chunk buf;
79};
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
86 *str = d->buf.str;
87 *len = d->buf.len;
88
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
100static apr_bucket *defender_bucket_make(apr_bucket *b, const struct chunk *buf)
101{
102 struct apr_bucket_defender *d;
103
104 d = apr_bucket_alloc(sizeof(*d), b->list);
105
106 d->buf.str = buf->str;
107 d->buf.len = buf->len;
108 d->buf.size = 0;
109
110 b = apr_bucket_shared_make(b, d, 0, buf->len);
111 b->type = &apr_bucket_type_defender;
112 return b;
113}
114
115static apr_bucket *defender_bucket_create(const struct chunk *buf,
116 apr_bucket_alloc_t *list)
117{
118 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
119
120 APR_BUCKET_INIT(b);
121 b->free = apr_bucket_free;
122 b->list = list;
123 return defender_bucket_make(b, buf);
124}
125
126static void defender_logger(int level, char *str)
127{
128 LOG(&null_worker, "%s", str);
129}
130
131static char *defender_strdup(apr_pool_t *pool, const char *src, uint64_t len)
132{
133 char *dst;
134
135 if (!(dst = apr_pcalloc(pool, len + 1)))
136 return NULL;
137
138 memcpy(dst, src, len);
139 dst[len] = '\0';
140
141 return dst;
142}
143
144static char *defender_printf(apr_pool_t *pool, const char *fmt, ...)
145{
146 va_list argp;
147 char *dst;
148 int len;
149
150 va_start(argp, fmt);
151 len = vsnprintf(NULL, 0, fmt, argp);
152 if (len < 0)
153 return NULL;
154 va_end(argp);
155
156 if (!(dst = apr_pcalloc(pool, len + 1)))
157 return NULL;
158
159 va_start(argp, fmt);
160 len = vsnprintf(dst, len + 1, fmt, argp);
161 va_end(argp);
162
163 return dst;
164}
165
166static char *defender_addr2str(apr_pool_t *pool, struct sample *addr)
167{
168 sa_family_t family;
169 const void *src;
170 char *dst;
171
172 switch (addr->data.type) {
173 case SMP_T_IPV4:
174 src = &addr->data.u.ipv4;
175 family = AF_INET;
176 break;
177 case SMP_T_IPV6:
178 src = &addr->data.u.ipv6;
179 family = AF_INET6;
180 break;
181 default:
182 return NULL;
183 }
184
185 if (!(dst = apr_pcalloc(pool, INET6_ADDRSTRLEN + 1)))
186 return NULL;
187
188 if (inet_ntop(family, src, dst, INET6_ADDRSTRLEN))
189 return dst;
190
191 return NULL;
192}
193
194static void defender_pre_config()
195{
196 apr_pool_t *ptemp = NULL;
197
198 defender_module.module_index = 0;
199 defender_module.register_hooks(defender_pool);
200
201 apr_pool_create(&ptemp, defender_pool);
202 run_ap_hook_post_config(defender_pool, defender_pool, ptemp, server);
203 apr_pool_destroy(ptemp);
204}
205
206static const char *defender_read_config(const char *file)
207{
208 apr_pool_t *ptemp = NULL;
209 const char *err;
210 const char *fullname;
211
212 defender_module_config = defender_module.create_dir_config(defender_pool, "/");
213 if (defender_module_config == NULL) {
214 return "cannot allocate space for the configuration structure";
215 }
216
217 apr_pool_create(&ptemp, defender_pool);
218
219 fullname = ap_server_root_relative(ptemp, file);
220
221 err = read_module_config(server, defender_module_config,
222 defender_module.cmds,
223 defender_pool, ptemp, fullname);
224
225 apr_pool_destroy(ptemp);
226
227 return err;
228}
229
230static void defender_post_config()
231{
232 apr_pool_t *ptemp = NULL;
233
234 apr_pool_create(&ptemp, defender_pool);
235 run_ap_hook_post_config(defender_pool, defender_pool, ptemp, server);
236 apr_pool_destroy(ptemp);
237}
238
239static const char *defender_set_logger(const char *file)
240{
241 char *logname;
242
243 logger = defender_logger;
244
245 if (file == NULL)
246 return NULL;
247
248 logname = ap_server_root_relative(defender_pool, file);
249
250 if (apr_file_open(&server->error_log, logname,
251 APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE,
252 APR_OS_DEFAULT, defender_pool) != APR_SUCCESS) {
253 return apr_pstrcat(defender_pool, "Cannot open log file, ",
254 logname, NULL);
255 }
256 server->error_fname = logname;
257
258 return NULL;
259}
260
261static apr_status_t defender_input_filter(ap_filter_t *f,
262 apr_bucket_brigade *new_bb,
263 ap_input_mode_t mode,
264 apr_read_type_e block,
265 apr_off_t readbytes)
266{
267 apr_bucket_brigade *bb = NULL;
268 apr_bucket *b = NULL, *a = NULL;
269 apr_status_t rv;
270
271 bb = (apr_bucket_brigade *)apr_table_get(f->r->notes, DEFENDER_BRIGADE_REQUEST);
272
273 if (bb == NULL || (bb && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)))) {
274 b = apr_bucket_eos_create(f->c->bucket_alloc);
275 APR_BRIGADE_INSERT_TAIL(new_bb, b);
276 if (bb == NULL)
277 return APR_SUCCESS;
278 }
279
280 rv = apr_brigade_partition(bb, readbytes, &a);
281 if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
282 return rv;
283
284 b = APR_BRIGADE_FIRST(bb);
285
286 while (b != a) {
287 if (APR_BUCKET_IS_EOS(b))
288 ap_remove_input_filter(f);
289
290 APR_BUCKET_REMOVE(b);
291 APR_BRIGADE_INSERT_TAIL(new_bb, b);
292 b = APR_BRIGADE_FIRST(bb);
293 }
294
295 return APR_SUCCESS;
296}
297
298static conn_rec *defender_create_conn()
299{
300 conn_rec *c = NULL;
301 apr_pool_t *ptrans = NULL;
302
303 apr_pool_create(&ptrans, defender_pool);
304
305 c = apr_pcalloc(ptrans, sizeof(conn_rec));
306
307 c->pool = ptrans;
308 c->local_ip = "127.0.0.1";
309 c->local_addr = server->addrs->host_addr;
310 c->local_host = defender_name;
311 c->client_addr = server->addrs->host_addr;
312 c->remote_host = defender_name;
313
314 c->id = 1;
315 c->base_server = server;
316 c->bucket_alloc = apr_bucket_alloc_create(ptrans);
317
318 return c;
319}
320
321static request_rec *defender_create_request(conn_rec *conn)
322{
323 request_rec *r = NULL;
324 apr_pool_t *p = NULL;
325 struct ap_logconf *l;
326
327 apr_pool_create(&p, conn->pool);
328
329 r = apr_pcalloc(p, sizeof(request_rec));
330
331 r->pool = p;
332 r->connection = conn;
333 r->server = conn->base_server;
334
335 r->headers_in = apr_table_make(p, 25);
336 r->headers_out = apr_table_make(p, 12);
337 r->subprocess_env = apr_table_make(p, 25);
338 r->err_headers_out = apr_table_make(p, 5);
339 r->notes = apr_table_make(p, 5);
340
341 r->request_config = apr_palloc(p, sizeof(void *));
342 r->per_dir_config = apr_palloc(p, sizeof(void *));
343 ((void **)r->per_dir_config)[0] = defender_module_config;
344
345 r->handler = defender_name;
346
347 r->parsed_uri.scheme = "http";
348 r->parsed_uri.is_initialized = 1;
349 r->parsed_uri.port = 80;
350 r->parsed_uri.port_str = "80";
351 r->parsed_uri.fragment = "";
352
353 r->input_filters = NULL;
354 r->output_filters = NULL;
355
356 l = apr_pcalloc(p, sizeof(struct ap_logconf));
357 l->level = APLOG_DEBUG;
358 r->log = l;
359
360 return r;
361}
362
363static int defender_process_headers(request_rec *r)
364{
365 return run_ap_hook_header_parser(r);
366}
367
368static int defender_process_body(request_rec *r)
369{
370 ap_add_input_filter(DEFENDER_INPUT_FILTER, NULL, r, r->connection);
371 return run_ap_hook_fixups(r);
372}
373
374int defender_init(const char *config_file, const char *log_file)
375{
376 apr_status_t rv;
377 const char *msg;
378
379 if (!config_file) {
380 LOG(&null_worker, "Mod Defender configuration file not specified.\n");
381 return 0;
382 }
383
384 apr_initialize();
385 apr_pool_create(&defender_pool, NULL);
386 apr_hook_global_pool = defender_pool;
387
388 ap_server_root = getcwd(defender_cwd, APR_PATH_MAX);
389
390 server = (server_rec *) apr_palloc(defender_pool, sizeof(server_rec));
391 server->process = apr_palloc(defender_pool, sizeof(process_rec));
392 server->process->argc = 1;
393 server->process->argv = defender_argv;
394 server->process->short_name = defender_name;
395 server->process->pconf = defender_pool;
396 server->process->pool = defender_pool;
397
398 server->addrs = apr_palloc(defender_pool, sizeof(server_addr_rec));
399 rv = apr_sockaddr_info_get(&server->addrs->host_addr,
400 "127.0.0.1", APR_UNSPEC, 0, 0,
401 defender_pool);
402 if (rv != APR_SUCCESS) {
403 LOG(&null_worker, "Mod Defender getaddrinfo failed.\n");
404 return 0;
405 }
406
407 server->path = "/";
408 server->pathlen = strlen(server->path);
409 server->port = 0;
410 server->server_admin = defender_name;
411 server->server_scheme = "";
412 server->error_fname = NULL;
413 server->error_log = NULL;
414 server->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
415 server->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
416 server->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
417 server->timeout = apr_time_from_sec(DEFAULT_TIMEOUT);
418
419 memset(hostname, 0, sizeof(hostname));
420 gethostname(hostname, sizeof(hostname) - 1);
421 server->server_hostname = hostname;
422
423 server->addrs->host_port = 0;
424 server->names = server->wild_names = NULL;
425 server->is_virtual = 0;
426
427 server->lookup_defaults = NULL;
428 server->module_config = NULL;
429
430 msg = defender_set_logger(log_file);
431 if (msg != NULL) {
432 LOG(&null_worker, "Mod Defender init failed: %s\n", msg);
433 return 0;
434 }
435
436 ap_register_input_filter(DEFENDER_INPUT_FILTER, defender_input_filter,
437 NULL, AP_FTYPE_RESOURCE);
438
439 defender_pre_config();
440
441 msg = defender_read_config(config_file);
442 if (msg != NULL) {
443 LOG(&null_worker, "Mod Defender configuration failed: %s\n", msg);
444 return 0;
445 }
446
447 defender_post_config();
448
449 return 1;
450}
451
452int defender_process_request(struct worker *worker, struct defender_request *request)
453{
454 struct conn_rec *c = NULL;
455 struct request_rec *r = NULL;
456
457 struct apr_bucket_brigade *bb = NULL;
458 struct apr_bucket *d = NULL, *e = NULL;
459
460 struct chunk *method;
461 struct chunk *path;
462 struct chunk *query;
463 struct chunk *version;
464 struct chunk *body;
465
466 struct defender_header hdr;
467 char *hdr_ptr, *hdr_end;
468
469 const char *ptr;
470
471 int status = DECLINED;
472
473 if (!(c = defender_create_conn()))
474 goto out;
475
476 if (!(r = defender_create_request(c)))
477 goto out;
478
479 /* request */
480 r->request_time = apr_time_now();
481
482 if (request->clientip.data.type != SMP_T_IPV4 &&
483 request->clientip.data.type != SMP_T_IPV6)
484 goto out;
485
486 if (!(r->useragent_ip = defender_addr2str(r->pool, &request->clientip)))
487 goto out;
488
489 if (request->id.data.u.str.str && request->id.data.u.str.len > 0) {
490 apr_table_setn(r->subprocess_env, "UNIQUE_ID",
491 defender_strdup(r->pool, request->id.data.u.str.str,
492 request->id.data.u.str.len));
493 }
494 else {
495 apr_table_setn(r->subprocess_env, "UNIQUE_ID",
496 DEFENDER_DEFAULT_UNIQUE_ID);
497 }
498
499 method = &request->method.data.u.str;
500 path = &request->path.data.u.str;
501 query = &request->query.data.u.str;
502 version = &request->version.data.u.str;
503
504 r->method_number = lookup_builtin_method(method->str, method->len);
505 if (!(r->method = defender_strdup(r->pool, method->str, method->len)))
506 goto out;
507
508 r->unparsed_uri = defender_printf(r->pool, "%.*s%s%.*s",
509 path->len, path->str,
510 query->len > 0 ? "?" : "",
511 query->len, query->str);
512 if (!r->unparsed_uri)
513 goto out;
514
515 if (!(r->uri = defender_strdup(r->pool, path->str, path->len)))
516 goto out;
517
518 r->parsed_uri.path = r->filename = r->uri;
519
520 if (!(r->args = defender_strdup(r->pool, query->str, query->len)))
521 goto out;
522
523 r->parsed_uri.query = r->args;
524
525 r->protocol = defender_printf(r->pool, "%s%.*s",
526 version->len > 0 ? "HTTP/" : "",
527 version->len, version->str);
528 if (!r->protocol)
529 goto out;
530
531 r->the_request = defender_printf(r->pool, "%.*s %s%s%s",
532 method->len, method->str,
533 r->unparsed_uri,
534 version->len > 0 ? " " : "",
535 r->protocol);
536 if (!r->the_request)
537 goto out;
538
539 /* headers */
540 if (request->headers.data.type != SMP_T_BIN)
541 goto misc;
542
543 hdr_ptr = request->headers.data.u.str.str;
544 hdr_end = hdr_ptr + request->headers.data.u.str.len;
545
546 while (1) {
547 memset(&hdr, 0, sizeof(hdr));
548
549 if (decode_varint(&hdr_ptr, hdr_end, &hdr.name.len) == -1)
550 goto out;
551 if (!(hdr.name.str = defender_strdup(r->pool, hdr_ptr, hdr.name.len)))
552 goto out;
553
554 hdr_ptr += hdr.name.len;
555 if (hdr_ptr > hdr_end)
556 goto out;
557
558 if (decode_varint(&hdr_ptr, hdr_end, &hdr.value.len) == -1)
559 goto out;
560 if (!(hdr.value.str = defender_strdup(r->pool, hdr_ptr, hdr.value.len)))
561 goto out;
562
563 hdr_ptr += hdr.value.len;
564 if (hdr_ptr > hdr_end)
565 goto out;
566
567 if (!hdr.name.len && !hdr.value.len)
568 break;
569
570 apr_table_setn(r->headers_in, hdr.name.str, hdr.value.str);
571 }
572
573misc:
574
575 r->hostname = apr_table_get(r->headers_in, "Host");
576 if (!r->hostname)
577 r->hostname = defender_unknown_hostname;
578 r->parsed_uri.hostname = (char *)r->hostname;
579
580 r->content_type = apr_table_get(r->headers_in, "Content-Type");
581 r->content_encoding = apr_table_get(r->headers_in, "Content-Encoding");
582 ptr = apr_table_get(r->headers_in, "Content-Length");
583 if (ptr)
584 r->clength = strtol(ptr, NULL, 10);
585
586 /* body */
587 body = &request->body.data.u.str;
588
589 bb = apr_brigade_create(r->pool, c->bucket_alloc);
590 if (bb == NULL)
591 goto out;
592
593 d = defender_bucket_create(body, c->bucket_alloc);
594 if (d == NULL)
595 goto out;
596
597 APR_BRIGADE_INSERT_TAIL(bb, d);
598
599 e = apr_bucket_eos_create(c->bucket_alloc);
600 APR_BRIGADE_INSERT_TAIL(bb, e);
601
602 apr_table_setn(r->notes, DEFENDER_BRIGADE_REQUEST, (char *)bb);
603
604 /* process */
605 status = defender_process_headers(r);
606
607 if (status == DECLINED)
608 status = defender_process_body(r);
609
610 apr_brigade_cleanup(bb);
611
612 /* success */
613 if (status == DECLINED)
614 status = OK;
615
616out:
617
618 if (r && r->pool) {
619 apr_table_clear(r->headers_in);
620 apr_table_clear(r->headers_out);
621 apr_table_clear(r->subprocess_env);
622 apr_table_clear(r->err_headers_out);
623 apr_table_clear(r->notes);
624 apr_pool_destroy(r->pool);
625 }
626
627 if (c && c->pool) {
628 apr_bucket_alloc_destroy(c->bucket_alloc);
629 apr_pool_destroy(c->pool);
630 }
631
632 return status;
633}