/* * this routine gives our module another chance to examine the request * headers and to take special action. This is the first phase whose * hooks' configuration directives can appear inside the <Directory> * and similar sections, because at this stage the URI has been mapped * to the filename. For example this phase can be used to block evil * clients, while little resources were wasted on these. * * This is a RUN_ALL hook. */ static int header_parser(request_rec *r) { // Get the module configuration dir_config_t *dcfg = (dir_config_t *) ap_get_module_config(r->per_dir_config, &defender_module); // Stop if Defender not enabled if (!dcfg->defender) return DECLINED; RuntimeScanner *scanner = new RuntimeScanner(*dcfg->parser); // Register a C function to delete scanner at the end of the request cycle apr_pool_cleanup_register(r->pool, (void *) scanner, defender_delete_runtimescanner_object, apr_pool_cleanup_null); // Reserve a temporary memory block from the request pool to store data between hooks defender_config_t *pDefenderConfig = (defender_config_t *) apr_palloc(r->pool, sizeof(defender_config_t)); // Remember our application pointer for future calls pDefenderConfig->vpRuntimeScanner = scanner; // Register our config data structure for our module for retrieval later as required ap_set_module_config(r->request_config, &defender_module, (void *) pDefenderConfig); // Set method if (r->method_number == M_GET) scanner->method = METHOD_GET; else if (r->method_number == M_POST) scanner->method = METHOD_POST; else if (r->method_number == M_PUT) scanner->method = METHOD_PUT; // Set logger info scanner->pid = getpid(); apr_os_thread_t tid = apr_os_thread_current(); unsigned int pid_buffer_len = 16; char pid_buffer[pid_buffer_len]; apr_snprintf(pid_buffer, pid_buffer_len, "%pT", &tid); scanner->threadId = std::string(pid_buffer); scanner->connectionId = r->connection->id; scanner->clientIp = r->useragent_ip; scanner->requestedHost = r->hostname; scanner->serverHostname = r->server->server_hostname; scanner->fullUri = r->unparsed_uri; scanner->protocol = r->protocol; ap_version_t vers; ap_get_server_revision(&vers); scanner->softwareVersion = std::to_string(vers.major) + "." + std::to_string(vers.minor) + "." + std::to_string(vers.patch); scanner->logLevel = static_cast<LOG_LVL>(r->log->level); if (scanner->logLevel >= APLOG_DEBUG) scanner->logLevel = LOG_LVL_DEBUG; scanner->writeLogFn = write_log; scanner->errorLogFile = r->server->error_log; scanner->learningLogFile = dcfg->matchlog_file; scanner->learningJSONLogFile = dcfg->jsonmatchlog_file; scanner->learning = dcfg->learning; scanner->extensiveLearning = dcfg->extensive; // Set runtime modifiers scanner->libinjSQL = dcfg->libinjection_sql; scanner->libinjXSS = dcfg->libinjection_xss; scanner->bodyLimit = dcfg->requestBodyLimit; // Set the uri path scanner->setUri(r->parsed_uri.path); // Pass every HTTP header received const apr_array_header_t *headerFields = apr_table_elts(r->headers_in); apr_table_entry_t *headerEntry = (apr_table_entry_t *) headerFields->elts; for (int i = 0; i < headerFields->nelts; i++) scanner->addHeader(headerEntry[i].key, headerEntry[i].val); // Pass GET parameters apr_table_t *getTable = NULL; ap_args_to_table(r, &getTable); const apr_array_header_t *getParams = apr_table_elts(getTable); apr_table_entry_t *getParam = (apr_table_entry_t *) getParams->elts; for (int i = 0; i < getParams->nelts; i++) scanner->addGETParameter(getParam[i].key, getParam[i].val); // Run scanner int ret = scanner->processHeaders(); if (dcfg->useenv) ret = pass_in_env(r, scanner); return ret; }
/* * Implements the <IfVersion> container */ static const char *start_ifversion(cmd_parms *cmd, void *mconfig, const char *arg1, const char *arg2, const char *arg3) { const char *endp; int reverse = 0, done = 0, match = 0, compare; const char *p, *error; char c; /* supplying one argument is possible, we assume an equality check then */ if (!arg2) { arg2 = arg1; arg1 = "="; } /* surrounding quotes without operator */ if (!arg3 && *arg2 == '>' && !arg2[1]) { arg3 = ">"; arg2 = arg1; arg1 = "="; } /* the third argument makes version surrounding quotes plus operator * possible. */ endp = arg2 + strlen(arg2); if ( endp == arg2 || (!(arg3 && *arg3 == '>' && !arg3[1]) && *--endp != '>')) { return apr_pstrcat(cmd->pool, cmd->cmd->name, "> directive missing closing '>'", NULL); } p = arg1; if (*p == '!') { reverse = 1; if (p[1]) { ++p; } } c = *p++; if (!*p || (*p == '=' && !p[1] && c != '~')) { if (!httpd_version.major) { ap_get_server_revision(&httpd_version); } done = 1; switch (c) { case '=': /* normal comparison */ if (*arg2 != '/') { compare = compare_version(apr_pstrmemdup(cmd->pool, arg2, endp-arg2), &error); if (error) { return error; } match = !compare; break; } /* regexp otherwise */ if (endp == ++arg2 || *--endp != '/') { return "Missing delimiting / of regular expression."; } case '~': /* regular expression */ match = match_version(cmd->pool, apr_pstrmemdup(cmd->pool, arg2, endp-arg2), &error); if (error) { return error; } break; case '<': compare = compare_version(apr_pstrmemdup(cmd->pool, arg2, endp-arg2), &error); if (error) { return error; } match = ((-1 == compare) || (*p && !compare)); break; case '>': compare = compare_version(apr_pstrmemdup(cmd->pool, arg2, endp-arg2), &error); if (error) { return error; } match = ((1 == compare) || (*p && !compare)); break; default: done = 0; break; } } if (!done) { return apr_pstrcat(cmd->pool, "unrecognized operator '", arg1, "'", NULL); } if ((!reverse && match) || (reverse && !match)) { ap_directive_t *parent = NULL; ap_directive_t *current = NULL; const char *retval; retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd, ¤t, &parent, "<IfVersion"); *(ap_directive_t **)mconfig = current; return retval; } *(ap_directive_t **)mconfig = NULL; return ap_soak_end_container(cmd, "<IfVersion"); }