Skip to content

Commit

Permalink
Add TLS codepath to server_base
Browse files Browse the repository at this point in the history
  • Loading branch information
loonycyborg committed Feb 23, 2021
1 parent cd2dd8d commit 0896b5c
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 18 deletions.
2 changes: 1 addition & 1 deletion SConstruct
Expand Up @@ -393,7 +393,7 @@ if env["prereqs"]:

if(env["PLATFORM"] != 'darwin'):
# Otherwise, use Security.framework
have_server_prereqs = have_server_prereqs & conf.CheckLib("libcrypto")
have_server_prereqs = have_server_prereqs & conf.CheckLib("libcrypto") & conf.CheckLib("ssl")

env = conf.Finish()

Expand Down
2 changes: 2 additions & 0 deletions src/server/campaignd/server.cpp
Expand Up @@ -449,6 +449,8 @@ void server::load_config()
}

LOG_CS << "Loaded addons metadata. " << addons_.size() << " addons found.\n";

load_tls_config(cfg_);
}

std::ostream& operator<<(std::ostream& o, const server::request& r)
Expand Down
80 changes: 63 additions & 17 deletions src/server/common/server_base.cpp
Expand Up @@ -15,6 +15,7 @@
#include "server/common/server_base.hpp"

#include "log.hpp"
#include "serialization/parser.hpp"
#include "filesystem.hpp"

#ifdef HAVE_CONFIG_H
Expand Down Expand Up @@ -98,6 +99,7 @@ void server_base::serve(boost::asio::yield_context yield, boost::asio::ip::tcp::
}

socket_ptr socket = std::make_shared<socket_ptr::element_type>(io_service_);
bool use_tls { false };

