Bug fixes and socket improvements

Added the ability to disable certificate verification for SSL sockets which is useful for testing.

Added the ability to specify maximum HTTP request header/body sizes in the CMake build config to prevent a malicious client from causing OOM errors.

Fixed a bug in HttpResponse and HttpRequest parse causing it to abort if the request is invalid in some cases.
This commit is contained in:
Fred Nicolson 2017-09-25 12:29:52 +01:00
parent 5c2b6c3a8d
commit abb3655739
11 changed files with 2556 additions and 27 deletions

View File

@ -8,11 +8,23 @@ set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake_mo
#User options
option(USE_SSL "Use SSL" OFF)
set(FRNETLIB_BUILD_SHARED_LIBS false CACHE BOOL "Build shared library.")
set(MAX_HTTP_HEADER_SIZE "0xC800" CACHE STRING "The maximum allowed HTTP header size in bytes")
set(MAX_HTTP_BODY_SIZE "0xA00000" CACHE STRING "The maximum allowed HTTP body size in bytes")
#Enable tests and examples by default
option(BUILD_EXAMPLES "Build frnetlib examples" ON)
option(BUILD_TESTS "Build frnetlib tests" ON)
#Configure defines based on user options
add_definitions(-DMAX_HTTP_HEADER_SIZE=${MAX_HTTP_HEADER_SIZE})
add_definitions(-DMAX_HTTP_BODY_SIZE=${MAX_HTTP_BODY_SIZE})
if(USE_SSL)
FIND_PACKAGE(MBEDTLS)
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
add_definitions(-DSSL_ENABLED)
endif()
add_definitions(-DNOMINMAX)
add_definitions(-Dhtonf)
add_definitions(-Dhtonll)
@ -20,12 +32,6 @@ add_definitions(-Dntohll)
add_definitions(-Dhtond)
add_definitions(-Dntodh)
if(USE_SSL)
FIND_PACKAGE(MBEDTLS)
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
add_definitions(-DSSL_ENABLED)
endif()
set( INCLUDE_PATH "${PROJECT_SOURCE_DIR}/include" )
set( SOURCE_PATH "${PROJECT_SOURCE_DIR}/src" )

2481
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,9 @@ namespace fr
};
enum RequestStatus
{
ParseError = 0,
HttpHeaderTooBig = 1,
HttpBodyTooBig = 2,
Continue = 100,
SwitchingProtocols = 101,
Ok = 200,

View File

@ -20,6 +20,7 @@
#include <windows.h>
#include <ws2tcpip.h>
#define SOL_TCP SOL_SOCKET
#define SHUT_RDWR SD_BOTH
#else
#define closesocket(x) close(x)
#define INVALID_SOCKET 0

View File

@ -24,7 +24,7 @@ namespace fr
class SSLListener : public Listener
{
public:
explicit SSLListener(std::shared_ptr<SSLContext> ssl_context, const std::string &crt_path, const std::string &pem_path, const std::string &private_key_path) noexcept;
explicit SSLListener(std::shared_ptr<SSLContext> ssl_context, const std::string &pem_path, const std::string &private_key_path) noexcept;
virtual ~SSLListener() noexcept;
SSLListener(SSLListener &&o) noexcept = default;

View File

@ -107,6 +107,16 @@ namespace fr
return ssl_socket_descriptor->fd > -1;
}
/*!
* Sets if the socket should verify the endpoints
* certificates or not. Verification is enforced
* by default, but disabling it could be useful
* for testing.
*
* @param should_verify True if certificates should be verified, false otherwise
*/
void verify_certificates(bool should_verify);
private:
std::shared_ptr<SSLContext> ssl_context;
@ -114,6 +124,7 @@ namespace fr
std::unique_ptr<mbedtls_ssl_context> ssl;
mbedtls_ssl_config conf;
uint32_t flags;
bool should_verify;
};
}

View File

