Blame apache2/persist_dbm.c

Packit Service 384592
/*
Packit Service 384592
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
Packit Service 384592
* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
Packit Service 384592
*
Packit Service 384592
* You may not use this file except in compliance with
Packit Service 384592
* the License.  You may obtain a copy of the License at
Packit Service 384592
*
Packit Service 384592
*     http://www.apache.org/licenses/LICENSE-2.0
Packit Service 384592
*
Packit Service 384592
* If any of the files related to licensing are missing or if you have any
Packit Service 384592
* other questions related to licensing please contact Trustwave Holdings, Inc.
Packit Service 384592
* directly using the email address security@modsecurity.org.
Packit Service 384592
*/
Packit Service 384592
Packit Service 384592
#include "persist_dbm.h"
Packit Service 384592
#include "apr_sdbm.h"
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob, unsigned int blob_size,
Packit Service 384592
    int log_vars)
Packit Service 384592
{
Packit Service 384592
    apr_table_t *col = NULL;
Packit Service 384592
    unsigned int blob_offset;
Packit Service 384592
Packit Service 384592
    col = apr_table_make(msr->mp, 32);
Packit Service 384592
    if (col == NULL) return NULL;
Packit Service 384592
Packit Service 384592
    /* ENH verify the first 3 bytes (header) */
Packit Service 384592
Packit Service 384592
    blob_offset = 3;
Packit Service 384592
    while (blob_offset + 1 < blob_size) {
Packit Service 384592
        msc_string *var = apr_pcalloc(msr->mp, sizeof(msc_string));
Packit Service 384592
Packit Service 384592
        var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1];
Packit Service 384592
        if (var->name_len == 0) {
Packit Service 384592
            /* Is the length a name length, or just the end of the blob? */
Packit Service 384592
            if (blob_offset < blob_size - 2) {
Packit Service 384592
                /* This should never happen as the name length
Packit Service 384592
                 * includes the terminating NUL and should be 1 for ""
Packit Service 384592
                 */
Packit Service 384592
                if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
                    msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset));
Packit Service 384592
                }
Packit Service 384592
                msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1);
Packit Service 384592
            }
Packit Service 384592
            break;
Packit Service 384592
        }
Packit Service 384592
        else if (var->name_len > 65536) {
Packit Service 384592
            /* This should never happen as the length is restricted on store
Packit Service 384592
             * to 65536.
Packit Service 384592
             */
Packit Service 384592
            if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
                msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset));
Packit Service 384592
            }
Packit Service 384592
            msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1);
Packit Service 384592
            break;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        blob_offset += 2;
Packit Service 384592
        if (blob_offset + var->name_len > blob_size) return NULL;
Packit Service 384592
        var->name = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->name_len - 1);
Packit Service 384592
        blob_offset += var->name_len;
Packit Service 384592
        var->name_len--;
Packit Service 384592
Packit Service 384592
        var->value_len = (blob[blob_offset] << 8) + blob[blob_offset + 1];
Packit Service 384592
        blob_offset += 2;
Packit Service 384592
Packit Service 384592
        if (blob_offset + var->value_len > blob_size) return NULL;
Packit Service 384592
        var->value = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->value_len - 1);
Packit Service 384592
        blob_offset += var->value_len;
Packit Service 384592
        var->value_len--;
