Beispiel #1
0
static apr_table_t *collection_retrieve_ex(apr_sdbm_t *existing_dbm, modsec_rec *msr, const char *col_name,
    const char *col_key, int col_key_len)
{
    char *dbm_filename = NULL;
    apr_status_t rc;
    apr_sdbm_datum_t key;
    apr_sdbm_datum_t *value = NULL;
    apr_sdbm_t *dbm = NULL;
    apr_table_t *col = NULL;
    const apr_array_header_t *arr;
    apr_table_entry_t *te;
    int expired = 0;
    int i;

    if (msr->txcfg->data_dir == NULL) {
        msr_log(msr, 1, "Unable to retrieve collection (name \"%s\", key \"%s\"). Use "
            "SecDataDir to define data directory first.", log_escape(msr->mp, col_name),
            log_escape_ex(msr->mp, col_key, col_key_len));
        goto cleanup;
    }

    dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "collection_retrieve_ex: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name),
                log_escape(msr->mp, dbm_filename));
    }

    key.dptr = (char *)col_key;
    key.dsize = col_key_len + 1;

    if (existing_dbm == NULL) {
        rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK,
            CREATEMODE, msr->mp);
        if (rc != APR_SUCCESS) {
            dbm = NULL;
            goto cleanup;
        }
    }
    else {
        dbm = existing_dbm;
    }

    value = (apr_sdbm_datum_t *)apr_pcalloc(msr->mp, sizeof(apr_sdbm_datum_t));
    rc = apr_sdbm_fetch(dbm, value, key);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Failed to read from DBM file \"%s\": %s", log_escape(msr->mp,
            dbm_filename), get_apr_error(msr->mp, rc));
        goto cleanup;
    }

    if (value->dptr == NULL) { /* Key not found in DBM file. */
        goto cleanup;
    }

    /* ENH Need expiration (and perhaps other metadata) accessible in blob
     * form to determine if converting to a table is needed.  This will
     * save some cycles.
     */

    /* Transform raw data into a table. */
    col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1);
    if (col == NULL) {
        goto cleanup;
    }

    /* Close after "value" used from fetch or memory may be overwritten. */
    if (existing_dbm == NULL) {
        apr_sdbm_close(dbm);
        dbm = NULL;
    }

    /* Remove expired variables. */
    do {
        arr = apr_table_elts(col);
        te = (apr_table_entry_t *)arr->elts;
        for (i = 0; i < arr->nelts; i++) {
            if (strncmp(te[i].key, "__expire_", 9) == 0) {
                msc_string *var = (msc_string *)te[i].val;
                int expiry_time = atoi(var->value);

                if (expiry_time <= apr_time_sec(msr->request_time)) {
                    char *key_to_expire = te[i].key;

                    /* Done early if the col expired */
                    if (strcmp(key_to_expire, "__expire_KEY") == 0) {
                        expired = 1;
                    }
                    
                    if (msr->txcfg->debuglog_level >= 9) {
                        msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire + 9);
                        msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire);
                    }
                    
                    apr_table_unset(col, key_to_expire + 9);
                    apr_table_unset(col, key_to_expire);
                    
                    if (msr->txcfg->debuglog_level >= 4) {
                        msr_log(msr, 4, "Removed expired variable \"%s\".", key_to_expire + 9);
                    }
                    
                    break;
                }
            }
        }
    } while(!expired && (i != arr->nelts));

    /* Delete the collection if the variable "KEY" does not exist.
     *
     * ENH It would probably be more efficient to hold the DBM
     * open until determined if it needs deleted than to open a second
     * time.
     */
    if (apr_table_get(col, "KEY") == NULL) {
        if (existing_dbm == NULL) {
            rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
                CREATEMODE, msr->mp);
            if (rc != APR_SUCCESS) {
                msr_log(msr, 1, "Failed to access DBM file \"%s\": %s",
                    log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc));
                dbm = NULL;
                goto cleanup;
            }
        }
        else {
            dbm = existing_dbm;
        }

        rc = apr_sdbm_delete(dbm, key);
        if (rc != APR_SUCCESS) {
            msr_log(msr, 1, "Failed deleting collection (name \"%s\", "
                "key \"%s\"): %s", log_escape(msr->mp, col_name),
                log_escape_ex(msr->mp, col_key, col_key_len), get_apr_error(msr->mp, rc));
            goto cleanup;
        }


        if (existing_dbm == NULL) {
            apr_sdbm_close(dbm);
            dbm = NULL;
        }

        if (expired && (msr->txcfg->debuglog_level >= 9)) {
            msr_log(msr, 9, "Collection expired (name \"%s\", key \"%s\").", col_name,
                log_escape_ex(msr->mp, col_key, col_key_len));
        }
        if (msr->txcfg->debuglog_level >= 4) {
            msr_log(msr, 4, "Deleted collection (name \"%s\", key \"%s\").",
                log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len));
        }
        goto cleanup;
    }

    /* Update UPDATE_RATE */
    {
        msc_string *var;
        int create_time, counter;

        var = (msc_string *)apr_table_get(col, "CREATE_TIME");
        if (var == NULL) {
            /* Error. */
        } else {
            create_time = atoi(var->value);
            var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER");
            if (var == NULL) {
                /* Error. */
            } else {
                apr_time_t td;
                counter = atoi(var->value);

                /* UPDATE_RATE is removed on store, so add it back here */
                var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
                var->name = "UPDATE_RATE";
                var->name_len = strlen(var->name);
                apr_table_setn(col, var->name, (void *)var);

                /* NOTE: No rate if there has been no time elapsed */
                td = (apr_time_sec(apr_time_now()) - create_time);
                if (td == 0) {
                    var->value = apr_psprintf(msr->mp, "%d", 0);
                }
                else {
                    var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT,
                        (apr_time_t)((60 * counter)/td));
                }
                var->value_len = strlen(var->value);
            }
        }
    }

    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "Retrieved collection (name \"%s\", key \"%s\").",
            log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len));
    }

    if ((existing_dbm == NULL) && dbm) {
        /* Should not ever get here */
        msr_log(msr, 1, "Internal Error: Collection remained open (name \"%s\", key \"%s\").",
            log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len));

        apr_sdbm_close(dbm);
    }

    return col;

