2018-03-26 17:01:53 -03:00

658 lines
21 KiB
C++

/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
*
* You may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* If any of the files related to licensing are missing or if you have any
* other questions related to licensing please contact Trustwave Holdings, Inc.
* directly using the email address security@modsecurity.org.
*
*/
#ifdef __cplusplus
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <memory>
#endif
#include <stdlib.h>
#include <stddef.h>
#ifndef HEADERS_MODSECURITY_TRANSACTION_H_
#define HEADERS_MODSECURITY_TRANSACTION_H_
#ifndef __cplusplus
typedef struct ModSecurity_t ModSecurity;
typedef struct Transaction_t Transaction;
typedef struct Rules_t Rules;
#endif
#include "anchored_set_variable.h"
#include "anchored_variable.h"
#include "modsecurity/intervention.h"
#include "modsecurity/collection/collections.h"
#include "modsecurity/variable_value.h"
#include "modsecurity/collection/collection.h"
#include "modsecurity/variable_origin.h"
#define LOGFY_ADD(a, b) \
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(a), strlen(a)); \
if (b == NULL) { \
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(""), \
strlen("")); \
} else { \
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(b), \
strlen(b)); \
}
#define LOGFY_ADD_INT(a, b) \
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(a), strlen(a)); \
yajl_gen_number(g, reinterpret_cast<const char*>(b), strlen(b));
#define LOGFY_ADD_NUM(a, b) \
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(a), strlen(a)); \
yajl_gen_integer(g, b);
#ifdef __cplusplus
namespace modsecurity {
class ModSecurity;
class Transaction;
class Rules;
class RuleMessage;
namespace actions {
class Action;
namespace disruptive {
enum AllowType : int;
}
}
namespace RequestBodyProcessor {
class XML;
class JSON;
}
namespace operators {
class Operator;
}
class TransactionAnchoredVariables {
public:
explicit TransactionAnchoredVariables(Transaction *t)
: m_variableArgsNames(t, "ARGS_NAMES"),
m_variableArgsGetNames(t, "ARGS_GET_NAMES"),
m_variableArgsPostNames(t, "ARGS_POST_NAMES"),
m_variableRequestHeadersNames(t, "REQUEST_HEADERS_NAMES"),
m_variableResponseContentType(t, "RESPONSE_CONTENT_TYPE"),
m_variableResponseHeadersNames(t, "RESPONSE_HEADERS_NAMES"),
m_variableARGScombinedSize(t, "ARGS_COMBINED_SIZE"),
m_variableAuthType(t, "AUTH_TYPE"),
m_variableFilesCombinedSize(t, "FILES_COMBINED_SIZE"),
m_variableFullRequest(t, "FULL_REQUEST"),
m_variableFullRequestLength(t, "FULL_REQUEST_LENGTH"),
m_variableInboundDataError(t, "INBOUND_DATA_ERROR"),
m_variableMatchedVar(t, "MATCHED_VAR"),
m_variableMatchedVarName(t, "MATCHED_VAR_NAME"),
m_variableMultipartBoundaryQuoted(t, "MULTIPART_BOUNDARY_QUOTED"),
m_variableMultipartBoundaryWhiteSpace(t,
"MULTIPART_BOUNDARY_WHITESPACE"),
m_variableMultipartCrlfLFLines(t, "MULTIPART_CRLF_LF_LINES"),
m_variableMultipartDataAfter(t, "MULTIPART_DATA_AFTER"),
m_variableMultipartDataBefore(t, "MULTIPART_DATA_BEFORE"),
m_variableMultipartFileLimitExceeded(t,
"MULTIPART_FILE_LIMIT_EXCEEDED"),
m_variableMultipartHeaderFolding(t, "MULTIPART_HEADER_FOLDING"),
m_variableMultipartInvalidHeaderFolding(t,
"MULTIPART_INVALID_HEADER_FOLDING"),
m_variableMultipartInvalidPart(t, "MULTIPART_INVALID_PART"),
m_variableMultipartInvalidQuoting(t, "MULTIPART_INVALID_QUOTING"),
m_variableMultipartLFLine(t, "MULTIPART_LF_LINE"),
m_variableMultipartMissingSemicolon(t, "MULTIPART_MISSING_SEMICOLON"),
m_variableMultipartStrictError(t, "MULTIPART_STRICT_ERROR"),
m_variableMultipartUnmatchedBoundary(t,
"MULTIPART_UNMATCHED_BOUNDARY"),
m_variableOutboundDataError(t, "OUTBOUND_DATA_ERROR"),
m_variablePathInfo(t, "PATH_INFO"),
m_variableQueryString(t, "QUERY_STRING"),
m_variableRemoteAddr(t, "REMOTE_ADDR"),
m_variableRemoteHost(t, "REMOTE_HOST"),
m_variableRemotePort(t, "REMOTE_PORT"),
m_variableReqbodyError(t, "REQBODY_ERROR"),
m_variableReqbodyErrorMsg(t, "REQBODY_ERROR_MSG"),
m_variableReqbodyProcessorError(t, "REQBODY_PROCESSOR_ERROR"),
m_variableReqbodyProcessorErrorMsg(t, "REQBODY_PROCESSOR_ERROR_MSG"),
m_variableReqbodyProcessor(t, "REQBODY_PROCESSOR"),
m_variableRequestBasename(t, "REQUEST_BASENAME"),
m_variableRequestBody(t, "REQUEST_BODY"),
m_variableRequestBodyLength(t, "REQUEST_BODY_LENGTH"),
m_variableRequestFilename(t, "REQUEST_FILENAME"),
m_variableRequestLine(t, "REQUEST_LINE"),
m_variableRequestMethod(t, "REQUEST_METHOD"),
m_variableRequestProtocol(t, "REQUEST_PROTOCOL"),
m_variableRequestURI(t, "REQUEST_URI"),
m_variableRequestURIRaw(t, "REQUEST_URI_RAW"),
m_variableResource(t, "RESOURCE"),
m_variableResponseBody(t, "RESPONSE_BODY"),
m_variableResponseContentLength(t, "RESPONSE_CONTENT_LENGTH"),
m_variableResponseProtocol(t, "RESPONSE_PROTOCOL"),
m_variableResponseStatus(t, "RESPONSE_STATUS"),
m_variableServerAddr(t, "SERVER_ADDR"),
m_variableServerName(t, "SERVER_NAME"),
m_variableServerPort(t, "SERVER_PORT"),
m_variableSessionID(t, "SESSIONID"),
m_variableUniqueID(t, "UNIQUE_ID"),
m_variableUrlEncodedError(t, "URLENCODED_ERROR"),
m_variableUserID(t, "USERID"),
m_variableArgs(t, "ARGS"),
m_variableArgsGet(t, "ARGS_GET"),
m_variableArgsPost(t, "ARGS_POST"),
m_variableFilesSizes(t, "FILES_SIZES"),
m_variableFilesNames(t, "FILES_NAMES"),
m_variableFilesTmpContent(t, "FILES_TMP_CONTENT"),
m_variableMultipartFileName(t, "MULTIPART_FILENAME"),
m_variableMultipartName(t, "MULTIPART_NAME"),
m_variableMatchedVarsNames(t, "MATCHED_VARS_NAMES"),
m_variableMatchedVars(t, "MATCHED_VARS"),
m_variableFiles(t, "FILES"),
m_variableRequestCookies(t, "REQUEST_COOKIES"),
m_variableRequestHeaders(t, "REQUEST_HEADERS"),
m_variableResponseHeaders(t, "RESPONSE_HEADERS"),
m_variableGeo(t, "GEO"),
m_variableRequestCookiesNames(t, "REQUEST_COOKIES_NAMES"),
m_variableRule(t, "RULE"),
m_variableFilesTmpNames(t, "FILES_TMPNAMES"),
m_variableOffset(0)
{ }
AnchoredSetVariable m_variableArgsNames;
AnchoredSetVariable m_variableArgsGetNames;
AnchoredSetVariable m_variableArgsPostNames;
AnchoredSetVariable m_variableRequestHeadersNames;
AnchoredVariable m_variableResponseContentType;
AnchoredSetVariable m_variableResponseHeadersNames;
AnchoredVariable m_variableARGScombinedSize;
AnchoredVariable m_variableAuthType;
AnchoredVariable m_variableFilesCombinedSize;
AnchoredVariable m_variableFullRequest;
AnchoredVariable m_variableFullRequestLength;
AnchoredVariable m_variableInboundDataError;
AnchoredVariable m_variableMatchedVar;
AnchoredVariable m_variableMatchedVarName;
AnchoredVariable m_variableMultipartBoundaryQuoted;
AnchoredVariable m_variableMultipartBoundaryWhiteSpace;
AnchoredVariable m_variableMultipartCrlfLFLines;
AnchoredVariable m_variableMultipartDataAfter;
AnchoredVariable m_variableMultipartDataBefore;
AnchoredVariable m_variableMultipartFileLimitExceeded;
AnchoredVariable m_variableMultipartHeaderFolding;
AnchoredVariable m_variableMultipartInvalidHeaderFolding;
AnchoredVariable m_variableMultipartInvalidPart;
AnchoredVariable m_variableMultipartInvalidQuoting;
AnchoredVariable m_variableMultipartLFLine;
AnchoredVariable m_variableMultipartMissingSemicolon;
AnchoredVariable m_variableMultipartStrictError;
AnchoredVariable m_variableMultipartUnmatchedBoundary;
AnchoredVariable m_variableOutboundDataError;
AnchoredVariable m_variablePathInfo;
AnchoredVariable m_variableQueryString;
AnchoredVariable m_variableRemoteAddr;
AnchoredVariable m_variableRemoteHost;
AnchoredVariable m_variableRemotePort;
AnchoredVariable m_variableReqbodyError;
AnchoredVariable m_variableReqbodyErrorMsg;
AnchoredVariable m_variableReqbodyProcessorError;
AnchoredVariable m_variableReqbodyProcessorErrorMsg;
AnchoredVariable m_variableReqbodyProcessor;
AnchoredVariable m_variableRequestBasename;
AnchoredVariable m_variableRequestBody;
AnchoredVariable m_variableRequestBodyLength;
AnchoredVariable m_variableRequestFilename;
AnchoredVariable m_variableRequestLine;
AnchoredVariable m_variableRequestMethod;
AnchoredVariable m_variableRequestProtocol;
AnchoredVariable m_variableRequestURI;
AnchoredVariable m_variableRequestURIRaw;
AnchoredVariable m_variableResource;
AnchoredVariable m_variableResponseBody;
AnchoredVariable m_variableResponseContentLength;
AnchoredVariable m_variableResponseProtocol;
AnchoredVariable m_variableResponseStatus;
AnchoredVariable m_variableServerAddr;
AnchoredVariable m_variableServerName;
AnchoredVariable m_variableServerPort;
AnchoredVariable m_variableSessionID;
AnchoredVariable m_variableUniqueID;
AnchoredVariable m_variableUrlEncodedError;
AnchoredVariable m_variableUserID;
AnchoredSetVariable m_variableArgs;
AnchoredSetVariable m_variableArgsGet;
AnchoredSetVariable m_variableArgsPost;
AnchoredSetVariable m_variableFilesSizes;
AnchoredSetVariable m_variableFilesNames;
AnchoredSetVariable m_variableFilesTmpContent;
AnchoredSetVariable m_variableMultipartFileName;
AnchoredSetVariable m_variableMultipartName;
AnchoredSetVariable m_variableMatchedVarsNames;
AnchoredSetVariable m_variableMatchedVars;
AnchoredSetVariable m_variableFiles;
AnchoredSetVariable m_variableRequestCookies;
AnchoredSetVariable m_variableRequestHeaders;
AnchoredSetVariable m_variableResponseHeaders;
AnchoredSetVariable m_variableGeo;
AnchoredSetVariable m_variableRequestCookiesNames;
AnchoredSetVariable m_variableRule;
AnchoredSetVariable m_variableFilesTmpNames;
int m_variableOffset;
};
/** @ingroup ModSecurity_CPP_API */
class Transaction : public TransactionAnchoredVariables {
public:
Transaction(ModSecurity *transaction, Rules *rules, void *logCbData);
~Transaction();
/** TODO: Should be an structure that fits an IP address */
int processConnection(const char *client, int cPort,
const char *server, int sPort);
int processURI(const char *uri, const char *protocol,
const char *http_version);
/**
* Types of request body that ModSecurity may give a special treatment
* for the data.
*/
enum RequestBodyType {
/**
*
*/
UnknownFormat,
/**
*
*/
MultiPartRequestBody,
/**
*
*/
WWWFormUrlEncoded,
/**
*
*/
JSONRequestBody,
/**
*
*/
XMLRequestBody
};
int processRequestHeaders();
int addRequestHeader(const std::string& key, const std::string& value);
int addRequestHeader(const unsigned char *key, const unsigned char *value);
int addRequestHeader(const unsigned char *key, size_t len_key,
const unsigned char *value, size_t len_value);
int processRequestBody();
int appendRequestBody(const unsigned char *body, size_t size);
int requestBodyFromFile(const char *path);
int processResponseHeaders(int code, const std::string& proto);
int addResponseHeader(const std::string& key, const std::string& value);
int addResponseHeader(const unsigned char *key, const unsigned char *value);
int addResponseHeader(const unsigned char *key, size_t len_key,
const unsigned char *value, size_t len_value);
int processResponseBody();
int appendResponseBody(const unsigned char *body, size_t size);
int processLogging();
int updateStatusCode(int status);
bool intervention(ModSecurityIntervention *it);
bool addArgument(const std::string& orig, const std::string& key,
const std::string& value, size_t offset);
bool extractArguments(const std::string &orig, const std::string& buf,
size_t offset);
const char *getResponseBody();
size_t getResponseBodyLength();
size_t getRequestBodyLength();
#ifndef NO_LOGS
void debug(int, std::string) const;
#endif
void serverLog(std::shared_ptr<RuleMessage> rm);
int getRuleEngineState();
std::string toJSON(int parts);
std::string toOldAuditLogFormat(int parts, const std::string &trailer);
std::string toOldAuditLogFormatIndex(const std::string &filename,
double size, const std::string &md5);
/**
* Filled during the class instantiation, this variable can be later
* used to fill the SecRule variable `duration'. The variable `duration'
* is dynamic calculated, it is always relative to the value found in
* m_creationTimeStamp.
*
* @note There is space for performance improvement. This value don't
* need to be filled if there is no rule using the variable
* `duration'.
*/
clock_t m_creationTimeStamp;
/**
* Holds the client IP address.
*/
std::string m_clientIpAddress;
/**
* Holds the HTTP version: 1.2, 2.0, 3.0 and so on....
*/
std::string m_httpVersion;
/**
* Holds the server IP Address
*/
std::string m_serverIpAddress;
/**
* Holds the raw URI that was requested.
*/
std::string m_uri;
/**
* Holds the URI that was requests (without the query string).
*/
std::string m_uri_no_query_string_decoded;
/**
* Holds the combined size of all arguments, later used to fill the
* variable ARGS_COMBINED_SIZE.
*/
double m_ARGScombinedSizeDouble;
/**
* Client tcp port.
*/
int m_clientPort;
/**
* This variable is set by the action `severity' and later can be
* consulted via the SecLanguage variable HIGHEST_SEVERITY.
*/
int m_highestSeverityAction;
/**
* Holds the HTTP return code when it is known. If 0 nothing was
* set.
*/
int m_httpCodeReturned;
/**
* Holds the server port.
*/
int m_serverPort;
/**
* ModSecurity instance used to start this transaction. Basically used
* to fill the server log whenever is needed.
*/
ModSecurity *m_ms;
/**
* Holds the type of the request body, in case there is one.
*/
RequestBodyType m_requestBodyType;
/**
* Holds the request body "processor"
*/
RequestBodyType m_requestBodyProcessor;
/**
* Rules object utilized during this specific transaction.
*/
Rules *m_rules;
/**
*
*/
std::list<int > m_ruleRemoveById;
/**
*
*/
std::list<std::string> m_ruleRemoveByTag;
/**
*
*/
std::list< std::pair<std::string, std::string> > m_ruleRemoveTargetByTag;
/**
*
*/
std::list< std::pair<int, std::string> > m_ruleRemoveTargetById;
/**
*
*/
int m_requestBodyAccess;
/**
* The list m_auditLogModifier contains modifications to the `auditlogs'
* for this specific request, those modifications can happens via the
* utilization of the action: `ctl:auditLogParts='
*
*/
std::list< std::pair<int, std::string> > m_auditLogModifier;
/**
* This variable holds all the messages asked to be save by the utilization
* of the actions: `log_data' and `msg'. These should be included on the
* auditlogs.
*/
std::list<modsecurity::RuleMessage> m_rulesMessages;
/**
* Holds the request body, in case of any.
*/
std::ostringstream m_requestBody;
/**
* Holds the response body, in case of any.
*/
std::ostringstream m_responseBody;
/**
* Contains the unique ID of the transaction. Use by the variable
* `UNIQUE_ID'. This unique id is also saved as part of the AuditLog.
*/
std::string m_id;
/**
* Holds the SecMarker name that this transaction should wait to perform
* rules evaluation again.
*/
std::string m_marker;
/**
* Holds the amount of rules that should be skipped. If bigger than 0 the
* current rule should be skipped and the number needs to be decreased.
*/
int m_skip_next;
/**
* If allow action was utilized, this variable holds the allow type.
*/
modsecurity::actions::disruptive::AllowType m_allowType;
/**
* Holds the decode URI. Notice that m_uri holds the raw version
* of the URI.
*/
std::string m_uri_decoded;
/**
* Actions (disruptive?) that should be taken by the connector related to
* that transaction.
*/
std::vector<ModSecurityIntervention> m_actions;
ModSecurityIntervention m_it;
/**
* Holds the creation time stamp, using std::time.
*
* TODO: m_timeStamp and m_creationTimeStamp may be merged into a single
* variable.
*/
time_t m_timeStamp;
/**
* Holds all the collections related to that transaction.
*/
collection::Collections m_collections;
/**
* Holds the whatever matched in the operation utilization.
* That variable will be further used by the capture action.
*
*/
std::list<std::string> m_matched;
RequestBodyProcessor::XML *m_xml;
RequestBodyProcessor::JSON *m_json;
int m_secRuleEngine;
std::string m_variableDuration;
std::map<std::string, std::string> m_variableEnvs;
std::string m_variableHighestSeverityAction;
std::string m_variableRemoteUser;
std::string m_variableTime;
std::string m_variableTimeDay;
std::string m_variableTimeEpoch;
std::string m_variableTimeHour;
std::string m_variableTimeMin;
std::string m_variableTimeSec;
std::string m_variableTimeWDay;
std::string m_variableTimeYear;
private:
/**
* Pointer to the callback function that will be called to fill
* the web server (connector) log.
*/
void *m_logCbData;
};
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** @ingroup ModSecurity_C_API */
Transaction *msc_new_transaction(ModSecurity *ms,
Rules *rules, void *logCbData);
/** @ingroup ModSecurity_C_API */
int msc_process_connection(Transaction *transaction,
const char *client, int cPort, const char *server, int sPort);
/** @ingroup ModSecurity_C_API */
int msc_process_request_headers(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
int msc_add_request_header(Transaction *transaction, const unsigned char *key,
const unsigned char *value);
/** @ingroup ModSecurity_C_API */
int msc_add_n_request_header(Transaction *transaction,
const unsigned char *key, size_t len_key, const unsigned char *value,
size_t len_value);
/** @ingroup ModSecurity_C_API */
int msc_process_request_body(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
int msc_append_request_body(Transaction *transaction,
const unsigned char *body, size_t size);
/** @ingroup ModSecurity_C_API */
int msc_request_body_from_file(Transaction *transaction, const char *path);
/** @ingroup ModSecurity_C_API */
int msc_process_response_headers(Transaction *transaction, int code,
const char* protocol);
/** @ingroup ModSecurity_C_API */
int msc_add_response_header(Transaction *transaction,
const unsigned char *key, const unsigned char *value);
/** @ingroup ModSecurity_C_API */
int msc_add_n_response_header(Transaction *transaction,
const unsigned char *key, size_t len_key, const unsigned char *value,
size_t len_value);
/** @ingroup ModSecurity_C_API */
int msc_process_response_body(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
int msc_append_response_body(Transaction *transaction,
const unsigned char *body, size_t size);
/** @ingroup ModSecurity_C_API */
int msc_process_uri(Transaction *transaction, const char *uri,
const char *protocol, const char *http_version);
/** @ingroup ModSecurity_C_API */
const char *msc_get_response_body(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
size_t msc_get_response_body_length(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
size_t msc_get_request_body_length(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
void msc_transaction_cleanup(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
int msc_intervention(Transaction *transaction, ModSecurityIntervention *it);
/** @ingroup ModSecurity_C_API */
int msc_process_logging(Transaction *transaction);
/** @ingroup ModSecurity_C_API */
int msc_update_status_code(Transaction *transaction, int status);
#ifdef __cplusplus
}
} // namespace modsecurity
#endif
#endif // HEADERS_MODSECURITY_TRANSACTION_H_