Packit Service 384592
Packit Service 384592
        if (log_vars && (msr->txcfg->debuglog_level >= 9)) {
Packit Service 384592
            msr_log(msr, 9, "collection_unpack: Read variable: name \"%s\", value \"%s\".",
Packit Service 384592
                log_escape_ex(msr->mp, var->name, var->name_len),
Packit Service 384592
                log_escape_ex(msr->mp, var->value, var->value_len));
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        apr_table_addn(col, var->name, (void *)var);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return col;
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
static apr_table_t *collection_retrieve_ex(apr_sdbm_t *existing_dbm, modsec_rec *msr, const char *col_name,
Packit Service 384592
    const char *col_key, int col_key_len)
Packit Service 384592
{
Packit Service 384592
    char *dbm_filename = NULL;
Packit Service 384592
    apr_status_t rc;
Packit Service 384592
    apr_sdbm_datum_t key;
Packit Service 384592
    apr_sdbm_datum_t *value = NULL;
Packit Service 384592
    apr_sdbm_t *dbm = NULL;
Packit Service 384592
    apr_table_t *col = NULL;
Packit Service 384592
    const apr_array_header_t *arr;
Packit Service 384592
    apr_table_entry_t *te;
Packit Service 384592
    int expired = 0;
Packit Service 384592
    int i;
Packit Service 384592
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->data_dir == NULL) {
Packit Service 384592
        msr_log(msr, 1, "collection_retrieve_ex: Unable to retrieve collection (name \"%s\", key \"%s\"). Use "
Packit Service 384592
            "SecDataDir to define data directory first.", log_escape(msr->mp, col_name),
Packit Service 384592
            log_escape_ex(msr->mp, col_key, col_key_len));
Packit Service 384592
        goto cleanup;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
        msr_log(msr, 9, "collection_retrieve_ex: collection_retrieve_ex: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name),
Packit Service 384592
                log_escape(msr->mp, dbm_filename));
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    key.dptr = (char *)col_key;
Packit Service 384592
    key.dsize = col_key_len + 1;
Packit Service 384592
Packit Service 384592
    if (existing_dbm == NULL) {
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock);
Packit Service 384592
        if (rc != APR_SUCCESS) {
Packit Service 384592
            msr_log(msr, 1, "collection_retrieve_ex: Failed to lock proc mutex: %s",
Packit Service 384592
                    get_apr_error(msr->mp, rc));
Packit Service 384592
            goto cleanup;
Packit Service 384592
        }
Packit Service 384592
#endif
Packit Service 384592
        rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK,
Packit Service 384592
            CREATEMODE, msr->mp);
Packit Service 384592
        if (rc != APR_SUCCESS) {
Packit Service 384592
            dbm = NULL;
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
            apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
            goto cleanup;
Packit Service 384592
        }
Packit Service 384592
    }
Packit Service 384592
    else {
Packit Service 384592
        dbm = existing_dbm;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    value = (apr_sdbm_datum_t *)apr_pcalloc(msr->mp, sizeof(apr_sdbm_datum_t));
Packit Service 384592
    rc = apr_sdbm_fetch(dbm, value, key);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
        msr_log(msr, 1, "collection_retrieve_ex: Failed to read from DBM file \"%s\": %s", log_escape(msr->mp,
Packit Service 384592
            dbm_filename), get_apr_error(msr->mp, rc));
Packit Service 384592
        goto cleanup;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if (value->dptr == NULL) { /* Key not found in DBM file. */
Packit Service 384592
        goto cleanup;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* ENH Need expiration (and perhaps other metadata) accessible in blob
Packit Service 384592
     * form to determine if converting to a table is needed.  This will
Packit Service 384592
     * save some cycles.
Packit Service 384592
     */
Packit Service 384592
Packit Service 384592
    /* Transform raw data into a table. */
Packit Service 384592
    col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1);
Packit Service 384592
    if (col == NULL) {
Packit Service 384592
        goto cleanup;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* Close after "value" used from fetch or memory may be overwritten. */
Packit Service 384592
    if (existing_dbm == NULL) {
Packit Service 384592
        apr_sdbm_close(dbm);
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
        dbm = NULL;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* Remove expired variables. */
Packit Service 384592
    do {
Packit Service 384592
        arr = apr_table_elts(col);
Packit Service 384592
        te = (apr_table_entry_t *)arr->elts;
Packit Service 384592
        for (i = 0; i < arr->nelts; i++) {
Packit Service 384592
            if (strncmp(te[i].key, "__expire_", 9) == 0) {
Packit Service 384592
                msc_string *var = (msc_string *)te[i].val;
Packit Service 384592
                int expiry_time = atoi(var->value);
Packit Service 384592
Packit Service 384592
                if (expiry_time <= apr_time_sec(msr->request_time)) {
Packit Service 384592
                    char *key_to_expire = te[i].key;
Packit Service 384592
Packit Service 384592
                    /* Done early if the col expired */
Packit Service 384592
                    if (strcmp(key_to_expire, "__expire_KEY") == 0) {
Packit Service 384592
                        expired = 1;
Packit Service 384592
                    }
Packit Service 384592
                    
Packit Service 384592
                    if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
                        msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire + 9);
Packit Service 384592
                        msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire);
Packit Service 384592
                    }
Packit Service 384592
                    
Packit Service 384592
                    apr_table_unset(col, key_to_expire + 9);
Packit Service 384592
                    apr_table_unset(col, key_to_expire);
Packit Service 384592
                    
Packit Service 384592
                    if (msr->txcfg->debuglog_level >= 4) {
Packit Service 384592
                        msr_log(msr, 4, "collection_retrieve_ex: Removed expired variable \"%s\".", key_to_expire + 9);
Packit Service 384592
                    }
Packit Service 384592
                    
Packit Service 384592
                    break;
Packit Service 384592
                }
Packit Service 384592
            }
Packit Service 384592
        }
Packit Service 384592
    } while(!expired && (i != arr->nelts));