cleanup:

    if ((existing_dbm == NULL) && dbm) {
        apr_sdbm_close(dbm);
    }

    return NULL;
}
Beispiel #2
0
int collections_remove_stale(modsec_rec *msr, const char *col_name) {
    char *dbm_filename = NULL;
    apr_sdbm_datum_t key, value;
    apr_sdbm_t *dbm = NULL;
    apr_status_t rc;
    apr_array_header_t *keys_arr;
    char **keys;
    apr_time_t now = apr_time_sec(msr->request_time);
    int i;

    if (msr->txcfg->data_dir == NULL) {
        /* The user has been warned about this problem enough times already by now.
         * msr_log(msr, 1, "Unable to access collection file (name \"%s\"). Use SecDataDir to "
         *     "define data directory first.", log_escape(msr->mp, col_name));
         */
        goto error;
    }

    if(strstr(col_name,"USER") || strstr(col_name,"SESSION") || strstr(col_name, "RESOURCE"))
        dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", msr->txcfg->webappid, "_", col_name, NULL);
    else
        dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "collections_remove_stale: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name),
                log_escape(msr->mp, dbm_filename));
    }

    rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
            CREATEMODE, msr->mp);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
                get_apr_error(msr->mp, rc));
        dbm = NULL;
        goto error;
    }

    /* First get a list of all keys. */
    keys_arr = apr_array_make(msr->mp, 256, sizeof(char *));
    rc = apr_sdbm_lock(dbm, APR_FLOCK_SHARED);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Failed to lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
            get_apr_error(msr->mp, rc));
        goto error;
    }

    /* No one can write to the file while doing this so
     * do it as fast as possible.
     */
    rc = apr_sdbm_firstkey(dbm, &key);
    while(rc == APR_SUCCESS) {
        char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize - 1);
        *(char **)apr_array_push(keys_arr) = s;
        rc = apr_sdbm_nextkey(dbm, &key);
    }
    apr_sdbm_unlock(dbm);

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "Found %d record(s) in file \"%s\".", keys_arr->nelts,
            log_escape(msr->mp, dbm_filename));
    }

    /* Now retrieve the entires one by one. */
    keys = (char **)keys_arr->elts;
    for (i = 0; i < keys_arr->nelts; i++) {
        key.dptr = keys[i];
        key.dsize = strlen(key.dptr) + 1;

        rc = apr_sdbm_fetch(dbm, &value, key);
        if (rc != APR_SUCCESS) {
            msr_log(msr, 1, "Failed reading DBM file \"%s\": %s",
                log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc));
            goto error;
        }

        if (value.dptr != NULL) {
            apr_table_t *col = NULL;
            msc_string *var = NULL;

            col = collection_unpack(msr, (const unsigned char *)value.dptr, value.dsize, 0);
            if (col == NULL) {
                goto error;
            }

            var = (msc_string *)apr_table_get(col, "__expire_KEY");
            if (var == NULL) {
                msr_log(msr, 1, "Collection cleanup discovered entry with no "
                    "__expire_KEY (name \"%s\", key \"%s\").",
                    log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1));
            } else {
                unsigned int expiry_time = atoi(var->value);

                if (msr->txcfg->debuglog_level >= 9) {
                    msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.",
                        log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1),
                        expiry_time - now);
                }

                if (expiry_time <= now) {
                    rc = apr_sdbm_delete(dbm, key);
                    if (rc != APR_SUCCESS) {
                        msr_log(msr, 1, "Failed deleting collection (name \"%s\", "
                            "key \"%s\"): %s", log_escape(msr->mp, col_name),
                            log_escape_ex(msr->mp, key.dptr, key.dsize - 1), get_apr_error(msr->mp, rc));
                        goto error;
                    }
                    
                    if (msr->txcfg->debuglog_level >= 4) {
                        msr_log(msr, 4, "Removed stale collection (name \"%s\", "
                            "key \"%s\").", log_escape(msr->mp, col_name),
                            log_escape_ex(msr->mp, key.dptr, key.dsize - 1));
                    }
                }
            }
        } else {
            /* Ignore entry not found - it may have been removed in the meantime. */
        }
    }

    apr_sdbm_close(dbm);

    return 1;