boost::system::error_code error;
acceptor.async_accept(socket->lowest_layer(), yield[error]);
Expand Down Expand Up @@ -128,30 +130,57 @@ void server_base::serve(boost::asio::yield_context yield, boost::asio::ip::tcp::

DBG_SERVER << client_address(socket) << "\tnew connection tentatively accepted\n";

boost::shared_array<char> handshake(new char[4]);
async_read(*socket, boost::asio::buffer(handshake.get(), 4), yield[error]);
union {
uint32_t number;
char buf[4];
} protocol_version;

async_read(*socket, boost::asio::buffer(protocol_version.buf), yield[error]);
if(check_error(error, socket))
return;

if(memcmp(handshake.get(), "\0\0\0\0", 4) != 0) {
ERR_SERVER << client_address(socket) << "\tincorrect handshake\n";
return;
}
switch(ntohl(protocol_version.number)) {
case 0:
async_write(*socket, boost::asio::buffer(handshake_response_.buf, 4), yield[error]);
if(check_error(error, socket)) return;
break;
case 1:
if(!tls_enabled_) {
ERR_SERVER << client_address(socket) << "\tTLS requested by client but not enabled on server\n";
async_send_error(socket, "TLS support disabled on server.");
return;
}
use_tls = true;

async_write(*socket, boost::asio::buffer(handshake_response_.buf, 4), yield[error]);
break;
default:
ERR_SERVER << client_address(socket) << "\tincorrect handshake\n";
return;
}

if(!check_error(error, socket)) {
const std::string ip = client_address(socket);
const std::string ip = client_address(socket);

const std::string reason = is_ip_banned(ip);
if (!reason.empty()) {
LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
async_send_error(socket, "You are banned. Reason: " + reason);
return;
} else if (ip_exceeds_connection_limit(ip)) {
LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
async_send_error(socket, "Too many connections from your IP.");
const std::string reason = is_ip_banned(ip);
if (!reason.empty()) {
LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
async_send_error(socket, "You are banned. Reason: " + reason);
return;
} else if (ip_exceeds_connection_limit(ip)) {
LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
async_send_error(socket, "Too many connections from your IP.");
return;
} else {
if(use_tls) {
async_send_warning(socket, "Go TLS.");
tls_socket_ptr tls_socket { new tls_socket_ptr::element_type(std::move(*socket), tls_context_) };
tls_socket->async_handshake(boost::asio::ssl::stream_base::server, yield[error]);
if(error) {
ERR_SERVER << "TLS handshake failed: " << error.message() << "\n";
return;
}

DBG_SERVER << ip << "\tnew encrypted connection fully accepted\n";
this->handle_new_client(tls_socket);
} else {
DBG_SERVER << ip << "\tnew connection fully accepted\n";
this->handle_new_client(socket);
Expand Down Expand Up @@ -473,6 +502,23 @@ void server_base::async_send_warning(socket_ptr socket, const std::string& msg,
async_send_doc_queued(socket, doc);
}

void server_base::load_tls_config(const config& cfg)
{
tls_enabled_ = cfg["tls_enabled"].to_bool(false);
if(!tls_enabled_) return;

tls_context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
| boost::asio::ssl::context::single_dh_use
);

tls_context_.use_certificate_chain_file(cfg["tls_fullchain"].str());
tls_context_.use_private_key_file(cfg["tls_private_key"].str(), boost::asio::ssl::context::pem);
if(!cfg["tls_dh"].str().empty()) tls_context_.use_tmp_dh_file(cfg["tls_dh"].str());
}

// This is just here to get it to build without the deprecation_message function
#include "game_version.hpp"
#include "deprecation.hpp"
Expand Down
12 changes: 12 additions & 0 deletions src/server/common/server_base.hpp
Expand Up @@ -33,14 +33,20 @@
#endif
#include <boost/asio/signal_set.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/shared_array.hpp>
#include <utils/variant.hpp>

#include <map>

extern bool dump_wml;

class config;

typedef std::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;
typedef std::shared_ptr<boost::asio::ssl::stream<socket_ptr::element_type>> tls_socket_ptr;
typedef utils::variant<socket_ptr, tls_socket_ptr> any_socket_ptr;

struct server_shutdown : public game::error
{
Expand Down Expand Up @@ -93,8 +99,13 @@ class server_base
unsigned short port_;
bool keep_alive_;
boost::asio::io_service io_service_;
boost::asio::ssl::context tls_context_ { boost::asio::ssl::context::sslv23 };
bool tls_enabled_ { false };
boost::asio::ip::tcp::acceptor acceptor_v6_;
boost::asio::ip::tcp::acceptor acceptor_v4_;

void load_tls_config(const config& cfg);

void start_server();
void serve(boost::asio::yield_context yield, boost::asio::ip::tcp::acceptor& acceptor, boost::asio::ip::tcp::endpoint endpoint);

Expand All @@ -104,6 +115,7 @@ class server_base
} handshake_response_;

virtual void handle_new_client(socket_ptr socket) = 0;
virtual void handle_new_client(tls_socket_ptr socket) = 0;

virtual bool accepting_connections() const { return true; }
virtual std::string is_ip_banned(const std::string&) { return std::string(); }
Expand Down
8 changes: 8 additions & 0 deletions src/server/wesnothd/server.cpp
Expand Up @@ -529,6 +529,8 @@ void server::load_config()
tournaments_ = user_handler_->get_tournaments();
}
#endif

load_tls_config(cfg_);
}

bool server::ip_exceeds_connection_limit(const std::string& ip) const
Expand Down Expand Up @@ -607,6 +609,12 @@ void server::handle_new_client(socket_ptr socket)
boost::asio::spawn(io_service_, [socket, this](boost::asio::yield_context yield) { login_client(yield, socket); });
}

void server::handle_new_client(tls_socket_ptr /*socket*/)
{
//boost::asio::spawn(io_service_, [socket, this](boost::asio::yield_context yield) { login_client(yield, socket); });
throw std::runtime_error("Not implemented");
}

void server::login_client(boost::asio::yield_context yield, socket_ptr socket)
{
boost::system::error_code ec;
Expand Down
1 change: 1 addition & 0 deletions src/server/wesnothd/server.hpp
Expand Up @@ -38,6 +38,7 @@ class server : public server_base

private:
void handle_new_client(socket_ptr socket);
void handle_new_client(tls_socket_ptr socket);

void login_client(boost::asio::yield_context yield, socket_ptr socket);
bool is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& username, bool& registered, bool& is_moderator);
Expand Down

0 comments on commit 0896b5c

Please sign in to comment.