Packit Service 384592
Packit Service 384592
    /* Delete the collection if the variable "KEY" does not exist.
Packit Service 384592
     *
Packit Service 384592
     * ENH It would probably be more efficient to hold the DBM
Packit Service 384592
     * open until determined if it needs deleted than to open a second
Packit Service 384592
     * time.
Packit Service 384592
     */
Packit Service 384592
    if (apr_table_get(col, "KEY") == NULL) {
Packit Service 384592
        if (existing_dbm == NULL) {
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
            rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock);
Packit Service 384592
            if (rc != APR_SUCCESS) {
Packit Service 384592
                msr_log(msr, 1, "collection_retrieve_ex: Failed to lock proc mutex: %s",
Packit Service 384592
                        get_apr_error(msr->mp, rc));
Packit Service 384592
                goto cleanup;
Packit Service 384592
            }
Packit Service 384592
#endif
Packit Service 384592
            rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
Packit Service 384592
                CREATEMODE, msr->mp);
Packit Service 384592
            if (rc != APR_SUCCESS) {
Packit Service 384592
                msr_log(msr, 1, "collection_retrieve_ex: Failed to access DBM file \"%s\": %s",
Packit Service 384592
                    log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc));
Packit Service 384592
                dbm = NULL;
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
                apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
                goto cleanup;
Packit Service 384592
            }
Packit Service 384592
        }