error:

    if (dbm) {
        apr_sdbm_close(dbm);
    }

    return -1;
}
Beispiel #3
0
int collection_store(modsec_rec *msr, apr_table_t *col) {
    char *dbm_filename = NULL;
    msc_string *var_name = NULL, *var_key = NULL;
    unsigned char *blob = NULL;
    unsigned int blob_size, blob_offset;
    apr_status_t rc;
    apr_sdbm_datum_t key;
    apr_sdbm_datum_t value;
    apr_sdbm_t *dbm = NULL;
    const apr_array_header_t *arr;
    apr_table_entry_t *te;
    int i;
    const apr_table_t *stored_col = NULL;
    const apr_table_t *orig_col = NULL;

    var_name = (msc_string *)apr_table_get(col, "__name");
    if (var_name == NULL) {
        goto error;
    }

    var_key = (msc_string *)apr_table_get(col, "__key");
    if (var_key == NULL) {
        goto error;
    }

    if (msr->txcfg->data_dir == NULL) {
        msr_log(msr, 1, "Unable to store collection (name \"%s\", key \"%s\"). Use "
            "SecDataDir to define data directory first.", log_escape_ex(msr->mp, var_name->value, var_name->value_len),
            log_escape_ex(msr->mp, var_key->value, var_key->value_len));
        goto error;
    }

    // ENH: lowercase the var name in the filename
    dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL);

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "collection_store: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value),
                log_escape(msr->mp, dbm_filename));
    }

    /* Delete IS_NEW on store. */
    apr_table_unset(col, "IS_NEW");

    /* Delete UPDATE_RATE on store to save space as it is calculated */
    apr_table_unset(col, "UPDATE_RATE");

    /* Update the timeout value. */
    {
        msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT");
        if (var != NULL) {
            int timeout = atoi(var->value);
            var = (msc_string *)apr_table_get(col, "__expire_KEY");
            if (var != NULL) {
                var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout));
                var->value_len = strlen(var->value);
            }
        }
    }

    /* LAST_UPDATE_TIME */
    {
        msc_string *var = (msc_string *)apr_table_get(col, "LAST_UPDATE_TIME");
        if (var == NULL) {
            var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
            var->name = "LAST_UPDATE_TIME";
            var->name_len = strlen(var->name);
            apr_table_setn(col, var->name, (void *)var);
        }
        var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now())));
        var->value_len = strlen(var->value);
    }

    /* UPDATE_COUNTER */
    {
        msc_string *var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER");
        int counter = 0;
        if (var == NULL) {
            var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
            var->name = "UPDATE_COUNTER";
            var->name_len = strlen(var->name);
            apr_table_setn(col, var->name, (void *)var);
        } else {
            counter = atoi(var->value);
        }
        var->value = apr_psprintf(msr->mp, "%d", counter + 1);
        var->value_len = strlen(var->value);
    }

    /* ENH Make the expiration timestamp accessible in blob form so that
     * it is easier/faster to determine expiration without having to
     * convert back to table form
     */

    rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
        CREATEMODE, msr->mp);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
            get_apr_error(msr->mp, rc));
        dbm = NULL;
        goto error;
    }

    /* Need to lock to pull in the stored data again and apply deltas. */
    rc = apr_sdbm_lock(dbm, APR_FLOCK_EXCLUSIVE);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
            get_apr_error(msr->mp, rc));
        goto error;
    }

    /* If there is an original value, then create a delta and
     * apply the delta to the current value */
    orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value);
    if (orig_col != NULL) {
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "Re-retrieving collection prior to store: %s",
                apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value));
        }

        stored_col = (const apr_table_t *)collection_retrieve_ex(dbm, msr, var_name->value, var_key->value, var_key->value_len);
    }

    /* Merge deltas and calculate the size first. */
    blob_size = 3 + 2;
    arr = apr_table_elts(col);
    te = (apr_table_entry_t *)arr->elts;
    for (i = 0; i < arr->nelts; i++) {
        msc_string *var = (msc_string *)te[i].val;
        int len;

        /* If there is an original value, then apply the delta
         * to the latest stored value */
        if (stored_col != NULL) {
            const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name);
            if (orig_var != NULL) {
                const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name);
                if (stored_var != NULL) {
                    int origval = atoi(orig_var->value);
                    int ourval = atoi(var->value);
                    int storedval = atoi(stored_var->value);
                    int delta = ourval - origval;
                    int newval = storedval + delta;

                    if (newval < 0) newval = 0; /* Counters never go below zero. */

                    var->value = apr_psprintf(msr->mp, "%d", newval);
                    var->value_len = strlen(var->value);
                    
                    if (msr->txcfg->debuglog_level >= 9) {
                        msr_log(msr, 9, "Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]",
                        log_escape_ex(msr->mp, var_name->value, var_name->value_len),
                        log_escape_ex(msr->mp, var->name, var->name_len),
                        origval, ourval, delta, storedval, delta, newval, var->value, var->value_len);
                    }
                }
            }
        }

        len = var->name_len + 1;
        if (len >= 65536) len = 65536;
        blob_size += len + 2;

        len = var->value_len + 1;
        if (len >= 65536) len = 65536;
        blob_size += len + 2;
    }

    /* Now generate the binary object. */
    blob = apr_pcalloc(msr->mp, blob_size);
    if (blob == NULL) {
        goto error;
    }

    blob[0] = 0x49;
    blob[1] = 0x52;
    blob[2] = 0x01;

    blob_offset = 3;
    arr = apr_table_elts(col);
    te = (apr_table_entry_t *)arr->elts;
    for (i = 0; i < arr->nelts; i++) {
        msc_string *var = (msc_string *)te[i].val;
        int len;

        len = var->name_len + 1;
        if (len >= 65536) len = 65536;

        blob[blob_offset + 0] = (len & 0xff00) >> 8;
        blob[blob_offset + 1] = len & 0x00ff;
        memcpy(blob + blob_offset + 2, var->name, len - 1);
        blob[blob_offset + 2 + len - 1] = '\0';
        blob_offset += 2 + len;

        len = var->value_len + 1;
        if (len >= 65536) len = 65536;

        blob[blob_offset + 0] = (len & 0xff00) >> 8;
        blob[blob_offset + 1] = len & 0x00ff;
        memcpy(blob + blob_offset + 2, var->value, len - 1);
        blob[blob_offset + 2 + len - 1] = '\0';
        blob_offset += 2 + len;

        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "Wrote variable: name \"%s\", value \"%s\".",
                log_escape_ex(msr->mp, var->name, var->name_len),
                log_escape_ex(msr->mp, var->value, var->value_len));
        }
    }

    blob[blob_offset] = 0;
    blob[blob_offset + 1] = 0;

    /* And, finally, store it. */
    key.dptr = var_key->value;
    key.dsize = var_key->value_len + 1;

    value.dptr = (char *)blob;
    value.dsize = blob_size;

    rc = apr_sdbm_store(dbm, key, value, APR_SDBM_REPLACE);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Failed to write to DBM file \"%s\": %s", dbm_filename,
            get_apr_error(msr->mp, rc));
        goto error;
    }

    apr_sdbm_close(dbm);

    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "Persisted collection (name \"%s\", key \"%s\").",
            log_escape_ex(msr->mp, var_name->value, var_name->value_len),
            log_escape_ex(msr->mp, var_key->value, var_key->value_len));
    }

    return 0;

