|
Packit |
284210 |
/*
|
|
Packit |
284210 |
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
|
Packit |
284210 |
* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
|
Packit |
284210 |
*
|
|
Packit |
284210 |
* You may not use this file except in compliance with
|
|
Packit |
284210 |
* the License. You may obtain a copy of the License at
|
|
Packit |
284210 |
*
|
|
Packit |
284210 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
284210 |
*
|
|
Packit |
284210 |
* If any of the files related to licensing are missing or if you have any
|
|
Packit |
284210 |
* other questions related to licensing please contact Trustwave Holdings, Inc.
|
|
Packit |
284210 |
* directly using the email address security@modsecurity.org.
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
|
|
Packit |
284210 |
#include "msc_json.h"
|
|
Packit |
284210 |
|
|
Packit |
284210 |
#ifdef WITH_YAJL
|
|
Packit |
284210 |
|
|
Packit |
284210 |
int json_add_argument(modsec_rec *msr, const char *value, unsigned length)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
msc_arg *arg = (msc_arg *) NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* If we do not have a prefix, we cannot create a variable name
|
|
Packit |
284210 |
* to reference this argument; for now we simply ignore these
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
if (!msr->json->current_key) {
|
|
Packit |
284210 |
msr_log(msr, 3, "Cannot add scalar value without an associated key");
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
arg = (msc_arg *) apr_pcalloc(msr->mp, sizeof(msc_arg));
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Argument name is 'prefix + current_key'
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
if (msr->json->prefix) {
|
|
Packit |
284210 |
arg->name = apr_psprintf(msr->mp, "%s.%s", msr->json->prefix,
|
|
Packit |
284210 |
msr->json->current_key);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
else {
|
|
Packit |
284210 |
arg->name = apr_psprintf(msr->mp, "%s", msr->json->current_key);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
arg->name_len = strlen(arg->name);
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Argument value is copied from the provided string
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
arg->value = apr_pstrmemdup(msr->mp, value, length);
|
|
Packit |
284210 |
arg->value_len = length;
|
|
Packit |
284210 |
arg->origin = "JSON";
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (msr->txcfg->debuglog_level >= 9) {
|
|
Packit |
284210 |
msr_log(msr, 9, "Adding JSON argument '%s' with value '%s'",
|
|
Packit |
284210 |
arg->name, arg->value);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
apr_table_addn(msr->arguments,
|
|
Packit |
284210 |
log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *) arg);
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* yajl callback functions
|
|
Packit |
284210 |
* For more information on the function signatures and order, check
|
|
Packit |
284210 |
* http://lloyd.github.com/yajl/yajl-1.0.12/structyajl__callbacks.html
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for hash key values; we use those to define the variable names
|
|
Packit |
284210 |
* under ARGS. Whenever we reach a new key, we update the current key value.
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_map_key(void *ctx, const unsigned char *key, size_t length)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
unsigned char *safe_key = (unsigned char *) NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* yajl does not provide us with null-terminated strings, but
|
|
Packit |
284210 |
* rather expects us to copy the data from the key up to the
|
|
Packit |
284210 |
* length informed; we create a standalone null-termined copy
|
|
Packit |
284210 |
* in safe_key
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
safe_key = apr_pstrndup(msr->mp, key, length);
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (msr->txcfg->debuglog_level >= 9) {
|
|
Packit |
284210 |
msr_log(msr, 9, "New JSON hash key '%s'", safe_key);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* TODO: How do we free the previously string value stored here?
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
msr->json->current_key = safe_key;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for null values
|
|
Packit |
284210 |
*
|
|
Packit |
284210 |
* TODO: Is there a way to define true null parameter values instead of
|
|
Packit |
284210 |
* empty values?
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_null(void *ctx)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return json_add_argument(msr, "", 0);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for boolean values
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_boolean(void *ctx, int value)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (value) {
|
|
Packit |
284210 |
return json_add_argument(msr, "true", strlen("true"));
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
else {
|
|
Packit |
284210 |
return json_add_argument(msr, "false", strlen("false"));
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for string values
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_string(void *ctx, const unsigned char *value, size_t length)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return json_add_argument(msr, value, length);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for numbers; YAJL can use separate callbacks for integers/longs and
|
|
Packit |
284210 |
* float/double values, but since we are not interested in using the numeric
|
|
Packit |
284210 |
* values here, we use a generic handler which uses numeric strings
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_number(void *ctx, const char *value, size_t length)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return json_add_argument(msr, value, length);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for a new hash, which indicates a new subtree, labeled as the current
|
|
Packit |
284210 |
* argument name, is being created
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_start_map(void *ctx)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* If we do not have a current_key, this is a top-level hash, so we do not
|
|
Packit |
284210 |
* need to do anything
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
if (!msr->json->current_key) return 1;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Check if we are already inside a hash context, and append or create the
|
|
Packit |
284210 |
* current key name accordingly
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
if (msr->json->prefix) {
|
|
Packit |
284210 |
msr->json->prefix = apr_psprintf(msr->mp, "%s.%s", msr->json->prefix,
|
|
Packit |
284210 |
msr->json->current_key);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
else {
|
|
Packit |
284210 |
msr->json->prefix = apr_pstrdup(msr->mp, msr->json->current_key);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (msr->txcfg->debuglog_level >= 9) {
|
|
Packit |
284210 |
msr_log(msr, 9, "New JSON hash context (prefix '%s')", msr->json->prefix);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Callback for end hash, meaning the current subtree is being closed, and that
|
|
Packit |
284210 |
* we should go back to the parent variable label
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static int yajl_end_map(void *ctx)
|
|
Packit |
284210 |
{
|
|
Packit |
284210 |
modsec_rec *msr = (modsec_rec *) ctx;
|
|
Packit |
284210 |
unsigned char *separator = (unsigned char *) NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* If we have no prefix, then this is the end of a top-level hash and
|
|
Packit |
284210 |
* we don't do anything
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
if (msr->json->prefix == NULL) return 1;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Current prefix might or not include a separator character; top-level
|
|
Packit |
284210 |
* hash keys do not have separators in the variable name
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
separator = strrchr(msr->json->prefix, '.');
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (separator) {
|
|
Packit |
284210 |
msr->json->prefix = apr_pstrmemdup(msr->mp, msr->json->prefix,
|
|
Packit |
284210 |
separator - msr->json->prefix);
|
|
Packit |
284210 |
msr->json->current_key = apr_psprintf(msr->mp, "%s", separator + 1);
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
else {
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* TODO: Check if it is safe to do this kind of pointer tricks
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
msr->json->current_key = msr->json->prefix;
|
|
Packit |
284210 |
msr->json->prefix = (unsigned char *) NULL;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Initialise JSON parser.
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
int json_init(modsec_rec *msr, char **error_msg) {
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* yajl configuration and callbacks
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
static yajl_callbacks callbacks = {
|
|
Packit |
284210 |
yajl_null,
|
|
Packit |
284210 |
yajl_boolean,
|
|
Packit |
284210 |
NULL /* yajl_integer */,
|
|
Packit |
284210 |
NULL /* yajl_double */,
|
|
Packit |
284210 |
yajl_number,
|
|
Packit |
284210 |
yajl_string,
|
|
Packit |
284210 |
yajl_start_map,
|
|
Packit |
284210 |
yajl_map_key,
|
|
Packit |
284210 |
yajl_end_map,
|
|
Packit |
284210 |
NULL /* yajl_start_array */,
|
|
Packit |
284210 |
NULL /* yajl_end_array */
|
|
Packit |
284210 |
};
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (error_msg == NULL) return -1;
|
|
Packit |
284210 |
*error_msg = NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
msr_log(msr, 4, "JSON parser initialization");
|
|
Packit |
284210 |
msr->json = apr_pcalloc(msr->mp, sizeof(json_data));
|
|
Packit |
284210 |
if (msr->json == NULL) return -1;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Prefix and current key are initially empty
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
msr->json->prefix = (unsigned char *) NULL;
|
|
Packit |
284210 |
msr->json->current_key = (unsigned char *) NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* yajl initialization
|
|
Packit |
284210 |
*
|
|
Packit |
284210 |
* yajl_parser_config definition:
|
|
Packit |
284210 |
* http://lloyd.github.io/yajl/yajl-2.0.1/yajl__parse_8h.html#aec816c5518264d2ac41c05469a0f986c
|
|
Packit |
284210 |
*
|
|
Packit |
284210 |
* TODO: make UTF8 validation optional, as it depends on Content-Encoding
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
if (msr->txcfg->debuglog_level >= 9) {
|
|
Packit |
284210 |
msr_log(msr, 9, "yajl JSON parsing callback initialization");
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
msr->json->handle = yajl_alloc(&callbacks, NULL, msr);
|
|
Packit |
284210 |
yajl_config(msr->json->handle, yajl_allow_partial_values, 0);
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Feed one chunk of data to the JSON parser.
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
int json_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char **error_msg) {
|
|
Packit |
284210 |
if (error_msg == NULL) return -1;
|
|
Packit |
284210 |
*error_msg = NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/* Feed our parser and catch any errors */
|
|
Packit |
284210 |
msr->json->status = yajl_parse(msr->json->handle, buf, size);
|
|
Packit |
284210 |
if (msr->json->status != yajl_status_ok) {
|
|
Packit |
284210 |
/* We need to free the yajl error message later, how to do this? */
|
|
Packit |
284210 |
*error_msg = yajl_get_error(msr->json->handle, 0, buf, size);
|
|
Packit |
284210 |
return -1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Finalise JSON parsing.
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
int json_complete(modsec_rec *msr, char **error_msg) {
|
|
Packit |
284210 |
char *json_data = (char *) NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
if (error_msg == NULL) return -1;
|
|
Packit |
284210 |
*error_msg = NULL;
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/* Wrap up the parsing process */
|
|
Packit |
284210 |
msr->json->status = yajl_complete_parse(msr->json->handle);
|
|
Packit |
284210 |
if (msr->json->status != yajl_status_ok) {
|
|
Packit |
284210 |
/* We need to free the yajl error message later, how to do this? */
|
|
Packit |
284210 |
*error_msg = yajl_get_error(msr->json->handle, 0, NULL, 0);
|
|
Packit |
284210 |
return -1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
/**
|
|
Packit |
284210 |
* Frees the resources used for JSON parsing.
|
|
Packit |
284210 |
*/
|
|
Packit |
284210 |
apr_status_t json_cleanup(modsec_rec *msr) {
|
|
Packit |
284210 |
msr_log(msr, 4, "JSON: Cleaning up JSON results");
|
|
Packit |
284210 |
if (msr->json->handle != NULL) {
|
|
Packit |
284210 |
yajl_free(msr->json->handle);
|
|
Packit |
284210 |
msr->json->handle = NULL;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
return 1;
|
|
Packit |
284210 |
}
|
|
Packit |
284210 |
|
|
Packit |
284210 |
#endif
|