Packit Service 384592
        else {
Packit Service 384592
            dbm = existing_dbm;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        rc = apr_sdbm_delete(dbm, key);
Packit Service 384592
        if (rc != APR_SUCCESS) {
Packit Service 384592
#ifdef LOG_NO_COLL_DELET_PB
Packit Service 384592
		if (msr->txcfg->debuglog_level >= 9)
Packit Service 384592
#endif
Packit Service 384592
		msr_log(msr, 1, "collection_retrieve_ex: Failed deleting collection (name \"%s\", "
Packit Service 384592
			"key \"%s\"): %s", log_escape(msr->mp, col_name),
Packit Service 384592
			log_escape_ex(msr->mp, col_key, col_key_len), get_apr_error(msr->mp, rc));
Packit Service 384592
		msr->msc_sdbm_delete_error = 1;
Packit Service 384592
            goto cleanup;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
Packit Service 384592
        if (existing_dbm == NULL) {
Packit Service 384592
            apr_sdbm_close(dbm);
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
            apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
            dbm = NULL;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        if (expired && (msr->txcfg->debuglog_level >= 9)) {
Packit Service 384592
            msr_log(msr, 9, "collection_retrieve_ex: Collection expired (name \"%s\", key \"%s\").", col_name,
Packit Service 384592
                log_escape_ex(msr->mp, col_key, col_key_len));
Packit Service 384592
        }
Packit Service 384592
        if (msr->txcfg->debuglog_level >= 4) {
Packit Service 384592
            msr_log(msr, 4, "collection_retrieve_ex: Deleted collection (name \"%s\", key \"%s\").",
Packit Service 384592
                log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len));
Packit Service 384592
        }
Packit Service 384592
        goto cleanup;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* Update UPDATE_RATE */
Packit Service 384592
    {
Packit Service 384592
        msc_string *var;
Packit Service 384592
        int create_time, counter;
Packit Service 384592
Packit Service 384592
        var = (msc_string *)apr_table_get(col, "CREATE_TIME");
Packit Service 384592
        if (var == NULL) {
Packit Service 384592
            /* Error. */
Packit Service 384592
        } else {
Packit Service 384592
            create_time = atoi(var->value);
Packit Service 384592
            var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER");
Packit Service 384592
            if (var == NULL) {
Packit Service 384592
                /* Error. */
Packit Service 384592
            } else {
Packit Service 384592
                apr_time_t td;
Packit Service 384592
                counter = atoi(var->value);
Packit Service 384592
Packit Service 384592
                /* UPDATE_RATE is removed on store, so add it back here */
Packit Service 384592
                var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
Packit Service 384592
                var->name = "UPDATE_RATE";
Packit Service 384592
                var->name_len = strlen(var->name);
Packit Service 384592
                apr_table_setn(col, var->name, (void *)var);
Packit Service 384592
Packit Service 384592
                /* NOTE: No rate if there has been no time elapsed */
Packit Service 384592
                td = (apr_time_sec(apr_time_now()) - create_time);
Packit Service 384592
                if (td == 0) {
Packit Service 384592
                    var->value = apr_psprintf(msr->mp, "%d", 0);
Packit Service 384592
                }
Packit Service 384592
                else {
Packit Service 384592
                    var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT,
Packit Service 384592
                        (apr_time_t)((60 * counter)/td));
Packit Service 384592
                }
Packit Service 384592
                var->value_len = strlen(var->value);
Packit Service 384592
            }
Packit Service 384592
        }
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->debuglog_level >= 4) {
Packit Service 384592
        msr_log(msr, 4, "collection_retrieve_ex: Retrieved collection (name \"%s\", key \"%s\").",
Packit Service 384592
            log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len));
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if ((existing_dbm == NULL) && dbm) {
Packit Service 384592
        /* Should not ever get here */
Packit Service 384592
        msr_log(msr, 1, "collection_retrieve_ex: Internal Error: Collection remained open (name \"%s\", key \"%s\").",
Packit Service 384592
            log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len));
Packit Service 384592
Packit Service 384592
        apr_sdbm_close(dbm);
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return col;
Packit Service 384592
Packit Service 384592
cleanup:
Packit Service 384592
Packit Service 384592
    if ((existing_dbm == NULL) && dbm) {
Packit Service 384592
        apr_sdbm_close(dbm);
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return NULL;
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name,
Packit Service 384592
    const char *col_key, int col_key_len)
Packit Service 384592
{
Packit Service 384592
    apr_time_t time_before = apr_time_now();
Packit Service 384592
    apr_table_t *rtable = NULL;
Packit Service 384592
    
Packit Service 384592
    rtable = collection_retrieve_ex(NULL, msr, col_name, col_key, col_key_len);
Packit Service 384592
    
Packit Service 384592
    msr->time_storage_read += apr_time_now() - time_before;
Packit Service 384592
    
Packit Service 384592
    return rtable;
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
int collection_store(modsec_rec *msr, apr_table_t *col) {
Packit Service 384592
    char *dbm_filename = NULL;
Packit Service 384592
    msc_string *var_name = NULL, *var_key = NULL;
Packit Service 384592
    unsigned char *blob = NULL;
Packit Service 384592
    unsigned int blob_size, blob_offset;
Packit Service 384592
    apr_status_t rc;
Packit Service 384592
    apr_sdbm_datum_t key;
Packit Service 384592
    apr_sdbm_datum_t value;
Packit Service 384592
    apr_sdbm_t *dbm = NULL;
Packit Service 384592
    const apr_array_header_t *arr;
Packit Service 384592
    apr_table_entry_t *te;
Packit Service 384592
    int i;
Packit Service 384592
    const apr_table_t *stored_col = NULL;
Packit Service 384592
    const apr_table_t *orig_col = NULL;
Packit Service 384592
Packit Service 384592
    var_name = (msc_string *)apr_table_get(col, "__name");
Packit Service 384592
    if (var_name == NULL) {
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    var_key = (msc_string *)apr_table_get(col, "__key");
Packit Service 384592
    if (var_key == NULL) {
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->data_dir == NULL) {
Packit Service 384592
        msr_log(msr, 1, "collection_store: Unable to store collection (name \"%s\", key \"%s\"). Use "
Packit Service 384592
            "SecDataDir to define data directory first.", log_escape_ex(msr->mp, var_name->value, var_name->value_len),
Packit Service 384592
            log_escape_ex(msr->mp, var_key->value, var_key->value_len));
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    // ENH: lowercase the var name in the filename
Packit Service 384592
    dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL);
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
        msr_log(msr, 9, "collection_store: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value),
Packit Service 384592
                log_escape(msr->mp, dbm_filename));
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    /* Need to lock to pull in the stored data again and apply deltas. */
Packit Service 384592
    rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
        msr_log(msr, 1, "collection_store: Failed to lock proc mutex: %s",
Packit Service 384592
                get_apr_error(msr->mp, rc));
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
    /* Delete IS_NEW on store. */
Packit Service 384592
    apr_table_unset(col, "IS_NEW");
Packit Service 384592
Packit Service 384592
    /* Delete UPDATE_RATE on store to save space as it is calculated */
Packit Service 384592
    apr_table_unset(col, "UPDATE_RATE");
Packit Service 384592
Packit Service 384592
    /* Update the timeout value. */
Packit Service 384592
    {
Packit Service 384592
        msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT");
Packit Service 384592
        if (var != NULL) {
Packit Service 384592
            int timeout = atoi(var->value);
Packit Service 384592
            var = (msc_string *)apr_table_get(col, "__expire_KEY");
Packit Service 384592
            if (var != NULL) {
Packit Service 384592
                var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout));
Packit Service 384592
                var->value_len = strlen(var->value);
Packit Service 384592
            }
Packit Service 384592
        }
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* LAST_UPDATE_TIME */
Packit Service 384592
    {
Packit Service 384592
        msc_string *var = (msc_string *)apr_table_get(col, "LAST_UPDATE_TIME");
Packit Service 384592
        if (var == NULL) {
Packit Service 384592
            var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
Packit Service 384592
            var->name = "LAST_UPDATE_TIME";
Packit Service 384592
            var->name_len = strlen(var->name);
Packit Service 384592
            apr_table_setn(col, var->name, (void *)var);
Packit Service 384592
        }
Packit Service 384592
        var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now())));
Packit Service 384592
        var->value_len = strlen(var->value);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* UPDATE_COUNTER */
Packit Service 384592
    {
Packit Service 384592
        msc_string *var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER");
Packit Service 384592
        int counter = 0;
Packit Service 384592
        if (var == NULL) {
Packit Service 384592
            var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
Packit Service 384592
            var->name = "UPDATE_COUNTER";
Packit Service 384592
            var->name_len = strlen(var->name);
Packit Service 384592
            apr_table_setn(col, var->name, (void *)var);
Packit Service 384592
        } else {
Packit Service 384592
            counter = atoi(var->value);
Packit Service 384592
        }
Packit Service 384592
        var->value = apr_psprintf(msr->mp, "%d", counter + 1);
Packit Service 384592
        var->value_len = strlen(var->value);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* ENH Make the expiration timestamp accessible in blob form so that
Packit Service 384592
     * it is easier/faster to determine expiration without having to
Packit Service 384592
     * convert back to table form
Packit Service 384592
     */
Packit Service 384592
Packit Service 384592
    rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
Packit Service 384592
        CREATEMODE, msr->mp);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
        msr_log(msr, 1, "collection_store: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
Packit Service 384592
            get_apr_error(msr->mp, rc));
Packit Service 384592
        dbm = NULL;
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
#ifndef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    /* Need to lock to pull in the stored data again and apply deltas. */
Packit Service 384592
    rc = apr_sdbm_lock(dbm, APR_FLOCK_EXCLUSIVE);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
        msr_log(msr, 1, "collection_store: Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
Packit Service 384592
            get_apr_error(msr->mp, rc));
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
    /* If there is an original value, then create a delta and
Packit Service 384592
     * apply the delta to the current value */
Packit Service 384592
    orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value);