@ -3,11 +3,11 @@
//
#include <algorithm>
#include <iostream>
#include "frnetlib/HttpRequest.h"
namespace fr
{
HttpRequest::HttpRequest()
HttpRequest::HttpRequest()
: header_ended(false),
last_parsed_character(0),
content_length(0)
@ -22,6 +22,13 @@ namespace fr
//Ensure that the whole header has been parsed first
if(!header_ended)
{
//Ensure that the header doesn't exceed max length
if(body.size() > MAX_HTTP_HEADER_SIZE)
{
status = HttpHeaderTooBig;
return false; //End parse
}
//Check to see if this request data contains the end of the header
uint16_t header_end_size = 4;
auto header_end = body.find("\r\n\r\n");
@ -38,11 +45,17 @@ namespace fr
//Else parse it
if(!parse_header(header_end))
return false;
body.clear();
return false;
//Leave things after the header intact
body.erase(0, header_end + header_end_size);
}
body += std::string(request + header_end + header_end_size, requestsz - header_end - header_end_size);
//Ensure that body doesn't exceed maximum length
if(body.size() > MAX_HTTP_BODY_SIZE)
{
status = HttpBodyTooBig;
return false; //End parse
}
//If we've got the whole request, parse the POST if it exists

View File

@ -14,6 +14,13 @@ namespace fr
//Ensure that the whole header has been parsed first
if(!header_ended)
{
//Ensure that the header doesn't exceed max length
if(body.size() > MAX_HTTP_HEADER_SIZE)
{
status = HttpHeaderTooBig;
return false; //End parse
}
//Check to see if this request data contains the end of the header
uint16_t header_end_size = 4;
auto header_end = body.find("\r\n\r\n");
@ -30,12 +37,20 @@ namespace fr
//Else parse it
parse_header(header_end);
body.clear();
body.clear();
body += std::string(response_data + header_end + header_end_size, datasz - header_end - header_end_size);
//Leave things after the header intact
body.erase(0, header_end + header_end_size);
}
//Ensure that body doesn't exceed maximum length
if(body.size() > MAX_HTTP_BODY_SIZE)
{
status = HttpBodyTooBig;
return false; //End parse
}
//Cut off any data if it exceeds content length
if(body.size() > content_length)
body.resize(content_length);
return body.size() < content_length;

View File

@ -13,7 +13,7 @@
namespace fr
{
SSLListener::SSLListener(std::shared_ptr<SSLContext> ssl_context_, const std::string &crt_path, const std::string &pem_path, const std::string &private_key_path) noexcept
SSLListener::SSLListener(std::shared_ptr<SSLContext> ssl_context_, const std::string &pem_path, const std::string &private_key_path) noexcept
: ssl_context(std::move(ssl_context_))
{
//Initialise SSL objects required
@ -25,13 +25,6 @@ namespace fr
int error = 0;
//Load certificates and private key
error = mbedtls_x509_crt_parse_file(&srvcert, crt_path.c_str());
if(error != 0)
{
std::cout << "Failed to initialise SSL listener. CRT Parse returned: " << error << std::endl;
return;
}
error = mbedtls_x509_crt_parse_file(&srvcert, pem_path.c_str());
if(error != 0)
{

View File

@ -13,7 +13,8 @@
namespace fr
{
SSLSocket::SSLSocket(std::shared_ptr<SSLContext> ssl_context_) noexcept
: ssl_context(std::move(ssl_context_))
: ssl_context(std::move(ssl_context_)),
should_verify(true)
{
//Initialise mbedtls structures
mbedtls_ssl_config_init(&conf);
@ -104,7 +105,7 @@ namespace fr
return Socket::Status::Error;
}
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_authmode(&conf, should_verify ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
mbedtls_ssl_conf_ca_chain(&conf, &ssl_context->cacert, nullptr);
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ssl_context->ctr_drbg);
@ -131,7 +132,7 @@ namespace fr
}
//Verify server certificate
if((flags = mbedtls_ssl_get_verify_result(ssl.get())) != 0)
if(should_verify && ((flags = mbedtls_ssl_get_verify_result(ssl.get())) != 0))
{
char verify_buffer[512];
mbedtls_x509_crt_verify_info( verify_buffer, sizeof( verify_buffer ), " ! ", flags );
@ -157,6 +158,11 @@ namespace fr
ssl_socket_descriptor = std::move(context);
reconfigure_socket();
}
void SSLSocket::verify_certificates(bool should_verify_)
{
should_verify = should_verify_;
}
}
#endif

View File

@ -67,7 +67,7 @@ namespace fr
void Socket::shutdown()
{
::shutdown(get_socket_descriptor(), 0);
::shutdown(get_socket_descriptor(), SHUT_RDWR);
}
void Socket::reconfigure_socket()