error:

    if (dbm) {
        apr_sdbm_close(dbm);
    }

    return -1;
}
Beispiel #4
0
/*
 * This routine is called to perform any module-specific fixing of header
 * fields, et cetera.  It is invoked just before any content-handler.
 *
 * This is a RUN_ALL HOOK.
 */
static int fixups(request_rec *r) {
    int ret;
    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;

    // Stop if this is not the main request
    if (r->main != NULL || r->prev != NULL)
        return DECLINED;

    // Process only if POST / PUT request
    if (r->method_number != M_POST && r->method_number != M_PUT)
        return DECLINED;

    defender_config_t *defc = (defender_config_t *) ap_get_module_config(r->request_config, &defender_module);
    RuntimeScanner *scanner = defc->vpRuntimeScanner;

    if (scanner->contentLengthProvided && scanner->contentLength == 0)
        return scanner->processBody();

    if (scanner->contentType == CONTENT_TYPE_UNSUPPORTED)
        return scanner->processBody();

    if (scanner->bodyLimitExceeded)
        return scanner->processBody();

    if (!scanner->contentLengthProvided)
        return HTTP_NOT_IMPLEMENTED;

    if (scanner->transferEncodingProvided /*&& scanner->transferEncoding == TRANSFER_ENCODING_UNSUPPORTED*/)
        return HTTP_NOT_IMPLEMENTED;

    // Retrieve the body
    // Pre-allocate necessary bytes
    scanner->body.reserve(scanner->contentLength);

    // Wait for the body to be fully received 
    apr_bucket_brigade *bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    unsigned int prev_nr_buckets = 0;
    if (bb == NULL)
        goto read_error_out;
    do {
        int rc = ap_get_brigade(r->input_filters, bb, AP_MODE_SPECULATIVE, APR_BLOCK_READ, LONG_MAX);
        if (rc != APR_SUCCESS) {
            ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Error reading body: %s", get_apr_error(r->pool, rc));
            goto read_error_out;
        }

        unsigned int nr_buckets = 0;
        // Iterate over buckets
        for (apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
             bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket)) {
            // Stop if we reach the EOS bucket
            if (APR_BUCKET_IS_EOS(bucket))
                break;

            // Ignore non data buckets
            if (APR_BUCKET_IS_METADATA(bucket) || APR_BUCKET_IS_FLUSH(bucket))
                continue;

            nr_buckets++;
            // Skip already copied buckets
            if (nr_buckets <= prev_nr_buckets)
                continue;

            const char *buf;
            apr_size_t nbytes;
            int rv = apr_bucket_read(bucket, &buf, &nbytes, APR_BLOCK_READ);
            if (rv != APR_SUCCESS) {
                ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Failed reading input / bucket: %s",
                              get_apr_error(r->pool, rv));
                goto read_error_out;
            }

//            cerr << "bucket #" << nr_buckets - 1 << " received, nbytes: " << nbytes << ", total len: "
//                 << scanner->body.length() + nbytes << endl;

            // More bytes in the BODY than specified in the content-length
            if (scanner->body.length() + nbytes > scanner->contentLength) {
                ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Too much POST data: received body of %lu bytes but "
                        "got content-length: %lu", scanner->body.length() + nbytes, scanner->contentLength);
                goto read_error_out;
            }

            // More bytes in the BODY than specified by the allowed body limit
            if (scanner->body.length() + nbytes > dcfg->requestBodyLimit) {
                scanner->bodyLimitExceeded = true;
                ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Body limit exceeded (%lu)", dcfg->requestBodyLimit);
                break;
            }

            scanner->body.append(buf, nbytes);
        }
        prev_nr_buckets = nr_buckets;

        if (scanner->body.length() == scanner->contentLength)
            break;

        apr_brigade_cleanup(bb);
        apr_sleep(1000);
    } while (true);

