Added checking of whitelisted keys on server
This commit is contained in:
@@ -67,6 +67,9 @@ else()
|
||||
message(FATAL_ERROR "OpenSSL not found")
|
||||
endif()
|
||||
|
||||
# json
|
||||
#ind_package(nlohmann_json CONFIG REQUIRED)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Output directories
|
||||
# ---------------------------------------------------------
|
||||
@@ -86,7 +89,7 @@ endforeach()
|
||||
# ---------------------------------------------------------
|
||||
file(GLOB_RECURSE COMMON_SRC CONFIGURE_DEPENDS src/common/*.cpp)
|
||||
add_library(common STATIC ${COMMON_SRC})
|
||||
target_link_libraries(common PUBLIC sodium OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_link_libraries(common PUBLIC sodium OpenSSL::SSL OpenSSL::Crypto nlohmann_json::nlohmann_json)
|
||||
target_include_directories(common PUBLIC
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${sodium_SOURCE_DIR}/src/libsodium/include
|
||||
@@ -99,7 +102,7 @@ target_compile_definitions(common PUBLIC ASIO_STANDALONE)
|
||||
# ---------------------------------------------------------
|
||||
file(GLOB_RECURSE CLIENT_SRC CONFIGURE_DEPENDS src/client/*.cpp)
|
||||
add_executable(client ${CLIENT_SRC})
|
||||
target_link_libraries(client PRIVATE common sodium OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_link_libraries(client PRIVATE common sodium OpenSSL::SSL OpenSSL::Crypto nlohmann_json::nlohmann_json)
|
||||
target_include_directories(client PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${sodium_SOURCE_DIR}/src/libsodium/include
|
||||
@@ -113,7 +116,7 @@ set_target_properties(client PROPERTIES OUTPUT_NAME "columnlynx_client")
|
||||
# ---------------------------------------------------------
|
||||
file(GLOB_RECURSE SERVER_SRC CONFIGURE_DEPENDS src/server/*.cpp)
|
||||
add_executable(server ${SERVER_SRC})
|
||||
target_link_libraries(server PRIVATE common sodium OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_link_libraries(server PRIVATE common sodium OpenSSL::SSL OpenSSL::Crypto nlohmann_json::nlohmann_json)
|
||||
target_include_directories(server PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${sodium_SOURCE_DIR}/src/libsodium/include
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@@ -25,9 +29,11 @@ namespace ColumnLynx::Utils {
|
||||
std::string getVersion();
|
||||
unsigned short serverPort();
|
||||
unsigned char protocolVersion();
|
||||
std::vector<std::string> getWhitelistedKeys();
|
||||
|
||||
// Raw byte to hex string conversion helper
|
||||
std::string bytesToHexString(const uint8_t* bytes, size_t length);
|
||||
std::vector<uint8_t> hexStringToBytes(const std::string& hex);
|
||||
|
||||
// uint8_t to raw string conversion helper
|
||||
template <size_t N>
|
||||
@@ -39,7 +45,7 @@ namespace ColumnLynx::Utils {
|
||||
return std::string(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
inline constexpr uint64_t bswap64(uint64_t x) {
|
||||
inline constexpr uint64_t cbswap64(uint64_t x) {
|
||||
return ((x & 0x00000000000000FFULL) << 56) |
|
||||
((x & 0x000000000000FF00ULL) << 40) |
|
||||
((x & 0x0000000000FF0000ULL) << 24) |
|
||||
@@ -50,11 +56,11 @@ namespace ColumnLynx::Utils {
|
||||
((x & 0xFF00000000000000ULL) >> 56);
|
||||
}
|
||||
|
||||
inline constexpr uint64_t htobe64(uint64_t x) {
|
||||
return bswap64(x); // host -> big-endian (for little-endian hosts)
|
||||
inline constexpr uint64_t chtobe64(uint64_t x) {
|
||||
return cbswap64(x); // host -> big-endian (for little-endian hosts)
|
||||
}
|
||||
|
||||
inline constexpr uint64_t be64toh(uint64_t x) {
|
||||
return bswap64(x); // big-endian -> host (for little-endian hosts)
|
||||
inline constexpr uint64_t cbe64toh(uint64_t x) {
|
||||
return cbswap64(x); // big-endian -> host (for little-endian hosts)
|
||||
}
|
||||
};
|
||||
@@ -28,7 +28,7 @@ namespace ColumnLynx::Net::TCP {
|
||||
// Check if hostname or IPv4/IPv6
|
||||
sockaddr_in addr4{};
|
||||
sockaddr_in6 addr6{};
|
||||
self->mIsHostDomain = inet_pton(AF_INET, mHost.c_str(), (void*)(&addr4)) != 1 && inet_pton(AF_INET6, mHost.c_str(), (void*)(&addr6)) != 1;
|
||||
self->mIsHostDomain = inet_pton(AF_INET, mHost.c_str(), (void*)(&addr4)) != 1 && inet_pton(AF_INET6, mHost.c_str(), (void*)(&addr6)) != 1; // Voodoo black magic
|
||||
|
||||
std::vector<uint8_t> payload;
|
||||
payload.reserve(1 + crypto_box_PUBLICKEYBYTES);
|
||||
@@ -248,7 +248,7 @@ namespace ColumnLynx::Net::TCP {
|
||||
std::memcpy(&mConnectionSessionID, decrypted.data(), sizeof(mConnectionSessionID));
|
||||
std::memcpy(&mTunConfig, decrypted.data() + sizeof(mConnectionSessionID), sizeof(Protocol::TunConfig));
|
||||
|
||||
mConnectionSessionID = Utils::be64toh(mConnectionSessionID);
|
||||
mConnectionSessionID = Utils::cbe64toh(mConnectionSessionID);
|
||||
|
||||
Utils::log("Connection established with Session ID: " + std::to_string(mConnectionSessionID));
|
||||
|
||||
|
||||
@@ -61,4 +61,48 @@ namespace ColumnLynx::Utils {
|
||||
|
||||
return hexString;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> hexStringToBytes(const std::string& hex) {
|
||||
// TODO: recover from errors
|
||||
|
||||
if (hex.length() % 2 != 0) {
|
||||
throw std::invalid_argument("Hex string must have even length");
|
||||
}
|
||||
|
||||
auto hexValue = [](char c) -> uint8_t {
|
||||
if ('0' <= c && c <= '9') return c - '0';
|
||||
if ('A' <= c && c <= 'F') return c - 'A' + 10;
|
||||
if ('a' <= c && c <= 'f') return c - 'a' + 10;
|
||||
throw std::invalid_argument("Invalid hex character");
|
||||
};
|
||||
|
||||
size_t len = hex.length();
|
||||
std::vector<uint8_t> bytes;
|
||||
bytes.reserve(len / 2);
|
||||
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
uint8_t high = hexValue(hex[i]);
|
||||
uint8_t low = hexValue(hex[i + 1]);
|
||||
bytes.push_back((high << 4) | low);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<std::string> getWhitelistedKeys() {
|
||||
// Currently re-reads the file every time, should be fine.
|
||||
// Advantage of it is that you don't need to reload the server binary after adding/removing keys. Disadvantage is re-reading the file every time.
|
||||
// I might redo this part.
|
||||
|
||||
std::vector<std::string> out;
|
||||
|
||||
std::ifstream file("whitelisted_keys"); // TODO: This is hardcoded for now, make dynamic
|
||||
std::string line;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
out.push_back(line);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <unordered_set>
|
||||
#include <cxxopts/cxxopts.hpp>
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
//#include <nlohmann/json.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
using namespace ColumnLynx::Utils;
|
||||
@@ -41,7 +42,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
options.add_options()
|
||||
("h,help", "Print help")
|
||||
("4,ipv4-only", "Force IPv4 only operation", cxxopts::value<bool>()->default_value("false"));
|
||||
("4,ipv4-only", "Force IPv4 only operation", cxxopts::value<bool>()->default_value("false"))
|
||||
("c,config", "Specify config file location", cxxopts::value<std::string>()->default_value("config.json"));
|
||||
|
||||
PanicHandler::init();
|
||||
|
||||
@@ -53,6 +55,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
bool ipv4Only = result["ipv4-only"].as<bool>();
|
||||
std::string configPath = result["config"].as<std::string>();
|
||||
|
||||
log("ColumnLynx Server, Version " + getVersion());
|
||||
log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details.");
|
||||
@@ -64,6 +67,18 @@ int main(int argc, char** argv) {
|
||||
std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>("utun0");
|
||||
log("Using virtual interface: " + tun->getName());
|
||||
|
||||
/*
|
||||
// Load the config
|
||||
std::ifstream f(configPath);
|
||||
if (!f) {
|
||||
error("Could not open config.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
nlohmann::json j;
|
||||
f >> j; // parse
|
||||
*/
|
||||
|
||||
// Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory)
|
||||
LibSodiumWrapper sodiumWrapper = LibSodiumWrapper();
|
||||
log("Server public key: " + bytesToHexString(sodiumWrapper.getPublicKey(), crypto_sign_PUBLICKEYBYTES));
|
||||
|
||||
@@ -115,6 +115,14 @@ namespace ColumnLynx::Net::TCP {
|
||||
Utils::log("Client protocol version " + std::to_string(clientProtoVer) + " accepted from " + reqAddr + ".");
|
||||
|
||||
std::memcpy(mConnectionPublicKey.data(), data.data() + 1, std::min(data.size() - 1, sizeof(mConnectionPublicKey))); // Store the client's public key (for identification)
|
||||
|
||||
std::vector<std::string> whitelistedKeys = Utils::getWhitelistedKeys();
|
||||
|
||||
if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(mConnectionPublicKey.data(), mConnectionPublicKey.size())) == whitelistedKeys.end()) {
|
||||
Utils::warn("Non-whitelisted client attempted to connect, terminating. Client IP: " + reqAddr);
|
||||
disconnect();
|
||||
}
|
||||
|
||||
mHandler->sendMessage(ServerMessageType::HANDSHAKE_IDENTIFY, Utils::uint8ArrayToString(mLibSodiumWrapper->getPublicKey(), crypto_sign_PUBLICKEYBYTES)); // This public key should always exist
|
||||
break;
|
||||
}
|
||||
@@ -188,7 +196,7 @@ namespace ColumnLynx::Net::TCP {
|
||||
|
||||
SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP);
|
||||
|
||||
uint64_t sessionIDNet = Utils::htobe64(mConnectionSessionID);
|
||||
uint64_t sessionIDNet = Utils::chtobe64(mConnectionSessionID);
|
||||
|
||||
std::vector<uint8_t> payload(sizeof(uint64_t) + sizeof(tunConfig));
|
||||
std::memcpy(payload.data(), &sessionIDNet, sizeof(uint64_t));
|
||||
|
||||
Reference in New Issue
Block a user