mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
Feb 15th 2023 update
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
add_subdirectory(http_transaction_data)
|
||||
add_subdirectory(ip_utilities)
|
||||
add_subdirectory(pm)
|
||||
|
5
components/utils/http_transaction_data/CMakeLists.txt
Executable file
5
components/utils/http_transaction_data/CMakeLists.txt
Executable file
@@ -0,0 +1,5 @@
|
||||
add_definitions(-DUSERSPACE)
|
||||
|
||||
add_library(http_transaction_data http_transaction_data.cc)
|
||||
|
||||
add_subdirectory(http_transaction_data_ut)
|
260
components/utils/http_transaction_data/http_transaction_data.cc
Normal file
260
components/utils/http_transaction_data/http_transaction_data.cc
Normal file
@@ -0,0 +1,260 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "http_transaction_data.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "enum_array.h"
|
||||
#include "buffer.h"
|
||||
#include "nginx_attachment_common.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT);
|
||||
|
||||
enum class ETransactionData {
|
||||
HTTP_PROTO,
|
||||
METHOD,
|
||||
HOST_NAME,
|
||||
LISTENING_IP,
|
||||
LISTENING_PORT,
|
||||
URI,
|
||||
CLIENT_IP,
|
||||
CLIENT_PORT,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
const string HttpTransactionData::http_proto_ctx = "transaction_http_proto";
|
||||
const string HttpTransactionData::method_ctx = "transaction_method";
|
||||
const string HttpTransactionData::host_name_ctx = "transaction_host_name";
|
||||
const string HttpTransactionData::listening_ip_ctx = "transaction_listening_ip";
|
||||
const string HttpTransactionData::listening_port_ctx = "transaction_listening_port";
|
||||
const string HttpTransactionData::uri_ctx = "transaction_uri";
|
||||
const string HttpTransactionData::uri_path_decoded = "transaction_uri_path_decoded";
|
||||
const string HttpTransactionData::uri_query_decoded = "transaction_uri_query_decoded";
|
||||
const string HttpTransactionData::client_ip_ctx = "transaction_client_ip";
|
||||
const string HttpTransactionData::client_port_ctx = "transaction_client_port";
|
||||
const string HttpTransactionData::req_headers = "transaction_request_headers";
|
||||
const string HttpTransactionData::req_body = "transaction_request_body";
|
||||
const string HttpTransactionData::source_identifier = "sourceIdentifiers";
|
||||
const string HttpTransactionData::proxy_ip_ctx = "proxy_ip";
|
||||
|
||||
const CompressionType HttpTransactionData::default_response_content_encoding = CompressionType::NO_COMPRESSION;
|
||||
|
||||
Maybe<uint16_t>
|
||||
deserializeUintParam(const Buffer &data, uint &cur_pos)
|
||||
{
|
||||
// Int value is encoded in binary form
|
||||
auto value = data.getTypePtr<uint16_t>(cur_pos);
|
||||
|
||||
if (!value.ok()) {
|
||||
return genError("Failed to get Uint param " + value.getErr());
|
||||
}
|
||||
|
||||
cur_pos += sizeof(uint16_t);
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully parsed the number parameter. Value: " << *(value.unpack());
|
||||
|
||||
return *(value.unpack());
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
deserializeStrParam(const Buffer &data, uint &cur_pos)
|
||||
{
|
||||
//String is encoded by 16-bit uint representing length followed by const c-type string data bytes
|
||||
Maybe<uint16_t> str_size = deserializeUintParam(data, cur_pos);
|
||||
if (!str_size.ok()) return genError("Could Not parse string size value: " + str_size.getErr());
|
||||
|
||||
dbgTrace(D_NGINX_ATTACHMENT)
|
||||
<< "Deserializing string parameter. Current position: "
|
||||
<< cur_pos
|
||||
<< ", String size: "
|
||||
<< *str_size;
|
||||
|
||||
string res = "";
|
||||
if (*str_size > 0) {
|
||||
auto value = data.getPtr(cur_pos, *str_size);
|
||||
if (!value.ok()) {
|
||||
return genError("Failed to get String param " + value.getErr());
|
||||
}
|
||||
|
||||
const u_char *ptr = value.unpack();
|
||||
res = string(reinterpret_cast<const char *>(ptr), *str_size);
|
||||
}
|
||||
dbgTrace(D_NGINX_ATTACHMENT)
|
||||
<< "Successfully parsed string parameter. Result: "
|
||||
<< res
|
||||
<< ", Length: "
|
||||
<< to_string(*str_size);
|
||||
|
||||
cur_pos += *str_size;
|
||||
|
||||
return move(res);
|
||||
}
|
||||
|
||||
Maybe<IPAddr>
|
||||
deserializeIpAddrParam(const Buffer &data, uint &cur_pos)
|
||||
{
|
||||
Maybe<string> str_value = deserializeStrParam(data, cur_pos);
|
||||
if (!str_value.ok()) return str_value.passErr();
|
||||
|
||||
Maybe<IPAddr> ip = IPAddr::createIPAddr(str_value.unpackMove());
|
||||
if (!ip.ok()) return genError("Could not parse IP Address: " + ip.getErr());
|
||||
|
||||
return move(ip.unpackMove());
|
||||
}
|
||||
|
||||
Maybe<HttpTransactionData>
|
||||
HttpTransactionData::createTransactionData(const Buffer &transaction_raw_data)
|
||||
{
|
||||
// Deserialize TransactionData from binary blob sent from attachment
|
||||
uint cur_pos = 0;
|
||||
|
||||
dbgTrace(D_NGINX_ATTACHMENT)
|
||||
<< "Parsing buffer "
|
||||
<< dumpHex(transaction_raw_data)
|
||||
<< " of size "
|
||||
<< transaction_raw_data.size();
|
||||
|
||||
Maybe<string> http_protocol = deserializeStrParam(transaction_raw_data, cur_pos);
|
||||
if (!http_protocol.ok()) {
|
||||
return genError("Could not deserialize HTTP protocol: " + http_protocol.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized HTTP protocol: " << http_protocol.unpack();
|
||||
}
|
||||
|
||||
Maybe<string> http_method = deserializeStrParam(transaction_raw_data, cur_pos);
|
||||
if (!http_method.ok()) {
|
||||
return genError("Could not deserialize HTTP method: " + http_method.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized HTTP method: " << http_method.unpack();
|
||||
}
|
||||
|
||||
Maybe<string> host_name = deserializeStrParam(transaction_raw_data, cur_pos);
|
||||
if (!host_name.ok()) {
|
||||
return genError("Could not deserialize host name: " + host_name.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized host name: " << host_name.unpack();
|
||||
}
|
||||
|
||||
Maybe<IPAddr> listening_addr = deserializeIpAddrParam(transaction_raw_data, cur_pos);
|
||||
if (!listening_addr.ok()) {
|
||||
return genError("Could not deserialize listening address: " + listening_addr.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized listening address: " << listening_addr.unpack();
|
||||
}
|
||||
|
||||
Maybe<uint32_t> listening_port = deserializeUintParam(transaction_raw_data, cur_pos);
|
||||
if (!listening_port.ok()) {
|
||||
return genError("Could not deserialize listening port: " + listening_port.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized listening port: " << listening_port.unpack();
|
||||
}
|
||||
|
||||
Maybe<string> uri = deserializeStrParam(transaction_raw_data, cur_pos);
|
||||
if (!uri.ok()) {
|
||||
return genError("Could not deserialize URI: " + uri.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized URI: " << uri.unpack();
|
||||
}
|
||||
|
||||
Maybe<IPAddr> client_addr = deserializeIpAddrParam(transaction_raw_data, cur_pos);
|
||||
if (!client_addr.ok()) {
|
||||
return genError("Could not deserialize client address: " + client_addr.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized client address: " << client_addr.unpack();
|
||||
}
|
||||
|
||||
Maybe<uint32_t> client_port = deserializeUintParam(transaction_raw_data, cur_pos);
|
||||
if (!client_port.ok()) {
|
||||
return genError("Could not deserialize client port: " + client_port.getErr());
|
||||
} else {
|
||||
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized client port: " << client_port.unpack();
|
||||
}
|
||||
|
||||
// Fail if after parsing exact number of items, we didn't exactly consume whole buffer
|
||||
if (cur_pos != transaction_raw_data.size()) {
|
||||
dbgWarning(D_NGINX_ATTACHMENT) << "Nothing to deserialize, but raw data still remain";
|
||||
return genError("Finished deserialization and raw data still exist - Probably corrupted buffer.");
|
||||
}
|
||||
|
||||
HttpTransactionData transaction(
|
||||
http_protocol.unpackMove(),
|
||||
http_method.unpackMove(),
|
||||
host_name.unpackMove(),
|
||||
listening_addr.unpackMove(),
|
||||
listening_port.unpackMove(),
|
||||
uri.unpackMove(),
|
||||
client_addr.unpackMove(),
|
||||
client_port.unpackMove()
|
||||
);
|
||||
|
||||
return move(transaction);
|
||||
}
|
||||
|
||||
HttpTransactionData::HttpTransactionData (
|
||||
string _http_proto,
|
||||
string _method,
|
||||
string _host_name,
|
||||
IPAddr _listening_ip,
|
||||
uint16_t _listening_port,
|
||||
string _uri,
|
||||
IPAddr _client_ip,
|
||||
uint16_t _client_port
|
||||
)
|
||||
:
|
||||
http_proto(move(_http_proto)),
|
||||
method(move(_method)),
|
||||
host_name(move(_host_name)),
|
||||
listening_ip(move(_listening_ip)),
|
||||
listening_port(move(_listening_port)),
|
||||
uri(move(_uri)),
|
||||
client_ip(move(_client_ip)),
|
||||
client_port(move(_client_port)),
|
||||
is_request(true),
|
||||
response_content_encoding(default_response_content_encoding)
|
||||
{
|
||||
}
|
||||
|
||||
HttpTransactionData::HttpTransactionData()
|
||||
:
|
||||
HttpTransactionData::HttpTransactionData(
|
||||
"",
|
||||
"GET",
|
||||
"",
|
||||
IPAddr(),
|
||||
-1,
|
||||
"",
|
||||
IPAddr(),
|
||||
-1
|
||||
)
|
||||
{}
|
||||
|
||||
void
|
||||
HttpTransactionData::print(ostream &out_stream) const
|
||||
{
|
||||
out_stream << http_proto << " " << method << endl;
|
||||
out_stream << "From: " << client_ip << ":" << client_port << endl;
|
||||
out_stream << "To: "
|
||||
<< host_name
|
||||
<< uri
|
||||
<< " (listening on "
|
||||
<< listening_ip
|
||||
<< ":"
|
||||
<< listening_port
|
||||
<< ")"
|
||||
<< endl;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
include_directories(${CMAKE_SOURCE_DIR}/components/include)
|
||||
|
||||
add_unit_test(
|
||||
http_transaction_data_ut
|
||||
"http_transaction_data_ut.cc"
|
||||
"http_transaction_data;http_transaction_data;connkey;${RT_LIBRARY}"
|
||||
)
|
@@ -0,0 +1,127 @@
|
||||
#include "http_transaction_data.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "cptest.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
Buffer
|
||||
encodeInt16(uint16_t val)
|
||||
{
|
||||
vector<u_char> raw_data(reinterpret_cast<u_char*>(&val), reinterpret_cast<u_char*>(&val) + sizeof(uint16_t));
|
||||
return move(Buffer(raw_data));
|
||||
}
|
||||
|
||||
class HttpTransactionTest : public Test
|
||||
{
|
||||
public:
|
||||
Buffer
|
||||
createValidBuf()
|
||||
{
|
||||
Buffer protocol_length = Buffer(encodeInt16(strlen("HTTP/1.1")));
|
||||
|
||||
return
|
||||
protocol_length +
|
||||
Buffer("HTTP/1.1") +
|
||||
encodeInt16(3) +
|
||||
Buffer("GET") +
|
||||
encodeInt16(9) +
|
||||
Buffer("localhost") +
|
||||
encodeInt16(7) +
|
||||
Buffer("0.0.0.0") +
|
||||
encodeInt16(443) +
|
||||
encodeInt16(10) +
|
||||
Buffer("/user-app/") +
|
||||
encodeInt16(9) +
|
||||
Buffer("127.0.0.1") +
|
||||
encodeInt16(47423);
|
||||
}
|
||||
|
||||
Buffer
|
||||
createBadVerBuf()
|
||||
{
|
||||
Buffer protocol_length = Buffer(encodeInt16(strlen("HTTP/1.1")));
|
||||
|
||||
return
|
||||
protocol_length +
|
||||
Buffer("HTTP/1");
|
||||
}
|
||||
|
||||
Buffer
|
||||
createBadAddressBuf()
|
||||
{
|
||||
Buffer protocol_length = Buffer(encodeInt16(strlen("HTTP/1.1")));
|
||||
|
||||
return
|
||||
protocol_length +
|
||||
Buffer("HTTP/1.1") +
|
||||
encodeInt16(3) +
|
||||
Buffer("GET") +
|
||||
encodeInt16(9) +
|
||||
Buffer("localhost") +
|
||||
encodeInt16(14) +
|
||||
Buffer("this.is.not.IP") +
|
||||
encodeInt16(443) +
|
||||
encodeInt16(10) +
|
||||
Buffer("/user-app/") +
|
||||
encodeInt16(9) +
|
||||
Buffer("127.0.0.1") +
|
||||
encodeInt16(47423);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(HttpTransactionTest, TestEmptyTransactionData)
|
||||
{
|
||||
HttpTransactionData data;
|
||||
stringstream data_stream;
|
||||
data.print(data_stream);
|
||||
string data_string(
|
||||
" GET\nFrom: Uninitialized IP address:65535\nTo: (listening on Uninitialized IP address:65535)\n"
|
||||
);
|
||||
EXPECT_EQ(data_stream.str(), data_string);
|
||||
}
|
||||
|
||||
TEST_F(HttpTransactionTest, TestTransactionDataFromBuf)
|
||||
{
|
||||
HttpTransactionData data = HttpTransactionData::createTransactionData(createValidBuf()).unpack();
|
||||
stringstream data_stream;
|
||||
data.print(data_stream);
|
||||
string data_string(
|
||||
"HTTP/1.1 GET\nFrom: 127.0.0.1:47423\nTo: localhost/user-app/ (listening on 0.0.0.0:443)\n"
|
||||
);
|
||||
EXPECT_EQ(data_stream.str(), data_string);
|
||||
|
||||
EXPECT_EQ(data.getSourceIP(), IPAddr::createIPAddr("127.0.0.1").unpack());
|
||||
EXPECT_EQ(data.getSourcePort(), 47423);
|
||||
EXPECT_EQ(data.getListeningIP(), IPAddr::createIPAddr("0.0.0.0").unpack());
|
||||
EXPECT_EQ(data.getListeningPort(), 443);
|
||||
EXPECT_EQ(data.getDestinationHost(), "localhost");
|
||||
EXPECT_EQ(data.getHttpProtocol(), "HTTP/1.1");
|
||||
EXPECT_EQ(data.getURI(), "/user-app/");
|
||||
EXPECT_EQ(data.getHttpMethod(), "GET");
|
||||
}
|
||||
|
||||
TEST_F(HttpTransactionTest, TestTransactionDataBadVer)
|
||||
{
|
||||
auto data = HttpTransactionData::createTransactionData(createBadVerBuf());
|
||||
ASSERT_FALSE(data.ok());
|
||||
EXPECT_EQ(
|
||||
data.getErr(),
|
||||
"Could not deserialize HTTP protocol: "
|
||||
"Failed to get String param Cannot get internal pointer beyond the buffer limits"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(HttpTransactionTest, TestTransactionDataBadAddress)
|
||||
{
|
||||
auto data = HttpTransactionData::createTransactionData(createBadAddressBuf());
|
||||
ASSERT_FALSE(data.ok());
|
||||
EXPECT_EQ(
|
||||
data.getErr(),
|
||||
"Could not deserialize listening address: "
|
||||
"Could not parse IP Address: String 'this.is.not.IP' is not a valid IPv4/IPv6 address"
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user