//    cerr << "[pid " << getpid() << "] read " << scanner->body.length() << " bytes, ";
//    cerr << "content-length: " << scanner->contentLength << endl;
//    cerr << "body: " << scanner->body << endl;

    // Run scanner
    ret = scanner->processBody();

    if (dcfg->useenv)
        ret = pass_in_env(r, scanner);

//    cerr << "[pid " << getpid() << "] body (" << scanner->body.length() << ") scanned" << endl;

    return ret;

    read_error_out:
    if (dcfg->useenv) return DECLINED;
    return HTTP_INTERNAL_SERVER_ERROR;
}
Beispiel #5
0
/**
 * Perform geographical lookup on target.
 */
int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **error_msg)
{
    apr_sockaddr_t *addr;
    long ipnum = 0;
    char *targetip = NULL;
    geo_db *geo = msr->txcfg->geo;
    char errstr[1024];
    unsigned char buf[2* GEO_MAX_RECORD_LEN];
    const int reclen = 3; /* Algorithm needs changed if this changes */
    apr_size_t nbytes;
    unsigned int rec_val = 0;
    apr_off_t seekto = 0;
    apr_status_t ret;
    int rc;
    int country = 0;
    int level;
    double dtmp;
    int itmp;

    *error_msg = NULL;

    /* init */
    georec->country_code = geo_country_code[0];
    georec->country_code3 = geo_country_code3[0];
    georec->country_name = geo_country_name[0];
    georec->country_continent = geo_country_continent[0];
    georec->region = "";
    georec->city = "";
    georec->postal_code = "";
    georec->latitude = 0;
    georec->longitude = 0;
    georec->dma_code = 0;
    georec->area_code = 0;

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "GEO: Looking up \"%s\".", log_escape(msr->mp, target));
    }

    /* NOTE: This only works with ipv4 */
    if ((rc = apr_sockaddr_info_get(&addr, target, APR_INET, 0, 0, msr->mp)) != APR_SUCCESS) {

        *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
        msr_log(msr, 4, "%s", *error_msg);
        return 0;
    }
    if ((rc = apr_sockaddr_ip_get(&targetip, addr)) != APR_SUCCESS) {
        *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
        msr_log(msr, 4, "%s", *error_msg);
        return 0;
    };

    /* Why is this in host byte order? */
    ipnum = ntohl(addr->sa.sin.sin_addr.s_addr);

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08lx). %lu", targetip, ipnum, ipnum);
    }

    ret = apr_global_mutex_lock(msr->modsecurity->geo_lock);
    if (ret != APR_SUCCESS) {
        msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
                get_apr_error(msr->mp, ret));
    }

    for (level = 31; level >= 0; level--) {
        /* Read the record */
        seekto = 2 * reclen * rec_val;
        apr_file_seek(geo->db, APR_SET, &seekto);
        /* TODO: check rc */
        rc = apr_file_read_full(geo->db, &buf, (2 * reclen), &nbytes);

        /* NOTE: This is hard-coded for size 3 records */
        /* Left */
        if ((ipnum & (1 << level)) == 0) {
            rec_val =   (buf[3*0 + 0] << (0*8)) +
                        (buf[3*0 + 1] << (1*8)) +
                        (buf[3*0 + 2] << (2*8));
        }
        /* Right */
        else {
            rec_val =   (buf[3*1 + 0] << (0*8)) +
                        (buf[3*1 + 1] << (1*8)) +
                        (buf[3*1 + 2] << (2*8));
        }

        /* If we are past the country offset, then we are done */
        if (rec_val >= geo->ctry_offset) {
            break;
        }
    }

    if (rec_val == geo->ctry_offset) {
        *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\").", log_escape(msr->mp, target));
        msr_log(msr, 4, "%s", *error_msg);

        ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock);
        if (ret != APR_SUCCESS) {
            msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
                    get_apr_error(msr->mp, ret));
        }

        return 0;
    }

    if (geo->dbtype == GEO_COUNTRY_DATABASE) {
        country = rec_val;
        country -= geo->ctry_offset;
        if ((country <= 0) || (country > GEO_COUNTRY_LAST)) {
            *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country);
            msr_log(msr, 4, "%s", *error_msg);

            ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock);
            if (ret != APR_SUCCESS) {
                msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
                        get_apr_error(msr->mp, ret));
            }

            return 0;
        }

        /* Country */
        georec->country_code = geo_country_code[country];
        georec->country_code3 = geo_country_code3[country];
        georec->country_name = geo_country_name[country];
        georec->country_continent = geo_country_continent[country];
    }
    else {
        int field_len = 0;
        int rec_offset = 0;
        int remaining = GEO_CITY_RECORD_LEN;
        unsigned char cbuf[GEO_CITY_RECORD_LEN];

        seekto = rec_val + (2 * reclen - 1) * geo->ctry_offset;
        apr_file_seek(geo->db, APR_SET, &seekto);
        /* TODO: check rc */
        rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes);

        country = cbuf[0];
        if ((country <= 0) || (country > GEO_COUNTRY_LAST)) {
            *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country);
            msr_log(msr, 4, "%s", *error_msg);

            ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock);
            if (ret != APR_SUCCESS) {
                msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
                        get_apr_error(msr->mp, ret));
            }

            return 0;
        }
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: rec=\"%s\"", log_escape_raw(msr->mp, cbuf, sizeof(cbuf)));
        }

        /* Country */
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: country=\"%.*s\"", (1*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf)));
        }
        georec->country_code = geo_country_code[country];
        georec->country_code3 = geo_country_code3[country];
        georec->country_name = geo_country_name[country];
        georec->country_continent = geo_country_continent[country];
        rec_offset++;
        remaining -= rec_offset;

        /* Region */
        field_len = field_length((const char *)cbuf+rec_offset, remaining);
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: region=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
        }
        georec->region = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
        rec_offset += field_len + 1;
        remaining -= field_len + 1;

        /* City */
        field_len = field_length((const char *)cbuf+rec_offset, remaining);
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: city=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
        }
        georec->city = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
        rec_offset += field_len + 1;
        remaining -= field_len + 1;

        /* Postal Code */
        field_len = field_length((const char *)cbuf+rec_offset, remaining);
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: postal_code=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
        }
        georec->postal_code = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
        rec_offset += field_len + 1;
        remaining -= field_len + 1;

        /* Latitude */
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: latitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
        }
        dtmp = cbuf[rec_offset] +
               (cbuf[rec_offset+1] << 8) +
               (cbuf[rec_offset+2] << 16);
        georec->latitude = dtmp/10000 - 180;
        rec_offset += 3;
        remaining -= 3;


        /* Longitude */
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: longitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
        }
        dtmp = cbuf[rec_offset] +
              (cbuf[rec_offset+1] << 8) +
              (cbuf[rec_offset+2] << 16);
        georec->longitude = dtmp/10000 - 180;
        rec_offset += 3;
        remaining -= 3;

        /* dma/area codes are in city rev1 and US only */
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "GEO: dma/area=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
        }
        if (geo->dbtype == GEO_CITY_DATABASE_1
            && georec->country_code[0] == 'U'
            && georec->country_code[1] == 'S')
        {
            /* DMA Code */
            itmp = cbuf[rec_offset] +
                  (cbuf[rec_offset+1] << 8) +
                  (cbuf[rec_offset+2] << 16);
            georec->dma_code = itmp / 1000;
            georec->area_code = itmp % 1000;
            rec_offset += 6;
            remaining -= 6;
        }

    }

    *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded.", log_escape(msr->mp, target));

    ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock);
    if (ret != APR_SUCCESS) {
        msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
                get_apr_error(msr->mp, ret));
    }

    return 1;
}
/**
 * Execute system command. First line of the output will be returned in
 * the "output" parameter.
 */