Packit Service 384592
    if (orig_col != NULL) {
Packit Service 384592
        if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
            msr_log(msr, 9, "collection_store: Re-retrieving collection prior to store: %s",
Packit Service 384592
                apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value));
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        stored_col = (const apr_table_t *)collection_retrieve_ex(dbm, msr, var_name->value, var_key->value, var_key->value_len);
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* Merge deltas and calculate the size first. */
Packit Service 384592
    blob_size = 3 + 2;
Packit Service 384592
    arr = apr_table_elts(col);
Packit Service 384592
    te = (apr_table_entry_t *)arr->elts;
Packit Service 384592
    for (i = 0; i < arr->nelts; i++) {
Packit Service 384592
        msc_string *var = (msc_string *)te[i].val;
Packit Service 384592
        int len;
Packit Service 384592
Packit Service 384592
        /* If there is an original value, then apply the delta
Packit Service 384592
         * to the latest stored value */
Packit Service 384592
        if (stored_col != NULL) {
Packit Service 384592
            const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name);
Packit Service 384592
            if (orig_var != NULL) {
Packit Service 384592
                const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name);
Packit Service 384592
                if (stored_var != NULL) {
Packit Service 384592
                    int origval = atoi(orig_var->value);
Packit Service 384592
                    int ourval = atoi(var->value);
Packit Service 384592
                    int storedval = atoi(stored_var->value);
Packit Service 384592
                    int delta = ourval - origval;
Packit Service 384592
                    int newval = storedval + delta;
Packit Service 384592
Packit Service 384592
                    if (newval < 0) newval = 0; /* Counters never go below zero. */
Packit Service 384592
Packit Service 384592
                    var->value = apr_psprintf(msr->mp, "%d", newval);
Packit Service 384592
                    var->value_len = strlen(var->value);
Packit Service 384592
Packit Service 384592
                    if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
                        msr_log(msr, 9, "collection_store: Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]",
Packit Service 384592
                        log_escape_ex(msr->mp, var_name->value, var_name->value_len),
Packit Service 384592
                        log_escape_ex(msr->mp, var->name, var->name_len),
Packit Service 384592
                        origval, ourval, delta, storedval, delta, newval, var->value, var->value_len);
Packit Service 384592
                    }
Packit Service 384592
                }
