1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License as published by
3 * the Free Software Foundation; version 2 of the License. For a copy,
4 * see http://www.gnu.org/licenses/gpl-2.0.html.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 */
11
12 #include "config.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdbool.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <pthread.h>
19 #include <pwd.h>
20 #include <regex.h>
21 #include <sys/socket.h>
22 #include "global.h"
23 #include "alternative.h"
24 #include "client.h"
25 #include "libstr.h"
26 #include "liblist.h"
27 #include "session.h"
28 #include "log.h"
29 #include "monitor.h"
30 #include "tomahawk.h"
31 #include "memdbg.h"
32
33 static const struct {
34 const char *text;
35 } sqli_detection[] = {
36 {"'[[:space:]]*(;[[:space:]]*)?--([[:space:]]|')"},
37 {"[[:space:]]+(and|or|xor|&&|\\|\\|)[[:space:]]*\\(?[[:space:]]*('|[0-9]|`?[a-z\\._-]+`?[[:space:]]*(=|like)|[a-z]+[[:space:]]*\\()"},
38 {"[[:space:]]+(not[[:space:]]+)?in[[:space:]]*\\([[:space:]]*['0-9]"},
39 {"union([[:space:]]+all)?([[:space:]]*\\([[:space:]]*|[[:space:]]+)select(`|[[:space:]])"},
40 {"select([[:space:]]*`|[[:space:]]+)(\\*|[a-z0-9_\\, ]*)(`[[:space:]]*|[[:space:]]+)from([[:space:]]*`|[[:space:]]+)[a-z0-9_\\.]*"},
41 {"insert[[:space:]]+into([[:space:]]*`|[[:space:]]+).*(`[[:space:]]*|[[:space:]]+)(values[[:space:]]*)?\\(.*\\)"},
42 {"update([[:space:]]*`|[[:space:]]+)[a-z0-9_\\.]*(`[[:space:]]*|[[:space:]]+)set([[:space:]]*`|[[:space:]]+).*="},
43 {"delete[[:space:]]+from([[:space:]]*`|[[:space:]]+)[a-z0-9_\\.]*`?"},
44 {"extractvalue[[:space:]]*\\([[:space:]]*[0-9'\"@]"},
45 {NULL}
46 };
47
48 static const struct {
49 const char *text;
50 } sql_operators[] = {
51 {"="}, {":="}, {"&"}, {"~"}, {"|"}, {"^"}, {"/"}, {"<"}, {"="}, {">"},
52 {"-"}, {"%"}, {"!"}, {"+"}, {"*"}, {"and"}, {"between"}, {"binary"},
53 {"case"}, {"div"}, {"in"}, {"is"}, {"like"}, {"mod"}, {"not"}, {"or"},
54 {"order"}, {"regexp"}, {"rlike"}, {"sounds"}, {"xor"},
55 {NULL}
56 };
57
58 typedef struct type_sqli_pattern {
59 regex_t regex;
60 struct type_sqli_pattern *next;
61 } t_sqli_pattern;
62
63 t_sqli_pattern *sqli_patterns = NULL;
64 static int new_client_id = 0;
65
66 /* Set the entries in a session-record to the default values.
67 */
clear_session(t_session * session)68 static void clear_session(t_session *session) {
69 session->time = time(NULL);
70 session->cgi_type = no_cgi;
71 session->cgi_handler = NULL;
72 session->fcgi_server = NULL;
73 session->method = NULL;
74 session->uri = NULL;
75 session->uri_len = 0;
76 session->uri_is_dir = false;
77 session->parsing_oke = false;
78 session->request_uri = NULL;
79 session->request_method = unknown;
80 session->extension = NULL;
81 session->encode_gzip = false;
82 session->path_info = NULL;
83 session->alias = NULL;
84 session->script_alias = NULL;
85 session->vars = NULL;
86 session->http_version = NULL;
87 session->http_headers = NULL;
88 session->body = NULL;
89 session->local_user = NULL;
90 session->header_sent = false;
91 session->data_sent = false;
92 session->cause_of_30x = missing_slash;
93 session->header_length = 0;
94 session->content_length = 0;
95 session->file_on_disk = NULL;
96 session->mimetype = NULL;
97 session->hostname = NULL;
98 session->host = session->config->first_host;
99 session->host_copied = false;
100 session->throttle = 0;
101 session->throttle_timer = 0;
102 session->bytecounter = 0;
103 session->part_of_dirspeed = false;
104 session->remote_user = NULL;
105 session->http_auth = no_auth;
106 session->directory = NULL;
107 session->handling_error = false;
108 session->reason_for_403 = "";
109 session->cookies = NULL;
110 session->bytes_sent = 0;
111 session->output_size = 0;
112 session->return_code = 200;
113 session->error_cause = ec_NONE;
114 session->error_code = -1;
115 session->log_request = true;
116 session->tempdata = NULL;
117 session->uploaded_file = NULL;
118 session->location = NULL;
119 session->send_date = true;
120 session->send_expires = false;
121 session->expires = -1;
122 session->caco_private = true;
123 session->letsencrypt_auth_request = false;
124 #ifdef ENABLE_TOOLKIT
125 session->toolkit_fastcgi = NULL;
126 #endif
127 #ifdef ENABLE_DEBUG
128 session->current_task = NULL;
129 #endif
130 }
131
132 /* Initialize a session-record.
133 */
init_session(t_session * session)134 void init_session(t_session *session) {
135 #ifdef ENABLE_TOMAHAWK
136 increment_counter(COUNTER_CLIENT);
137 #endif
138 if ((session->client_id = new_client_id++) == MAX_CHILD_ID) {
139 new_client_id = 0;
140 }
141 session->request_limit = true;
142 session->force_quit = false;
143 session->keep_alive = false;
144 session->kept_alive = 0;
145
146 session->last_host = NULL;
147 session->request = NULL;
148 session->buffer_size = 0;
149 session->bytes_in_buffer = 0;
150
151 clear_session(session);
152
153 session->socket_open = false;
154 session->via_trusted_proxy = false;
155 session->flooding_timer = session->time;
156
157 #ifdef ENABLE_RPROXY
158 session->rproxy_kept_alive = false;
159 #endif
160
161 #ifdef ENABLE_HTTP2
162 session->use_http2 = false;
163 #endif
164 }
165
166 /* Reset a session-record for reuse.
167 */
reset_session(t_session * session)168 void reset_session(t_session *session) {
169 long size;
170
171 check_clear_free(session->file_on_disk, CHECK_USE_STRLEN);
172 #ifdef CIFS
173 check_free(session->extension);
174 #endif
175 check_clear_free(session->local_user, CHECK_USE_STRLEN);
176 check_clear_free(session->remote_user, CHECK_USE_STRLEN);
177 check_free(session->path_info);
178 check_free(session->request_uri);
179 check_free(session->location);
180 if (session->uploaded_file != NULL) {
181 unlink(session->uploaded_file);
182 free(session->uploaded_file);
183 }
184 session->http_headers = remove_http_headers(session->http_headers);
185 if (session->directory != NULL) {
186 pthread_mutex_lock(&(session->directory->client_mutex));
187 if (session->part_of_dirspeed) {
188 if (--session->directory->nr_of_clients == 0) {
189 session->directory->session_speed = session->directory->upload_speed;
190 } else {
191 session->directory->session_speed = session->directory->upload_speed / session->directory->nr_of_clients;
192 }
193 }
194 pthread_mutex_unlock(&(session->directory->client_mutex));
195 }
196
197 #ifdef ENABLE_TOOLKIT
198 if (session->host->toolkit_rules_user != NULL) {
199 free(session->host->toolkit_rules_user);
200 }
201
202 remove_charlist(&(session->host->toolkit_rules_user_str));
203 #endif
204
205 /* HTTP pipelining
206 */
207 size = session->header_length + session->content_length;
208 if ((session->bytes_in_buffer > size) && session->keep_alive) {
209 session->bytes_in_buffer -= size;
210 memmove(session->request, session->request + size, session->bytes_in_buffer);
211 *(session->request + session->bytes_in_buffer) = '\0';
212 } else {
213 check_clear_free(session->request, session->buffer_size);
214 session->request = NULL;
215 session->buffer_size = 0;
216 session->bytes_in_buffer = 0;
217 }
218
219 remove_tempdata(session->tempdata);
220 if (session->host_copied) {
221 free(session->host);
222 }
223
224 clear_session(session);
225 }
226
227 /* Free all remaining buffers
228 */
destroy_session(t_session * session)229 void destroy_session(t_session *session) {
230 #ifdef ENABLE_RPROXY
231 if (session->rproxy_kept_alive) {
232 #ifdef ENABLE_TLS
233 if (session->rproxy_use_tls) {
234 tls_close(&(session->rproxy_tls));
235 }
236 #endif
237 close(session->rproxy_socket);
238 session->rproxy_kept_alive = false;
239 }
240 #endif
241
242 check_free(session->request);
243 session->request = NULL;
244 }
245
246 /* Determine the request method
247 */
determine_request_method(t_session * session)248 void determine_request_method(t_session *session) {
249 if (strncmp(session->request, "GET ", 4) == 0) {
250 session->request_method = GET;
251 } else if (strncmp(session->request, "POST ", 5) == 0) {
252 session->request_method = POST;
253 } else if (strncmp(session->request, "BREW ", 5) == 0) {
254 session->request_method = POST;
255 } else if (strncmp(session->request, "HEAD ", 5) == 0) {
256 session->request_method = HEAD;
257 } else if (strncmp(session->request, "TRACE ", 6) == 0) {
258 session->request_method = TRACE;
259 } else if (strncmp(session->request, "PUT ", 4) == 0) {
260 session->request_method = PUT;
261 } else if (strncmp(session->request, "DELETE ", 7) == 0) {
262 session->request_method = DELETE;
263 } else if (strncmp(session->request, "CONNECT ", 8) == 0) {
264 session->request_method = CONNECT;
265 } else if (strncmp(session->request, "WHEN ", 5) == 0) {
266 session->request_method = WHEN;
267 } else if (strncmp(session->request, "OPTIONS ", 8) == 0) {
268 session->request_method = unsupported;
269 } else if (strncmp(session->request, "PROPFIND ", 9) == 0) {
270 session->request_method = unsupported;
271 } else if (strncmp(session->request, "PROPPATCH ", 10) == 0) {
272 session->request_method = unsupported;
273 } else if (strncmp(session->request, "MKCOL ", 6) == 0) {
274 session->request_method = unsupported;
275 } else if (strncmp(session->request, "COPY ", 5) == 0) {
276 session->request_method = unsupported;
277 } else if (strncmp(session->request, "MOVE ", 5) == 0) {
278 session->request_method = unsupported;
279 } else if (strncmp(session->request, "LOCK ", 5) == 0) {
280 session->request_method = unsupported;
281 } else if (strncmp(session->request, "UNLOCK ", 7) == 0) {
282 session->request_method = unsupported;
283 } else if (strncmp(session->request, "REPORT ", 7) == 0) {
284 session->request_method = unsupported;
285 } else if (strncmp(session->request, "PATCH ", 6) == 0) {
286 session->request_method = unsupported;
287 } else if (strncmp(session->request, "MKCALENDAR ", 11) == 0) {
288 session->request_method = unsupported;
289 }
290 }
291
292 /* Get the extension of the file to be processed
293 */
get_target_extension(t_session * session)294 int get_target_extension(t_session *session) {
295 char *last_slash;
296
297 #ifdef CIFS
298 check_free(session->extension);
299 session->extension = NULL;
300 #endif
301
302 if ((last_slash = strrchr(session->file_on_disk, '/')) == NULL) {
303 return -1;
304 }
305
306 if ((session->extension = strrchr(last_slash, '.')) != NULL) {
307 session->extension++;
308 } else if (session->letsencrypt_auth_request == false) {
309 session->extension = session->host->no_extension_as;
310 }
311
312 #ifdef CIFS
313 if (session->extension != NULL) {
314 if ((session->extension = strdup(session->extension)) == NULL) {
315 return -1;
316 }
317 strlower(session->extension);
318 }
319 #endif
320
321 return 0;
322 }
323
324 /* Return the path of the user's homedirectory.
325 */
get_homedir(t_session * session,char * username)326 int get_homedir(t_session *session, char *username) {
327 struct passwd *pwd;
328 size_t len;
329 char *old_root;
330
331 if (username == NULL) {
332 return 500;
333 } else if ((pwd = getpwnam(username)) == NULL) {
334 return 404;
335 }
336
337 old_root = session->host->website_root;
338
339 len = strlen(pwd->pw_dir) + strlen(session->config->user_directory) + 1;
340 if ((session->host->website_root = (char*)malloc(len + 1)) == NULL) {
341 session->host->website_root = old_root;
342 return -1;
343 }
344
345 sprintf(session->host->website_root, "%s/%s", pwd->pw_dir, session->config->user_directory);
346 if (register_tempdata(&(session->tempdata), session->host->website_root, tc_data) == -1) {
347 free(session->host->website_root);
348 session->host->website_root = old_root;
349 return -1;
350 }
351 session->host->website_root_len = strlen(session->host->website_root);
352
353 return 200;
354 }
355
356 /* Duplicate the active host-record. The duplicate can now savely be altered
357 * and will be used during the session.
358 */
duplicate_host(t_session * session)359 bool duplicate_host(t_session *session) {
360 t_host *new_host;
361
362 if ((session->host != NULL) && (session->host_copied == false)) {
363 if ((new_host = (t_host*)malloc(sizeof(t_host))) == NULL) {
364 return false;
365 }
366
367 memcpy(new_host, session->host, sizeof(t_host));
368 new_host->next = NULL;
369 session->host = new_host;
370 session->host_copied = true;
371 }
372
373 return true;
374 }
375
376 #ifdef ENABLE_TOOLKIT
load_user_root_config(t_session * session)377 int load_user_root_config(t_session *session) {
378 char *conffile;
379 int result;
380
381 if ((conffile = malloc(session->host->website_root_len + 11)) == NULL) {
382 return -1;
383 }
384
385 memcpy(conffile, session->host->website_root, session->host->website_root_len);
386 memcpy(conffile + session->host->website_root_len, "/.hiawatha\0", 11);
387
388 if ((result = read_user_configfile(conffile, session->host, &(session->tempdata), only_root_config)) != 0) {
389 log_error_file(session, conffile, "error in configuration file on line %d", result);
390 result = -1;
391 }
392
393 free(conffile);
394
395 if (toolkit_rules_str_to_ptr(session->config->url_toolkit, &(session->host->toolkit_rules_user_str), &(session->host->toolkit_rules_user)) == -1) {
396 result = -1;
397 }
398
399 return result;
400 }
401 #endif
402
403 /* Load configfile from directories
404 */
load_user_config(t_session * session)405 int load_user_config(t_session *session) {
406 char *search, *conffile;
407 size_t length;
408 int result;
409 t_user_config_mode read_mode;
410
411 if (session->file_on_disk == NULL) {
412 return 0;
413 } else if ((length = strlen(session->file_on_disk)) <= 1) {
414 return 0;
415 } else if ((conffile = (char*)malloc(length + 10)) == NULL) {
416 return -1;
417 }
418
419 search = session->file_on_disk + 1;
420 while (*search != '\0') {
421 if (*search == '/') {
422 length = search - session->file_on_disk + 1;
423
424 if (length - 1 != session->host->website_root_len) {
425 read_mode = non_root_config;
426 } else if (memcmp(session->file_on_disk, session->host->website_root, length - 1) != 0) {
427 read_mode = non_root_config;
428 } else {
429 read_mode = ignore_root_config;
430 }
431
432 memcpy(conffile, session->file_on_disk, length);
433 memcpy(conffile + length, ".hiawatha\0", 10);
434 result = read_user_configfile(conffile, session->host, &(session->tempdata), read_mode);
435
436 if (result != 0) {
437 log_error_file(session, conffile, "error in configuration file on line %d", result);
438 free(conffile);
439 return -1;
440 }
441 }
442
443 search++;
444 }
445
446 free(conffile);
447
448 return 0;
449 }
450
451 /* Copy the settings from a directory-record to the active host-record.
452 */
copy_directory_settings(t_session * session)453 int copy_directory_settings(t_session *session) {
454 size_t path_length;
455 t_directory *dir;
456 int d, p;
457
458 if (session->host->directory == NULL) {
459 return 200;
460 }
461
462 for (d = 0; session->host->directory[d] != NULL; d++) {
463 dir = session->host->directory[d];
464
465 for (p = 0; p < dir->path.size; p++) {
466 path_length = strlen(dir->path.item[p]);
467
468 if (strncmp(session->request_uri, dir->path.item[p], path_length) != 0) {
469 continue;
470 }
471
472 if (dir->path.item[p][strlen(dir->path.item[p]) - 1] != '/') {
473 if (*(session->request_uri + path_length) != '/') {
474 continue;
475 }
476 }
477
478 if ((session->extension != NULL) && dir->extensions.size > 0) {
479 if (session->uri_len > 0 ? session->uri[session->uri_len - 1] != '/' : false) {
480 if (in_charlist(session->extension, &(dir->extensions)) == false) {
481 continue;
482 }
483 }
484 }
485
486 session->directory = dir;
487
488 if (dir->max_clients > -1) {
489 pthread_mutex_lock(&(dir->client_mutex));
490 if (dir->nr_of_clients < dir->max_clients) {
491 session->throttle = dir->session_speed = dir->upload_speed / ++dir->nr_of_clients;
492 pthread_mutex_unlock(&(dir->client_mutex));
493 session->part_of_dirspeed = true;
494 } else {
495 pthread_mutex_unlock(&(dir->client_mutex));
496 return 503;
497 }
498 }
499 if (dir->wrap_cgi != NULL) {
500 session->host->wrap_cgi = dir->wrap_cgi;
501 }
502 if (dir->start_file != NULL) {
503 session->host->start_file = dir->start_file;
504 }
505 if (dir->execute_cgi_set) {
506 session->host->execute_cgi = dir->execute_cgi;
507 }
508 #ifdef ENABLE_XSLT
509 if (dir->show_index_set) {
510 session->host->show_index = dir->show_index;
511 }
512 #endif
513 if (dir->follow_symlinks_set) {
514 session->host->follow_symlinks = dir->follow_symlinks;
515 }
516 if (dir->access_list != NULL) {
517 session->host->access_list = dir->access_list;
518 }
519 if (dir->alter_list != NULL) {
520 session->host->alter_list = dir->alter_list;
521 }
522 if (dir->alter_fmode != 0) {
523 session->host->alter_fmode = dir->alter_fmode;
524 }
525 if (dir->passwordfile != NULL) {
526 session->host->auth_method = dir->auth_method;
527 session->host->passwordfile = dir->passwordfile;
528 if (dir->groupfile != NULL) {
529 session->host->groupfile = dir->groupfile;
530 }
531 }
532 if (dir->required_group.size > 0) {
533 session->host->required_group.size = dir->required_group.size;
534 session->host->required_group.item = dir->required_group.item;
535 }
536 if (dir->alter_group.size > 0) {
537 session->host->alter_group.size = dir->alter_group.size;
538 session->host->alter_group.item = dir->alter_group.item;
539 }
540 if (dir->time_for_cgi > TIMER_OFF) {
541 session->host->time_for_cgi = dir->time_for_cgi;
542 }
543 if (dir->expires > -1) {
544 session->expires = dir->expires;
545 session->caco_private = dir->caco_private;
546 }
547
548 break;
549 }
550 }
551
552 return 200;
553 }
554
555 /* Remove port from hostname
556 */
remove_port_from_hostname(t_session * session)557 int remove_port_from_hostname(t_session *session) {
558 char *c, *hostname;
559
560 if (session->hostname == NULL) {
561 return -1;
562 }
563
564 if (session->binding->interface.family == AF_INET6) {
565 if ((*(session->hostname) == '[') && ((c = strchr(session->hostname, ']')) != NULL)) {
566 c++;
567
568 if (*c == ':') {
569 if ((hostname = strdup(session->hostname)) == NULL) {
570 return -1;
571 } else if (register_tempdata(&(session->tempdata), hostname, tc_data) == -1) {
572 free(hostname);
573 return -1;
574 }
575
576 *(hostname + (c - session->hostname)) = '\0';
577 session->hostname = hostname;
578 }
579
580 return 0;
581 }
582 }
583
584 if ((c = strrchr(session->hostname, ':')) != NULL) {
585 if (c == session->hostname) {
586 return 0;
587 }
588
589 if ((hostname = strdup(session->hostname)) == NULL) {
590 return -1;
591 } else if (register_tempdata(&(session->tempdata), hostname, tc_data) == -1) {
592 free(hostname);
593 return -1;
594 }
595
596 *(hostname + (c - session->hostname)) = '\0';
597 session->hostname = hostname;
598 }
599
600 return 0;
601 }
602
603 /* Prevent cross-site scripting.
604 */
605
prevent_xss_str(t_session * session,char * input)606 static int prevent_xss_str(t_session *session, char *input) {
607 int result = 0;
608 short low, high;
609 char *str, value;
610 char tag[22];
611
612 str = input;
613
614 while (*str != '\0') {
615 if ((value = *str) == '%') {
616 if ((high = hex_char_to_int(*(str + 1))) != -1) {
617 if ((low = hex_char_to_int(*(str + 2))) != -1) {
618 value = (char)(high<<4) + low;
619 str += 2;
620 }
621 }
622 }
623
624 if (value == '<') {
625 str++;
626 strncpy(tag, str, 21);
627 tag[21] = '\0';
628 url_decode(tag);
629 strlower(tag);
630
631 if ((memcmp(tag, "script", 6) == 0) && ((tag[6] == ' ') || (tag[6] == '>'))) {
632 log_exploit_attempt(session, "XSS", input);
633 #ifdef ENABLE_TOMAHAWK
634 increment_counter(COUNTER_EXPLOIT);
635 #endif
636 #ifdef ENABLE_MONITOR
637 if (session->config->monitor_enabled) {
638 monitor_count_exploit_attempt(session);
639 monitor_event("XSS attempt for %s%s", session->host->hostname.item[0], session->uri);
640 }
641 #endif
642
643 if (session->host->prevent_xss == p_prevent) {
644 *str = '_';
645 }
646
647 result = 1;
648
649 break;
650 }
651 }
652
653 str++;
654 }
655
656 return result;
657 }
658
prevent_xss(t_session * session)659 int prevent_xss(t_session *session) {
660 int result = 0;
661
662 if (session->vars != NULL) {
663 result += prevent_xss_str(session, session->vars);
664 }
665
666 if ((session->body != NULL) && session->request_limit) {
667 result += prevent_xss_str(session, session->body);
668 }
669
670 return result;
671 }
672
673 /* Initialize SQL injection detection
674 */
init_sqli_detection(void)675 int init_sqli_detection(void) {
676 t_sqli_pattern *prev;
677 int i;
678
679 for (i = 0; sqli_detection[i].text != NULL; i++) {
680 prev = sqli_patterns;
681 if ((sqli_patterns = (t_sqli_pattern*)malloc(sizeof(t_sqli_pattern))) == NULL) {
682 return -1;
683 }
684 sqli_patterns->next = prev;
685
686 if (regcomp(&(sqli_patterns->regex), sqli_detection[i].text, REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) {
687 fprintf(stderr, "Error in regexp %s\n", sqli_detection[i].text);
688 return -1;
689 }
690 }
691
692 return 0;
693 }
694
log_sqli_attempt(t_session * session,char * str)695 static void log_sqli_attempt(t_session *session, char *str) {
696 log_exploit_attempt(session, "SQLi", str);
697 #ifdef ENABLE_TOMAHAWK
698 increment_counter(COUNTER_EXPLOIT);
699 #endif
700 #ifdef ENABLE_MONITOR
701 if (session->config->monitor_enabled) {
702 monitor_count_exploit_attempt(session);
703 monitor_event("SQLi attempt for %s%s", session->host->hostname.item[0], session->uri);
704 }
705 #endif
706 }
707
708 /* Prevent SQL injection
709 */
prevent_sqli_str(t_session * session,char * str,int length)710 static int prevent_sqli_str(t_session *session, char *str, int length) {
711 char *data, *c, *begin, *end;
712 t_sqli_pattern *pattern;
713 int result = 0, i;
714
715 if ((str == NULL) || (length <= 0)) {
716 return 0;
717 }
718
719 if ((data = (char*)malloc(length + 1)) == NULL) {
720 return -1;
721 }
722
723 memcpy(data, str, length);
724 for (i = 0; i < length; i++) {
725 if (data[i] == '\0') {
726 data[i] = ' ';
727 } else if (strncmp(data + i, "%00", 3) == 0) {
728 data[i + 1] = '2';
729 }
730 }
731 data[length] = '\0';
732
733 url_decode(data);
734
735 c = data;
736 while (*c != '\0') {
737 if (*c == '+') {
738 *c = ' ';
739 }
740 c++;
741 }
742
743 /* Remove comments
744 */
745 end = data;
746 while ((begin = strstr(end, "/*")) != NULL) {
747 if ((end = strstr(begin + 2, "*/")) == NULL) {
748 break;
749 }
750 end += 2;
751 if (*(begin + 2) != '!') {
752 memset(begin, ' ', end - begin);
753 }
754 }
755
756 /* Remove double parenthesis
757 */
758 end = data;
759 while ((begin = strchr(end, '(')) != NULL) {
760 end = begin + 1;
761 while (*end == ' ') {
762 end++;
763 }
764 if (*end == '(') {
765 *begin = ' ';
766 }
767 }
768
769 /* SQL operators
770 */
771 if ((c = strchr(data, '\'')) != NULL) {
772 do {
773 c++;
774 } while ((*c == ' ') || (*c == '\t'));
775
776 for (i = 0; sql_operators[i].text != NULL; i++) {
777 if (strncasecmp(c, sql_operators[i].text, strlen(sql_operators[i].text)) == 0) {
778 log_sqli_attempt(session, str);
779 result = 1;
780 goto sqli_done;
781 }
782 }
783 }
784
785 /* Match patterns
786 */
787 pattern = sqli_patterns;
788 while (pattern != NULL) {
789 if (regexec(&(pattern->regex), data, 0, NULL, 0) != REG_NOMATCH) {
790 log_sqli_attempt(session, str);
791 result = 1;
792 goto sqli_done;
793 }
794
795 pattern = pattern->next;
796 }
797
798 sqli_done:
799 free(data);
800
801 return result;
802 }
803
prevent_sqli(t_session * session)804 int prevent_sqli(t_session *session) {
805 int result;
806
807 if (session->request_limit == false) {
808 return 0;
809 }
810
811 if (session->request_uri != NULL) {
812 if ((result = prevent_sqli_str(session, session->request_uri, strlen(session->request_uri))) != 0) {
813 return result;
814 }
815 }
816
817 if (session->body != NULL) {
818 if ((result = prevent_sqli_str(session, session->body, session->content_length)) != 0) {
819 return result;
820 }
821 }
822
823 if (session->cookies != NULL) {
824 if ((result = prevent_sqli_str(session, session->cookies, strlen(session->cookies))) != 0) {
825 return result;
826 }
827 }
828
829 return 0;
830 }
831
832 /* Prevent Cross-site Request Forgery
833 */
prevent_csrf(t_session * session)834 int prevent_csrf(t_session *session) {
835 char *referer, *slash, prev = '\0';
836 int i, n;
837 #ifdef ENABLE_MONITOR
838 char *csrf_url;
839 #endif
840
841 if (session->request_method != POST) {
842 return 0;
843 }
844
845 if ((referer = get_referer_header(session->http_headers)) == NULL) {
846 return 0;
847 }
848
849 #ifdef ENABLE_MONITOR
850 csrf_url = referer;
851 #endif
852
853 if (strncmp(referer, "http://", 7) == 0) {
854 referer += 7;
855 } else if (strncmp(referer, "https://", 8) == 0) {
856 referer += 8;
857 } else {
858 session->request_method = GET;
859 session->body = NULL;
860 session->cookies = NULL;
861
862 log_error_session(session, "invalid referer while checking for CSRF");
863
864 return 1;
865 }
866
867 if ((slash = strchr(referer, '/')) != NULL) {
868 n = slash - referer;
869 } else {
870 n = strlen(referer);
871 }
872
873 for (i = 0; i < session->host->hostname.size; i++) {
874 if (strncasecmp(referer, *(session->host->hostname.item + i), n) == 0) {
875 return 0;
876 }
877 }
878
879 if (session->body != NULL) {
880 prev = *(session->body + session->content_length);
881 *(session->body + session->content_length) = '\0';
882 }
883
884 log_exploit_attempt(session, "CSRF", session->body);
885
886 if (session->body != NULL) {
887 *(session->body + session->content_length) = prev;
888 }
889
890 if (session->host->prevent_csrf == p_prevent) {
891 session->request_method = GET;
892 session->body = NULL;
893 session->cookies = NULL;
894 }
895
896 #ifdef ENABLE_TOMAHAWK
897 increment_counter(COUNTER_EXPLOIT);
898 #endif
899 #ifdef ENABLE_MONITOR
900 if (session->config->monitor_enabled) {
901 monitor_count_exploit_attempt(session);
902 monitor_event("CSRF attempt for %s%s via %s", session->host->hostname.item[0], session->uri, csrf_url);
903 }
904 #endif
905
906 return 1;
907 }
908
close_socket(t_session * session)909 void close_socket(t_session *session) {
910 if (session->socket_open) {
911 #ifdef ENABLE_TLS
912 if (session->binding->use_tls) {
913 tls_close(&(session->tls_context));
914 }
915 #endif
916 fsync(session->client_socket);
917 close(session->client_socket);
918 session->socket_open = false;
919 }
920 }
921
handle_connection_not_allowed(t_session * session,int connections)922 int handle_connection_not_allowed(t_session *session, int connections) {
923 switch (connections) {
924 case ca_TOOMUCH_PERIP:
925 log_system_session(session, "Maximum number of connections for IP address reached");
926 if ((session->config->ban_on_max_per_ip > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
927 log_system_session(session, "Client banned because of too many simultaneous connections");
928 ban_ip(&(session->ip_address), session->config->ban_on_max_per_ip, session->config->kick_on_ban);
929 #ifdef ENABLE_MONITOR
930 if (session->config->monitor_enabled) {
931 monitor_count_ban(session);
932 }
933 #endif
934 }
935 return 444;
936 case ca_TOOMUCH_TOTAL:
937 log_system_session(session, "Maximum number of total connections reached");
938 return 503;
939 case ca_BANNED:
940 if (session->config->reban_during_ban && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
941 reban_ip(&(session->ip_address));
942 }
943 #ifdef ENABLE_TOMAHAWK
944 increment_counter(COUNTER_DENY);
945 #endif
946 return 444;
947 }
948
949 return 500;
950 }
951
file_can_be_compressed(t_session * session)952 bool file_can_be_compressed(t_session *session) {
953 if (session->mimetype != NULL) {
954 if (strncmp(session->mimetype, "text/", 5) == 0) {
955 return true;
956 } else if (strcmp(session->mimetype, "image/svg+xml") == 0) {
957 return true;
958 }
959 }
960
961 return in_charlist(session->extension, &(session->config->gzip_extensions));
962 }
963
964 #ifdef ENABLE_DEBUG
printhex(char * str,int len)965 void printhex(char *str, int len) {
966 int chars_per_line = 16, i, max_i;
967
968 while (len > 0) {
969 max_i = len > chars_per_line ? chars_per_line : len;
970
971 for (i = 0; i < max_i; i++) {
972 fprintf(stderr, "%02X ", *((unsigned char*)str + i));
973 }
974 for (i = max_i; i < chars_per_line; i++) {
975 fprintf(stderr, " ");
976 }
977
978 fprintf(stderr, " ");
979 for (i = 0; i < max_i; i++) {
980 if ((*((unsigned char*)str + i) >= 32) && (*((unsigned char*)str + i) <= 126)) {
981 fprintf(stderr, "%c", *((unsigned char*)str + i));
982 } else {
983 fprintf(stderr, ".");
984 }
985 }
986 for (i = max_i; i < chars_per_line; i++) {
987 fprintf(stderr, " ");
988 }
989
990 fprintf(stderr, "\n");
991
992 str += chars_per_line;
993 len -= chars_per_line;
994 }
995 }
996 #endif
997