int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) {
    apr_procattr_t *procattr = NULL;
    apr_proc_t *procnew = NULL;
    apr_status_t rc = APR_SUCCESS;
    const char *const *env = NULL;
    apr_file_t *script_out = NULL;
    request_rec *r = msr->r;

    if (argv == NULL) {
        argv = apr_pcalloc(r->pool, 3 * sizeof(char *));
        argv[0] = command;
        argv[1] = NULL;
    }

    ap_add_cgi_vars(r);
    ap_add_common_vars(r);

    /* PHP hack, getting around its silly security checks. */
    apr_table_add(r->subprocess_env, "PATH_TRANSLATED", command);
    apr_table_add(r->subprocess_env, "REDIRECT_STATUS", "302");

    env = (const char * const *)ap_create_environment(r->pool, r->subprocess_env);
    if (env == NULL) {
        msr_log(msr, 1, "Exec: Unable to create environment.");
        return -1;
    }

    procnew = apr_pcalloc(r->pool, sizeof(*procnew));
    if (procnew == NULL) {
        msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew));
        return -1;
    }

    apr_procattr_create(&procattr, r->pool);
    if (procattr == NULL) {
        msr_log(msr, 1, "Exec: Unable to create procattr.");
        return -1;
    }

    apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE);
    apr_procattr_cmdtype_set(procattr, APR_SHELLCMD);

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command));
    }

    rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool);
    if (rc != APR_SUCCESS) {
        msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command),
            get_apr_error(r->pool, rc));
        return -1;
    }

    apr_pool_note_subprocess(r->pool, procnew, APR_KILL_AFTER_TIMEOUT);

    script_out = procnew->out;
    if (!script_out) {
        msr_log(msr, 1, "Exec: Failed to get script output pipe.");
        return -1;
    }

    apr_file_pipe_timeout_set(script_out, r->server->timeout);

    /* Now read from the pipe. */
    {
        char buf[260] = "";
        char *p = buf;
        apr_size_t nbytes = 255;
        apr_status_t rc2;

        rc2 = apr_file_read(script_out, buf, &nbytes);
        if (rc2 == APR_SUCCESS) {
            buf[nbytes] = 0;

            /* if there is more than one line ignore them */
            while(*p != 0) {
                if (*p == 0x0a) *p = 0;
                p++;
            }

            if (msr->txcfg->debuglog_level >= 4) {
                msr_log(msr, 4, "Exec: First line from script output: \"%s\"",
                    log_escape(r->pool, buf));
            }

            if (output != NULL) *output = apr_pstrdup(r->pool, buf);

            /* Soak up the remaining data. */
            nbytes = 255;
            while(apr_file_read(script_out, buf, &nbytes) == APR_SUCCESS) nbytes = 255;
        } else {
            msr_log(msr, 1, "Exec: Execution failed while reading output: %s (%s)",
                log_escape_nq(r->pool, command),
                get_apr_error(r->pool, rc2));
            return -1;
        }
    }

    apr_proc_wait(procnew, NULL, NULL, APR_WAIT);

    return 1;
}