Packit Service 384592
            }
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        // Allocate blob_size for keys
Packit Service 384592
        len = var->name_len + 1;
Packit Service 384592
        if (len >= 65536) len = 65536;
Packit Service 384592
        blob_size += len + 2;
Packit Service 384592
Packit Service 384592
        // Allocate blob_size for values
Packit Service 384592
        len = var->value_len + 1;
Packit Service 384592
        if (len >= 65536) len = 65536;
Packit Service 384592
        blob_size += len + 2;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* Now generate the binary object. */
Packit Service 384592
    blob = apr_pcalloc(msr->mp, blob_size);
Packit Service 384592
    if (blob == NULL) {
Packit Service 384592
        if (dbm != NULL) {
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
            apr_sdbm_close(dbm);
Packit Service 384592
            apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#else
Packit Service 384592
            apr_sdbm_unlock(dbm);
Packit Service 384592
            apr_sdbm_close(dbm);
Packit Service 384592
#endif
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    blob[0] = 0x49;
Packit Service 384592
    blob[1] = 0x52;
Packit Service 384592
    blob[2] = 0x01;
Packit Service 384592
Packit Service 384592
    blob_offset = 3;
Packit Service 384592
    arr = apr_table_elts(col);
Packit Service 384592
    te = (apr_table_entry_t *)arr->elts;
Packit Service 384592
    for (i = 0; i < arr->nelts; i++) {
Packit Service 384592
        msc_string *var = (msc_string *)te[i].val;
Packit Service 384592
        int len;
Packit Service 384592
Packit Service 384592
        len = var->name_len + 1;
Packit Service 384592
        if (len >= 65536) len = 65536;
Packit Service 384592
Packit Service 384592
        blob[blob_offset + 0] = (len & 0xff00) >> 8;
Packit Service 384592
        blob[blob_offset + 1] = len & 0x00ff;
Packit Service 384592
        memcpy(blob + blob_offset + 2, var->name, len - 1);
Packit Service 384592
        blob[blob_offset + 2 + len - 1] = '\0';
Packit Service 384592
        blob_offset += 2 + len;
Packit Service 384592
Packit Service 384592
        len = var->value_len + 1;
Packit Service 384592
        if (len >= 65536) len = 65536;
Packit Service 384592
Packit Service 384592
        blob[blob_offset + 0] = (len & 0xff00) >> 8;
Packit Service 384592
        blob[blob_offset + 1] = len & 0x00ff;
Packit Service 384592
        memcpy(blob + blob_offset + 2, var->value, len - 1);
Packit Service 384592
        blob[blob_offset + 2 + len - 1] = '\0';
Packit Service 384592
        blob_offset += 2 + len;
Packit Service 384592
Packit Service 384592
        if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
            msr_log(msr, 9, "collection_store: Wrote variable: name \"%s\", value \"%s\".",
Packit Service 384592
                log_escape_ex(msr->mp, var->name, var->name_len),
Packit Service 384592
                log_escape_ex(msr->mp, var->value, var->value_len));
Packit Service 384592
        }
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    blob[blob_offset] = 0;
Packit Service 384592
    blob[blob_offset + 1] = 0;
Packit Service 384592
Packit Service 384592
    /* And, finally, store it. */
Packit Service 384592
    key.dptr = var_key->value;
Packit Service 384592
    key.dsize = var_key->value_len + 1;
Packit Service 384592
Packit Service 384592
    value.dptr = (char *)blob;
Packit Service 384592
    value.dsize = blob_size;
Packit Service 384592
Packit Service 384592
    rc = apr_sdbm_store(dbm, key, value, APR_SDBM_REPLACE);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
        msr_log(msr, 1, "collection_store: Failed to write to DBM file \"%s\": %s", dbm_filename,
Packit Service 384592
                get_apr_error(msr->mp, rc));
Packit Service 384592
        if (dbm != NULL) {
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
            apr_sdbm_close(dbm);
Packit Service 384592
            apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#else
Packit Service 384592
            apr_sdbm_unlock(dbm);
Packit Service 384592
            apr_sdbm_close(dbm);
Packit Service 384592
#endif
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        return -1;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    apr_sdbm_close(dbm);
Packit Service 384592
    apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#else
Packit Service 384592
    apr_sdbm_unlock(dbm);
Packit Service 384592
    apr_sdbm_close(dbm);
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->debuglog_level >= 4) {
Packit Service 384592
        msr_log(msr, 4, "collection_store: Persisted collection (name \"%s\", key \"%s\").",
Packit Service 384592
            log_escape_ex(msr->mp, var_name->value, var_name->value_len),
Packit Service 384592
            log_escape_ex(msr->mp, var_key->value, var_key->value_len));
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return 0;
Packit Service 384592
Packit Service 384592
error:
Packit Service 384592
    return -1;
Packit Service 384592
}
Packit Service 384592
Packit Service 384592
/**
Packit Service 384592
 *
Packit Service 384592
 */
Packit Service 384592
int collections_remove_stale(modsec_rec *msr, const char *col_name) {
Packit Service 384592
    char *dbm_filename = NULL;
Packit Service 384592
    apr_sdbm_datum_t key, value;
Packit Service 384592
    apr_sdbm_t *dbm = NULL;
Packit Service 384592
    apr_status_t rc;
Packit Service 384592
    apr_array_header_t *keys_arr;
Packit Service 384592
    char **keys;
Packit Service 384592
    apr_time_t now = apr_time_sec(msr->request_time);
Packit Service 384592
    int i;
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->data_dir == NULL) {
Packit Service 384592
        /* The user has been warned about this problem enough times already by now.
Packit Service 384592
         * msr_log(msr, 1, "Unable to access collection file (name \"%s\"). Use SecDataDir to "
Packit Service 384592
         *     "define data directory first.", log_escape(msr->mp, col_name));
Packit Service 384592
         */
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    if(strstr(col_name,"USER") || strstr(col_name,"SESSION") || strstr(col_name, "RESOURCE"))
Packit Service 384592
        dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", msr->txcfg->webappid, "_", col_name, NULL);
Packit Service 384592
    else
Packit Service 384592
        dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
        msr_log(msr, 9, "collections_remove_stale: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name),
Packit Service 384592
                log_escape(msr->mp, dbm_filename));
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
        msr_log(msr, 1, "collections_remove_stale: Failed to lock proc mutex: %s",
Packit Service 384592
                get_apr_error(msr->mp, rc));
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
    rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
Packit Service 384592
            CREATEMODE, msr->mp);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
        msr_log(msr, 1, "collections_remove_stale: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
Packit Service 384592
                get_apr_error(msr->mp, rc));
Packit Service 384592
        dbm = NULL;
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* First get a list of all keys. */
Packit Service 384592
    keys_arr = apr_array_make(msr->mp, 256, sizeof(char *));
Packit Service 384592
Packit Service 384592
#ifndef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    rc = apr_sdbm_lock(dbm, APR_FLOCK_SHARED);
Packit Service 384592
    if (rc != APR_SUCCESS) {
Packit Service 384592
        msr_log(msr, 1, "collections_remove_stale: Failed to lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
Packit Service 384592
            get_apr_error(msr->mp, rc));
Packit Service 384592
        goto error;
Packit Service 384592
    }
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
    /* No one can write to the file while doing this so
Packit Service 384592
     * do it as fast as possible.
Packit Service 384592
     */
Packit Service 384592
    rc = apr_sdbm_firstkey(dbm, &key);
Packit Service 384592
    while(rc == APR_SUCCESS) {
Packit Service 384592
        if (key.dsize) {
Packit Service 384592
            char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize - 1);
Packit Service 384592
            *(char **)apr_array_push(keys_arr) = s;
Packit Service 384592
        }
Packit Service 384592
        rc = apr_sdbm_nextkey(dbm, &key);
Packit Service 384592
    }
Packit Service 384592
#ifndef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    apr_sdbm_unlock(dbm);
Packit Service 384592
#endif
Packit Service 384592
Packit Service 384592
    if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
        msr_log(msr, 9, "collections_remove_stale: Found %d record(s) in file \"%s\".", keys_arr->nelts,
Packit Service 384592
            log_escape(msr->mp, dbm_filename));
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    /* Now retrieve the entires one by one. */
Packit Service 384592
    keys = (char **)keys_arr->elts;
Packit Service 384592
    for (i = 0; i < keys_arr->nelts; i++) {
Packit Service 384592
        key.dptr = keys[i];
Packit Service 384592
        key.dsize = strlen(key.dptr) + 1;
Packit Service 384592
Packit Service 384592
        rc = apr_sdbm_fetch(dbm, &value, key);
Packit Service 384592
        if (rc != APR_SUCCESS) {
Packit Service 384592
            msr_log(msr, 1, "collections_remove_stale: Failed reading DBM file \"%s\": %s",
Packit Service 384592
                log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc));
Packit Service 384592
            goto error;
Packit Service 384592
        }
Packit Service 384592
Packit Service 384592
        if (value.dptr != NULL) {
Packit Service 384592
            apr_table_t *col = NULL;
Packit Service 384592
            msc_string *var = NULL;
Packit Service 384592
Packit Service 384592
            col = collection_unpack(msr, (const unsigned char *)value.dptr, value.dsize, 0);
Packit Service 384592
            if (col == NULL) {
Packit Service 384592
                goto error;
Packit Service 384592
            }
Packit Service 384592
Packit Service 384592
            var = (msc_string *)apr_table_get(col, "__expire_KEY");
Packit Service 384592
            if (var == NULL) {
Packit Service 384592
                msr_log(msr, 1, "collections_remove_stale: Collection cleanup discovered entry with no "
Packit Service 384592
                    "__expire_KEY (name \"%s\", key \"%s\").",
Packit Service 384592
                    log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1));
Packit Service 384592
            } else {
Packit Service 384592
                unsigned int expiry_time = atoi(var->value);
Packit Service 384592
Packit Service 384592
                if (msr->txcfg->debuglog_level >= 9) {
Packit Service 384592
                    msr_log(msr, 9, "collections_remove_stale: Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.",
Packit Service 384592
                        log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1),
Packit Service 384592
                        expiry_time - now);
Packit Service 384592
                }
Packit Service 384592
Packit Service 384592
                if (expiry_time <= now) {
Packit Service 384592
                    rc = apr_sdbm_delete(dbm, key);
Packit Service 384592
                    if (rc != APR_SUCCESS) {
Packit Service 384592
#ifdef LOG_NO_COLL_DELET_PB
Packit Service 384592
			if (msr->txcfg->debuglog_level >= 9)
Packit Service 384592
#endif
Packit Service 384592
			msr_log(msr, 1, "collections_remove_stale: Failed deleting collection (name \"%s\", "
Packit Service 384592
                            "key \"%s\"): %s", log_escape(msr->mp, col_name),
Packit Service 384592
                            log_escape_ex(msr->mp, key.dptr, key.dsize - 1), get_apr_error(msr->mp, rc));
Packit Service 384592
			msr->msc_sdbm_delete_error = 1;
Packit Service 384592
                        goto error;
Packit Service 384592
                    }
Packit Service 384592
Packit Service 384592
                    if (msr->txcfg->debuglog_level >= 4) {
Packit Service 384592
                        msr_log(msr, 4, "collections_remove_stale: Removed stale collection (name \"%s\", "
Packit Service 384592
                            "key \"%s\").", log_escape(msr->mp, col_name),
Packit Service 384592
                            log_escape_ex(msr->mp, key.dptr, key.dsize - 1));
Packit Service 384592
                    }
Packit Service 384592
                }
Packit Service 384592
            }
Packit Service 384592
        } else {
Packit Service 384592
            /* Ignore entry not found - it may have been removed in the meantime. */
Packit Service 384592
        }
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    apr_sdbm_close(dbm);
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
    apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
    return 1;
Packit Service 384592
Packit Service 384592
error:
Packit Service 384592
Packit Service 384592
    if (dbm) {
Packit Service 384592
        apr_sdbm_close(dbm);
Packit Service 384592
#ifdef GLOBAL_COLLECTION_LOCK
Packit Service 384592
        apr_global_mutex_unlock(msr->modsecurity->dbm_lock);
Packit Service 384592
#endif
Packit Service 384592
    }
Packit Service 384592
Packit Service 384592
    return -1;
Packit Service 384592
}