int last_forwarded_ip(t_http_header *http_headers, t_ip_addr *ip_addr) { char *forwarded, *search, ip_str[MAX_IP_STR_LEN + 1], *begin, *end; size_t len; int port; if ((forwarded = get_http_header(hs_forwarded, http_headers)) != NULL) { /* Forwarded header */ begin = NULL; while ((forwarded = strcasestr(forwarded, "for=")) == NULL) { begin = forwarded; forwarded++; } if (begin == NULL) { return -1; } end = begin; while ((*end != '\0') && (*end != ',') && (*end != ';')) { end++; } if (*begin == '"') { begin++; end--; if (*end != '"') { return -1; } } len = end - begin; if (len > MAX_IP_STR_LEN) { return -1; } memcpy(ip_str, begin, len); *(ip_str + len) = '\0'; forwarded = ip_str; } else if ((forwarded = get_http_header(hs_x_forwarded_for, http_headers)) != NULL) { /* X-Forwarded-For header */ if ((search = strrchr(forwarded, ',')) != NULL) { forwarded = search + 1; while (*forwarded == ' ') { forwarded++; } } } else { return -1; } if (parse_ip(forwarded, ip_addr) == -1) { if (parse_ip_port(forwarded, ip_addr, &port) == -1) { return -1; } } return 0; }
void show_request_to_admins(char *method, char *uri, char *http_version, t_ip_addr *ip_addr, t_http_header *headers, int response_code, off_t bytes_sent) { bool generated = false; char message[1024], *format, ip_str[MAX_IP_STR_LEN + 1], *hostname, *user_agent; t_admin *admin; admin = adminlist; while (admin != NULL) { if (admin->show_requests) { if (generated == false) { if ((hostname = get_http_header("Host:", headers)) == NULL) { hostname = "-"; } if ((user_agent = get_http_header("User-Agent:", headers)) == NULL) { user_agent = "-"; } ip_to_str(ip_addr, ip_str, MAX_IP_STR_LEN); format = " %s %s %s\n" " Host: %s\n" " Client IP: %s\n" " User agent: %s\n" " Result: %d, %ld bytes sent\n\n"; if (snprintf(message, 1023, format, method, uri, http_version, hostname, ip_str, user_agent, response_code, (long)bytes_sent) > 1023) { sprintf(message, "(error generating request information message)\n"); } generated = true; } fprintf(admin->fp, "%s", message); fflush(admin->fp); } admin = admin->next; } }
int rproxy_cache_time(t_session *session, char *buffer) { int cache_time; char *value, *no_cache = "no-cache", extension[EXTENSION_SIZE]; if ((cache_time = cgi_cache_time(buffer)) > 0) { return cache_time; } if (extension_from_uri(session->request_uri, extension, EXTENSION_SIZE) == false) { return 0; } if (in_charlist(extension, &(session->config->cache_rproxy_extensions)) == false) { return 0; } if ((value = get_http_header("Cache-Control:", session->http_headers)) != NULL) { if (strstr(value, no_cache) != NULL) { return 0; } } if ((value = get_http_header("Pragma:", session->http_headers)) != NULL) { if (strstr(value, no_cache) != NULL) { return 0; } } return TIME_IN_CACHE; }
/* Prevent Cross-site Request Forgery */ int prevent_csrf(t_session *session) { char *referer, *slash; int i, n; if (strcmp(session->method, "POST") != 0) { return 0; } if ((referer = get_http_header("Referer:", session->http_headers)) == NULL) { return 0; } if (strncmp(referer, "http://", 7) == 0) { referer += 7; } else if (strncmp(referer, "https://", 8) == 0) { referer += 8; } else { session->cookie = NULL; log_error(session, "invalid referer while checking for CSRF"); return 1; } if ((slash = strchr(referer, '/')) != NULL) { n = slash - referer; } else { n = strlen(referer); } for (i = 0; i < session->host->hostname.size; i++) { if (strncasecmp(referer, *(session->host->hostname.item + i), n) == 0) { return 0; } } session->cookie = NULL; if (session->body != NULL) { log_exploit_attempt(session, "CSRF", session->body); #ifdef ENABLE_TOMAHAWK increment_counter(COUNTER_EXPLOIT); #endif #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_counter_exploit_attempt(session); } #endif } else { log_error(session, "CSRF attempt detected with no request body"); } #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_counter_exploit_attempt(session); } #endif return 1; }
int reqs_cont_send(struct reqs_t *reqs) { if (!reqs->rp_status_line) { set_http_status(reqs, 200); /* "200 OK" */ } if (strlen(get_http_header(reqs, "Content-Type")) == 0) { set_http_header(reqs, "Content-Type", "text/html"); } set_http_header_x(reqs, "Content-Length", "%d", bufx_get_used(reqs->contbufx)); send_http_header (reqs); bufx_get_each (reqs->contbufx, conn_send, reqs->conn); return 1; }
static t_access allow_client(t_session *session) { char *x_forwarded_for; t_ip_addr forwarded_ip; t_access access; if ((access = ip_allowed(&(session->ip_address), session->host->access_list)) != allow) { return access; } else if ((x_forwarded_for = get_http_header(hs_forwarded, session->http_headers)) == NULL) { return allow; } else if (parse_ip(x_forwarded_for, &forwarded_ip) == -1) { return allow; } else if (ip_allowed(&forwarded_ip, session->host->access_list) == deny) { return deny; } return unspecified; }
/* Convert the requestbuffer to a session record. */ int parse_request(t_session *session, int total_bytes) { int retval = 200; char *request_end, *str_end, *conn; request_end = session->request + total_bytes; /* Request method */ session->method = str_end = session->request; while ((*str_end != ' ') && (str_end != request_end)) { str_end++; } if (str_end == request_end) { return 400; } *str_end = '\0'; session->uri = ++str_end; /* URI */ while ((*str_end != ' ') && (str_end != request_end)) { str_end++; } if (str_end == request_end) { return 400; } *(str_end++) = '\0'; session->uri_len = strlen(session->uri); if ((session->config->max_url_length > 0) && (session->uri_len > session->config->max_url_length)) { return 414; } if (strncmp(session->uri, "http://", 7) == 0) { return 400; } else if ((session->request_uri = strdup(session->uri)) == NULL) { return -1; } /* Protocol version */ if (min_strlen(str_end, 10) == false) { return 400; } else if (memcmp(str_end, "HTTP/", 5) != 0) { return 400; } session->http_version = str_end; str_end += 7; if ((*(str_end - 1) != '.') || (*(str_end + 1) != '\r') || (*(str_end + 2) != '\n')) { return 400; } else if (*(str_end - 2) != '1') { return 505; } *(str_end + 1) = '\0'; /* Body and other request headerlines */ if (session->content_length > 0) { session->body = session->request + session->header_length; } session->http_headers = parse_http_headers(str_end + 3); session->hostname = strlower(get_http_header("Host:", session->http_headers)); session->cookie = get_http_header("Cookie:", session->http_headers); if ((conn = get_http_header("Connection:", session->http_headers)) != NULL) { conn = strlower(remove_spaces(conn)); } session->keep_alive = false; switch (*str_end) { case '0': if ((conn != NULL) && (session->kept_alive < session->binding->max_keepalive)) { if (strcasecmp(conn, "keep-alive") == 0) { session->keep_alive = true; } } break; case '1': if (session->hostname == NULL) { retval = 400; } else if (session->kept_alive < session->binding->max_keepalive) { session->keep_alive = true; if (conn != NULL) { if (strcmp(conn, "close") == 0) { session->keep_alive = false; } } } break; default: retval = 505; break; } if (session->keep_alive) { session->kept_alive++; } session->parsing_oke = true; return retval; }
/* Prevent Cross-site Request Forgery */ int prevent_csrf(t_session *session) { char *referer, *slash, prev = '\0'; int i, n; #ifdef ENABLE_MONITOR char *csrf_url; #endif if (session->request_method != POST) { return 0; } if ((referer = get_http_header("Origin:", session->http_headers)) == NULL) { if ((referer = get_http_header("Referer:", session->http_headers)) == NULL) { return 0; } } #ifdef ENABLE_MONITOR csrf_url = referer; #endif if (strncmp(referer, "http://", 7) == 0) { referer += 7; } else if (strncmp(referer, "https://", 8) == 0) { referer += 8; } else { session->request_method = GET; session->body = NULL; session->cookies = NULL; log_error(session, "invalid referer while checking for CSRF"); return 1; } if ((slash = strchr(referer, '/')) != NULL) { n = slash - referer; } else { n = strlen(referer); } for (i = 0; i < session->host->hostname.size; i++) { if (strncasecmp(referer, *(session->host->hostname.item + i), n) == 0) { return 0; } } if (session->body != NULL) { prev = *(session->body + session->content_length); *(session->body + session->content_length) = '\0'; } log_exploit_attempt(session, "CSRF", session->body); if (session->body != NULL) { *(session->body + session->content_length) = prev; } if (session->host->prevent_csrf == p_prevent) { session->request_method = GET; session->body = NULL; session->cookies = NULL; } #ifdef ENABLE_TOMAHAWK increment_counter(COUNTER_EXPLOIT); #endif #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_exploit_attempt(session); monitor_event("CSRF attempt for %s%s via %s", session->host->hostname.item[0], session->uri, csrf_url); } #endif return 1; }
/* Serve the client that connected to the webserver */ static int serve_client(t_session *session) { int result, length, auth_result; char *qmark, chr, *header; t_host *host_record; t_access access; t_deny_body *deny_body; t_req_method request_method; t_ip_addr ip_addr; #ifdef ENABLE_XSLT char *xslt_file; #endif #ifdef ENABLE_TOOLKIT int i; t_toolkit_options toolkit_options; #endif #ifdef ENABLE_RPROXY t_rproxy *rproxy; #endif #ifdef ENABLE_DEBUG session->current_task = "fetch & parse request"; #endif if ((result = fetch_request(session)) != 200) { session->request_method = GET; return result; } else if ((result = parse_request(session, session->header_length + session->content_length)) != 200) { session->request_method = GET; return result; } #ifdef ENABLE_DEBUG session->current_task = "serve client"; #endif session->time = time(NULL); /* Hide reverse proxies */ if (in_iplist(session->config->hide_proxy, &(session->ip_address))) { if (last_forwarded_ip(session->http_headers, &ip_addr) == 0) { if (reposition_client(session, &ip_addr) != -1) { copy_ip(&(session->ip_address), &ip_addr); } } } /* SSH tunneling */ #ifdef ENABLE_RPROXY if (session->request_method == CONNECT) { if (in_iplist(session->config->tunnel_ssh, &(session->ip_address)) == false) { return 405; } #ifdef ENABLE_SSL if (session->binding->use_ssl) { return 405; } #endif if (strcmp(session->request_uri, "localhost:22") != 0) { if (strcmp(session->request_uri, "127.0.0.1:22") != 0) { if (strcmp(session->request_uri, "::1.22") != 0) { return 403; } } } log_system(session, "SSH tunnel requested"); if (tunnel_ssh_connection(session->client_socket) != 0) { log_system(session, "SSH tunnel failed"); } else { log_system(session, "SSH tunnel terminated"); } session->keep_alive = false; return 200; } #endif /* Find host record */ if (session->hostname != NULL) { if (remove_port_from_hostname(session) == -1) { log_error(session, "error removing port from hostname"); return 500; } if ((host_record = get_hostrecord(session->config->first_host, session->hostname, session->binding)) != NULL) { session->host = host_record; #ifdef ENABLE_TOMAHAWK session->last_host = host_record; #endif } } session->host->access_time = session->time; #ifdef ENABLE_SSL /* SSL client authentication */ if (session->binding->use_ssl) { if ((session->host->ca_certificate != NULL) && (ssl_has_peer_cert(&(session->ssl_context)) == false)) { log_error(session, "Missing client SSL certificate"); return 440; } } #endif /* Enforce usage of first hostname */ if (session->host->enforce_first_hostname && (session->hostname != NULL)) { if (**(session->host->hostname.item) != '*') { if (strcmp(session->hostname, *(session->host->hostname.item)) != 0) { session->cause_of_301 = enforce_first_hostname; return 301; } } } /* Enforce usage of SSL */ #ifdef ENABLE_SSL if (session->host->require_ssl && (session->binding->use_ssl == false)) { if ((qmark = strchr(session->uri, '?')) != NULL) { *qmark = '\0'; session->vars = qmark + 1; session->uri_len = strlen(session->uri); } session->cause_of_301 = require_ssl; return 301; } #endif /* Deny matching bodies */ if (session->body != NULL) { chr = *(session->body + session->content_length); *(session->body + session->content_length) = '\0'; deny_body = session->host->deny_body; while (deny_body != NULL) { if (strpcmp(session->body, &(deny_body->pattern)) == 0) { if ((session->config->ban_on_denied_body > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_denied_body, session->config->kick_on_ban); log_system(session, "Client banned because of denied body"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } log_exploit_attempt(session, "denied body", session->body); #ifdef ENABLE_TOMAHAWK increment_counter(COUNTER_EXPLOIT); #endif #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_exploit(session); monitor_event("Request body denied for %s", session->host->hostname.item[0]); } #endif *(session->body + session->content_length) = chr; return 403; } deny_body = deny_body->next; } *(session->body + session->content_length) = chr; } /* Websocket */ if (session->request_method == GET) { if ((header = get_http_header("Connection:", session->http_headers)) != NULL) { if (strcasestr(header, "upgrade") != NULL) { if ((header = get_http_header("Upgrade:", session->http_headers)) != NULL) { if (strcasecmp(header, "websocket") == 0) { switch (access = allow_client(session)) { case deny: log_error(session, fb_accesslist); return 403; case allow: break; case pwd: case unspecified: if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) { return auth_result; } } session->keep_alive = false; if (forward_to_websocket(session) == -1) { return 500; } return 200; } } } } } #ifdef ENABLE_RPROXY /* Reverse proxy */ rproxy = session->host->rproxy; while (rproxy != NULL) { if (rproxy_match(rproxy, session->request_uri)) { if (rproxy_loop_detected(session->http_headers)) { return 508; } if ((qmark = strchr(session->uri, '?')) != NULL) { *qmark = '\0'; session->vars = qmark + 1; } if (validate_url(session) == false) { return -1; } if ((session->vars != NULL) && (session->host->secure_url)) { if (forbidden_chars_present(session->vars)) { log_error(session, "URL contains forbidden characters"); return 403; } } if (duplicate_host(session) == false) { log_error(session, "duplicate_host() error"); return 500; } if ((result = uri_to_path(session)) != 200) { return result; } if (session->host->ignore_dot_hiawatha == false) { if (load_user_config(session) == -1) { return 500; } } if ((result = copy_directory_settings(session)) != 200) { return result; } switch (access = allow_client(session)) { case deny: log_error(session, fb_accesslist); return 403; case allow: break; case pwd: case unspecified: if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) { return auth_result; } } /* Prevent SQL injection */ if (session->host->prevent_sqli) { result = prevent_sqli(session); if (result == 1) { session->error_cause = ec_SQL_INJECTION; } if (result != 0) { return -1; } } /* Prevent Cross-site Scripting */ if (session->host->prevent_xss != p_no) { if (prevent_xss(session) > 0) { if (session->host->prevent_xss == p_block) { session->error_cause = ec_XSS; return -1; } } } /* Prevent Cross-site Request Forgery */ if (session->host->prevent_csrf != p_no) { if (prevent_csrf(session) > 0) { if (session->host->prevent_csrf == p_block) { session->error_cause = ec_CSRF; return -1; } } } return proxy_request(session, rproxy); } rproxy = rproxy->next; } #endif /* Actions based on request method */ switch (session->request_method) { case TRACE: if (session->binding->enable_trace == false) { return 501; } return handle_trace_request(session); case PUT: case DELETE: if ((session->binding->enable_alter == false) && (session->host->webdav_app == false)) { return 501; } break; case unknown: return 400; case unsupported: if (session->host->webdav_app == false) { return 501; } break; default: break; } if (duplicate_host(session) == false) { log_error(session, "duplicate_host() error"); return 500; } #ifdef ENABLE_TOOLKIT if (session->host->ignore_dot_hiawatha == false) { if (load_user_root_config(session) == -1) { return 500; } } /* URL toolkit */ init_toolkit_options(&toolkit_options); toolkit_options.method = session->method; toolkit_options.website_root = session->host->website_root; toolkit_options.url_toolkit = session->config->url_toolkit; toolkit_options.allow_dot_files = session->host->allow_dot_files; toolkit_options.http_headers = session->http_headers; #ifdef ENABLE_SSL toolkit_options.use_ssl = session->binding->use_ssl; #endif if (((session->request_method != PUT) && (session->request_method != DELETE)) || session->host->webdav_app) { for (i = 0; i < session->host->toolkit_rules.size; i++) { if ((result = use_toolkit(session->uri, session->host->toolkit_rules.item[i], &toolkit_options)) == UT_ERROR) { return 500; } if ((toolkit_options.ban > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), toolkit_options.ban, session->config->kick_on_ban); log_system(session, "Client banned because of URL match in UrlToolkit rule"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif return 403; } session->toolkit_fastcgi = toolkit_options.fastcgi_server; if (toolkit_options.new_url != NULL) { if (register_tempdata(&(session->tempdata), toolkit_options.new_url, tc_data) == -1) { free(toolkit_options.new_url); log_error(session, "error registering temporary data"); return 500; } session->uri = toolkit_options.new_url; } if (result == UT_REDIRECT) { if ((session->location = strdup(toolkit_options.new_url)) == NULL) { return -1; } session->cause_of_301 = location; return 301; } if (result == UT_DENY_ACCESS) { log_error(session, "access denied via URL toolkit rule"); return 403; } if (toolkit_options.expire > -1) { session->expires = toolkit_options.expire; session->caco_private = toolkit_options.caco_private; } } } #endif /* Find GET data */ if ((qmark = strchr(session->uri, '?')) != NULL) { *qmark = '\0'; session->vars = qmark + 1; } url_decode(session->uri); session->uri_len = strlen(session->uri); if ((session->vars != NULL) && (session->host->secure_url)) { if (forbidden_chars_present(session->vars)) { log_error(session, "URL contains forbidden characters"); return 403; } } if (validate_url(session) == false) { return -1; } if ((result = uri_to_path(session)) != 200) { return result; } /* Load configfile from directories */ if (session->host->ignore_dot_hiawatha == false) { if (load_user_config(session) == -1) { return 500; } } if ((result = copy_directory_settings(session)) != 200) { return result; } switch (access = allow_client(session)) { case deny: log_error(session, fb_accesslist); return 403; case allow: break; case pwd: case unspecified: if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) { return auth_result; } } switch (is_directory(session->file_on_disk)) { case error: return 500; case yes: session->uri_is_dir = true; break; case no: if (((session->request_method != PUT) || session->host->webdav_app) && (session->host->enable_path_info)) { if ((result = get_path_info(session)) != 200) { return result; } } break; case no_access: log_error(session, fb_filesystem); return 403; case not_found: if (session->request_method == DELETE) { return 404; } } #ifdef ENABLE_TOOLKIT if ((session->toolkit_fastcgi == NULL) && session->uri_is_dir) { #else if (session->uri_is_dir) { #endif length = strlen(session->file_on_disk); if (*(session->file_on_disk + length - 1) == '/') { strcpy(session->file_on_disk + length, session->host->start_file); } else { return 301; } } if (get_target_extension(session) == -1) { log_error(session, "error getting extension"); return 500; } if (((session->request_method != PUT) && (session->request_method != DELETE)) || session->host->webdav_app) { check_target_is_cgi(session); } /* Handle request based on request method */ request_method = session->request_method; if (session->host->webdav_app) { if ((request_method == PUT) || (request_method == DELETE)) { request_method = POST; } } switch (request_method) { case GET: case HEAD: if (session->cgi_type != no_cgi) { session->body = NULL; result = execute_cgi(session); #ifdef ENABLE_XSLT } else if ((xslt_file = find_xslt_file(session)) != NULL) { result = handle_xml_file(session, xslt_file); free(xslt_file); #endif } else { result = send_file(session); } if (result == 404) { #ifdef ENABLE_XSLT if ((session->host->show_index != NULL) && (session->uri[session->uri_len - 1] == '/')) { result = show_index(session); } #endif #ifdef ENABLE_MONITOR } else if (session->config->monitor_enabled) { if ((result == 200) && (session->host->monitor_host)) { unlink(session->file_on_disk); } #endif } if ((session->request_method == GET) && (session->cgi_type == no_cgi) && (session->directory != NULL)) { if (session->directory->run_on_download != NULL) { run_program(session, session->directory->run_on_download, result); } } break; case POST: case unsupported: if (session->cgi_type != no_cgi) { result = execute_cgi(session); #ifdef ENABLE_XSLT } else if ((xslt_file = find_xslt_file(session)) != NULL) { result = handle_xml_file(session, xslt_file); free(xslt_file); #endif } else { result = 405; } break; case PUT: result = handle_put_request(session); if (((result == 201) || (result == 204)) && (session->host->run_on_alter != NULL)) { run_program(session, session->host->run_on_alter, result); } break; case DELETE: result = handle_delete_request(session); if ((result == 204) && (session->host->run_on_alter != NULL)) { run_program(session, session->host->run_on_alter, result); } break; case WHEN: send_code(session); break; default: result = 400; } return result; } /* Handle timeout upon sending request */ static void handle_timeout(t_session *session) { if ((session->config->ban_on_timeout > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_timeout, session->config->kick_on_ban); log_system(session, "Client banned because of connection timeout"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } else { log_system(session, "Timeout while waiting for first request"); } } /* Request has been handled, handle the return code. */ static void handle_request_result(t_session *session, int result) { char *hostname; #ifdef ENABLE_DEBUG session->current_task = "handle request result"; #endif if (result == -1) switch (session->error_cause) { case ec_MAX_REQUESTSIZE: log_system(session, "Maximum request size reached"); session->return_code = 413; send_code(session); if ((session->config->ban_on_max_request_size > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_max_request_size, session->config->kick_on_ban); log_system(session, "Client banned because of sending a too large request"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } break; case ec_TIMEOUT: if (session->kept_alive == 0) { session->return_code = 408; send_code(session); handle_timeout(session); } break; case ec_CLIENT_DISCONNECTED: if (session->kept_alive == 0) { log_system(session, "Silent client disconnected"); } break; case ec_SOCKET_READ_ERROR: if (errno != ECONNRESET) { log_system(session, "Error while reading request"); } break; case ec_SOCKET_WRITE_ERROR: log_request(session); break; case ec_FORCE_QUIT: log_system(session, "Client kicked"); break; case ec_SQL_INJECTION: if ((session->config->ban_on_sqli > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_sqli, session->config->kick_on_ban); hostname = (session->hostname != NULL) ? session->hostname : unknown_host; log_system(session, "Client banned because of SQL injection on %s", hostname); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } session->return_code = 441; send_code(session); log_request(session); break; case ec_XSS: session->return_code = 442; send_code(session); log_request(session); break; case ec_CSRF: session->return_code = 443; send_code(session); log_request(session); break; case ec_INVALID_URL: if ((session->config->ban_on_invalid_url > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_invalid_url, session->config->kick_on_ban); hostname = (session->hostname != NULL) ? session->hostname : unknown_host; log_system(session, "Client banned because of invalid URL on %s", hostname); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } send_code(session); break; default: if (session->data_sent == false) { session->return_code = 500; if (send_code(session) == -1) { session->keep_alive = false; } } } else switch (result) { case 200: break; case 201: case 204: case 304: case 412: if (session->data_sent == false) { session->return_code = result; if (send_header(session) == -1) { session->keep_alive = false; } else if (send_buffer(session, "Content-Length: 0\r\n\r\n", 21) == -1) { session->keep_alive = false; } } break; case 411: case 413: session->keep_alive = false; if (session->data_sent == false) { session->return_code = result; if (send_header(session) == -1) { session->keep_alive = false; } else if (send_buffer(session, "Content-Length: 0\r\n\r\n", 21) == -1) { session->keep_alive = false; } } break; case 400: log_garbage(session); if (session->data_sent == false) { session->return_code = 400; if (send_code(session) == -1) { session->keep_alive = false; } } if ((session->config->ban_on_garbage > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) { ban_ip(&(session->ip_address), session->config->ban_on_garbage, session->config->kick_on_ban); log_system(session, "Client banned because of sending garbage"); #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_ban(session); } #endif } #ifdef ENABLE_MONITOR if (session->config->monitor_enabled) { monitor_count_bad_request(session); } #endif break; case 401: case 403: case 404: case 501: case 503: if (session->data_sent == false) { switch (handle_error(session, result)) { case -1: session->keep_alive = false; break; case 200: break; default: if (session->data_sent == false) { session->return_code = result; if (send_code(session) == -1) { session->keep_alive = false; } } } } break; case 500: session->keep_alive = false; default: if (session->data_sent == false) { session->return_code = result; send_code(session); } } if ((result > 0) && (result != 400)) { log_request(session); } else { session->keep_alive = false; } }
int use_toolkit(char *url, char *toolkit_id, t_toolkit_options *options) { t_url_toolkit *toolkit; t_toolkit_rule *rule; bool condition_met, url_replaced = false; int result, skip = 0; char *file, *qmark, *header; regmatch_t pmatch[REGEXEC_NMATCH]; struct stat fileinfo; t_http_header *headers; if (options == NULL) { return UT_ERROR; } options->new_url = NULL; if ((toolkit = select_toolkit(toolkit_id, options->url_toolkit)) == NULL) { return UT_ERROR; } rule = toolkit->toolkit_rule; while (rule != NULL) { condition_met = false; /* Skip lines */ if (skip > 0) { skip--; rule = rule->next; continue; } /* Condition */ switch (rule->condition) { case tc_none: /* None */ condition_met = true; break; case tc_match: /* Match */ if (regexec(&(rule->pattern), url, REGEXEC_NMATCH, pmatch, 0) == 0) { condition_met = true; } if (rule->neg_match) { condition_met = (condition_met == false); } break; case tc_header: /* Header */ if (rule->header == NULL) { headers = options->http_headers; while (headers != NULL) { if (regexec(&(rule->pattern), headers->data + headers->value_offset, REGEXEC_NMATCH, pmatch, 0) == 0) { condition_met = true; } if (rule->neg_match) { condition_met = (condition_met == false); } if (condition_met) { break; } headers = headers->next; } } else { if ((header = get_http_header(rule->header, options->http_headers)) == NULL) { break; } if (regexec(&(rule->pattern), header, REGEXEC_NMATCH, pmatch, 0) == 0) { condition_met = true; } if (rule->neg_match) { condition_met = (condition_met == false); } } break; case tc_method: /* Request method */ if (strcmp(options->method, rule->parameter) == 0) { condition_met = true; } if (rule->neg_match) { condition_met = (condition_met == false); } break; case tc_request_uri: /* Request URI */ if (valid_uri(url, false) == false) { break; } if ((file = make_path(options->website_root, url)) == NULL) { return UT_ERROR; } if ((qmark = strchr(file, '?')) != NULL) { *qmark = '\0'; } url_decode(file); if (stat(file, &fileinfo) != -1) { switch (rule->value) { case IU_EXISTS: if (S_ISDIR(fileinfo.st_mode) || S_ISREG(fileinfo.st_mode)) { condition_met = true; } break; case IU_ISFILE: if (S_ISREG(fileinfo.st_mode)) { condition_met = true; } break; case IU_ISDIR: if (S_ISDIR(fileinfo.st_mode)) { condition_met = true; } break; } } free(file); break; case tc_total_connections: /* Total connections reached? */ condition_met = options->total_connections >= rule->value; break; #ifdef ENABLE_TLS case tc_use_tls: /* Client connections uses TLS? */ condition_met = options->use_tls; break; #endif } /* Condition not met */ if (condition_met == false) { rule = rule->next; continue; } /* Operation */ switch (rule->operation) { case to_none: /* None */ break; case to_ban: /* Ban client */ options->ban = rule->value; break; case to_deny_access: /* Deny access */ return UT_DENY_ACCESS; case to_omit_request_log: /* Omit requeest log */ options->log_request = false; break; case to_expire: /* Send Expire HTTP header */ options->expire = rule->value; options->caco_private = rule->caco_private; break; case to_fastcgi: /* Use FastCGI server */ options->fastcgi_server = rule->parameter; break; case to_redirect: /* Redirect client */ if (rule->neg_match) { if ((options->new_url = strdup(rule->parameter)) == NULL) { return UT_ERROR; } } else if (do_rewrite(url, &(rule->pattern), pmatch, rule->parameter, &(options->new_url), rule->match_loop) == -1) { if (options->new_url != NULL) { free(options->new_url); options->new_url = NULL; } return UT_ERROR; } if (options->new_url != NULL) { if (url_replaced) { free(url); } return UT_REDIRECT; } else if (url_replaced) { options->new_url = url; } break; case to_rewrite: /* Rewrite */ if (rule->neg_match) { if ((options->new_url = strdup(rule->parameter)) == NULL) { return UT_ERROR; } } else if (do_rewrite(url, &(rule->pattern), pmatch, rule->parameter, &(options->new_url), rule->match_loop) == -1) { if (options->new_url != NULL) { free(options->new_url); options->new_url = NULL; } return UT_ERROR; } if (options->new_url != NULL) { if (url_replaced) { free(url); } url = options->new_url; url_replaced = true; } else if (url_replaced) { options->new_url = url; } break; case to_skip: /* Skip */ skip = rule->value; break; case to_sub: /* Subroutine */ if (++(options->sub_depth) > MAX_SUB_DEPTH) { return UT_ERROR; } if ((result = use_toolkit(url, rule->parameter, options)) == UT_ERROR) { if (options->new_url != NULL) { free(options->new_url); options->new_url = NULL; } return UT_ERROR; } options->sub_depth--; if (options->new_url != NULL) { if (url_replaced) { free(url); } url = options->new_url; url_replaced = true; } else if (url_replaced) { options->new_url = url; } if (result != UT_RETURN) { return result; } break; case to_use: /* Replace URL */ if (url_replaced) { free(url); } if ((options->new_url = strdup(rule->parameter)) == NULL) { return UT_ERROR; } break; } /* Flow */ switch (rule->flow) { case tf_continue: /* Continue */ break; case tf_exit: /* Exit */ return UT_EXIT; case tf_return: /* Return */ return UT_RETURN; } rule = rule->next; } return UT_RETURN; }
int reqs_parse(struct reqs_t *reqs) { register char *a, *z, *p; char *reqs_head = reqs->conn->reqs_head; char *reqs_line; /* [ take reqs_line */ a = reqs_head; while (*a == CR || *a == LF || *a == SP || *a == HT) { a++; } z = a; while (*z != CR && *z != LF && *z) { z++; } if (z == a) { return 0; } reqs_line = x_strndup(a, z - a); reqs->reqs_line = reqs_line; /* ] */ /* [ method_name */ reqs->method = HTTP_METHOD_UNKNOWN; a = reqs_line; z = a; while (*z != SP && *z) { z++; } switch (z - a) { case 0: case 1: case 2: break; case 3: if (str3equ(a, 'G', 'E', 'T')) { reqs->method = HTTP_METHOD_GET; } else if (str3equ(a, 'P', 'U', 'T')) { reqs->method = HTTP_METHOD_PUT; } break; case 4: if (str4equ(a, 'P', 'O', 'S', 'T')) { reqs->method = HTTP_METHOD_POST; } else if (str4equ(a, 'H', 'E', 'A', 'D')) { reqs->method = HTTP_METHOD_HEAD; } break; case 5: if (str5equ(a, 'T', 'R', 'A', 'C', 'E')) { reqs->method = HTTP_METHOD_TRACE; } break; case 6: if (str6equ(a, 'D', 'E', 'L', 'E', 'T', 'E')) { reqs->method = HTTP_METHOD_DELETE; } break; case 7: if (str7equ(a, 'O', 'P', 'T', 'I', 'O', 'N', 'S')) { reqs->method = HTTP_METHOD_OPTIONS; } else if (str7equ(a, 'C', 'O', 'N', 'N', 'E', 'C', 'T')) { reqs->method = HTTP_METHOD_CONNECT; } break; default: break; } reqs->method_name = http_method_names[reqs->method]; if (reqs->method == HTTP_METHOD_UNKNOWN) { reqs_throw_status(reqs, 501, ""); /* "501 Method Not Implemented" */ chtd_cry(reqs->htdx, "Method Not Implemented: [%s]", a); return 0; } /* ] */ /* [ URI */ #define is_valid_uri_char(c) ((c > 0x20) && (c != 0x7f) && (c != 0xff)) a = z; while (*a == SP) { a++; } z = a; while (*z != SP && *z) { z++; } if (a == z) { return 0; } reqs->uri = x_strndup(a, z - a); p = reqs->uri; for ( ; *p; p++) { if (!is_valid_uri_char(*p)) { return 0; } } /* ] */ /* [ http version */ reqs->http_version = HTTP_VERSION_0_9; a = z; for ( ; *a == SP; a++); z = strchr(a, 0); if (z - a == 8) { if (str8equ(a, 'H', 'T', 'T', 'P', '/', '1', '.', '0')) { reqs->http_version = HTTP_VERSION_1_0; } else if (str8equ(a, 'H', 'T', 'T', 'P', '/', '1', '.', '1')) { reqs->http_version = HTTP_VERSION_1_1; } } reqs->http_version_name = http_version_names[reqs->http_version]; /* ] */ /* [ parse request_path */ a = reqs->uri; z = a; while (*z != '?' && *z) { z++; } reqs->request_path = x_strndup(a, z - a); path_tidy(reqs->request_path); /* ] */ /* [ parse query_string */ a = strchr(reqs->uri, '?'); if (a) { reqs->query_string = strdup(a + 1); } else { reqs->query_string = calloc(1 , 1); } /* ] */ /* [ http headers */ parse_header(&reqs->re_headers, strchr(reqs_head, LF) + 1); /* ] */ reqs->content_length = atoi(get_http_header(reqs, "Content-Length")); reqs_parse_post(reqs); return 1; }
int reqs_parse_post(struct reqs_t *reqs) { char *content_type; char *charset = NULL; char *p; if (!reqs_read_post(reqs)) { return 0; } if (!reqs->post_size) { return 0; } content_type = strdup(get_http_header(reqs, "Content-Type")); p = content_type; while (1) { p = strchr(p, ';'); if (!p) { break; } *p++ = '\0'; while (*p == ' ') { p++; } if (strncmp(p, "charset=", 8) == 0) { charset = p + 8; } } charset = charset; if (strcasecmp(content_type, "application/x-www-form-urlencoded") != 0) { char *n_a, *n_z; char *v_a, *v_z; int n_l = 0, v_l = 0; struct namevalue_t *nv; char *p = reqs->post_data; while (*p) { n_a = p; while (*p && *p != '=' && *p != '&') { p++; } if (!*p) { break; } n_z = p; if (*p != '&') { p++; } v_a = p; while (*p && *p != '&') { p++; } v_z = p; n_l = n_z - n_a; v_l = v_z - v_a; if (n_l > 0) { nv = namevalues_add(&reqs->post_vars, n_a, n_l, v_a, v_l); if (nv) { n_a = nv->n; for ( ; *n_a; n_a++) { if (strchr(" \t\r\n", *n_a)) { *n_a = '_'; } } } } p++; } } free(content_type); return 1; }
/* Log a HTTP request. */ void log_request(t_session *session) { char str[BUFFER_SIZE + 1], timestamp[TIMESTAMP_SIZE], ip_address[IP_ADDRESS_SIZE]; char *user, *field, *uri, *vars, *path_info; t_http_header *http_header; int offset; time_t t; struct tm s; if (ip_allowed(&(session->ip_address), session->config->logfile_mask) == deny) { return; } str[BUFFER_SIZE] = '\0'; #ifdef ENABLE_TOOLKIT if (session->request_uri == NULL) { #endif uri = secure_string(session->uri); path_info = secure_string(session->path_info); vars = secure_string(session->vars); #ifdef ENABLE_TOOLKIT } else { uri = secure_string(session->request_uri); path_info = NULL; vars = NULL; } #endif if ((user = session->remote_user) != NULL) { user = secure_string(user); } if (session->config->log_format == hiawatha) { /* Hiawatha log format */ if (session->config->anonymize_ip) { anonymized_ip_to_str(str, &(session->ip_address), IP_ADDRESS_SIZE); } else { ip_to_str(str, &(session->ip_address), IP_ADDRESS_SIZE); } strcat(str, "|"); offset = strlen(str); print_timestamp(str + offset); offset += strlen(str + offset); if (user == NULL) { user = ""; } snprintf(str + offset, BUFFER_SIZE - offset, "%d|%lld|%s|%s %s", session->return_code, (long long)session->bytes_sent, user, secure_string(session->method), uri); offset += strlen(str + offset); if ((offset < BUFFER_SIZE) && (path_info != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "/%s", path_info); offset += strlen(str + offset); } if ((offset < BUFFER_SIZE) && (vars != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "?%s", vars); offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { snprintf(str + offset, BUFFER_SIZE - offset, " %s", secure_string(session->http_version)); offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { http_header = session->http_headers; while (http_header != NULL) { if ((strncasecmp("Cookie:", http_header->data, 7) != 0) && (strncasecmp("Authorization:", http_header->data, 14) != 0)) { snprintf(str + offset, BUFFER_SIZE - offset, "|%s", secure_string(http_header->data)); if ((offset += strlen(str + offset)) >= BUFFER_SIZE) { break; } } http_header = http_header->next; } } } else { /* Common Log Format */ if (session->config->anonymize_ip) { anonymized_ip_to_str(ip_address, &(session->ip_address), IP_ADDRESS_SIZE); } else { ip_to_str(ip_address, &(session->ip_address), IP_ADDRESS_SIZE); } if (user == NULL) { user = "******"; } time(&t); localtime_r(&t, &s); timestamp[TIMESTAMP_SIZE - 1] = '\0'; strftime(timestamp, TIMESTAMP_SIZE - 1, "%d/%b/%Y:%T %z", &s); snprintf(str, BUFFER_SIZE, "%s - %s [%s] \"%s %s", ip_address, user, timestamp, secure_string(session->method), uri); offset = strlen(str); if ((offset < BUFFER_SIZE) && (path_info != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "/%s", path_info); offset += strlen(str + offset); } if ((offset < BUFFER_SIZE) && (vars != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "?%s", vars); offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { snprintf(str + offset, BUFFER_SIZE - offset, " %s\" %d %lld", secure_string(session->http_version), session->return_code, (long long)session->bytes_sent); } if (session->config->log_format == extended) { /* Extended Common Log Format */ offset += strlen(str + offset); if (offset < BUFFER_SIZE) { if ((field = get_http_header("Referer:", session->http_headers)) != NULL) { snprintf(str + offset, BUFFER_SIZE - offset, " \"%s\"", secure_string(field)); } else { snprintf(str + offset, BUFFER_SIZE - offset, " \"-\""); } offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { if ((field = get_http_header("User-Agent:", session->http_headers)) != NULL) { snprintf(str + offset, BUFFER_SIZE - offset, " \"%s\"", secure_string(field)); } else { snprintf(str + offset, BUFFER_SIZE - offset, " \"-\""); } } } } pthread_mutex_lock(&accesslog_mutex); if (*(session->host->access_fp) == NULL) { *(session->host->access_fp) = fopen(session->host->access_logfile, "a"); } if (*(session->host->access_fp) != NULL) { fprintf(*(session->host->access_fp), "%s"EOL, str); fflush(*(session->host->access_fp)); } pthread_mutex_unlock(&accesslog_mutex); }