/*
 * 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;
}
Exemple #2
0
/*
 * 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,
                                      &current, &parent, "<IfVersion");
        *(ap_directive_t **)mconfig = current;
        return retval;
    }

    *(ap_directive_t **)mconfig = NULL;
    return ap_soak_end_container(cmd, "<IfVersion");
}