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