From c63029239f03ffa2cfe5b9ae2eb36ad47890414a Mon Sep 17 00:00:00 2001 From: loki Date: Thu, 26 Dec 2019 22:44:09 +0100 Subject: [PATCH] Prevent unauthorized access to the HTTPS server --- sunshine/crypto.cpp | 23 +++++++++++++++++++ sunshine/crypto.h | 15 +++++++++++++ sunshine/nvhttp.cpp | 55 ++++++++++++++++++++++++++++++++++++++++----- sunshine/utility.h | 4 ++++ 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/sunshine/crypto.cpp b/sunshine/crypto.cpp index 82d54359..05023fc6 100644 --- a/sunshine/crypto.cpp +++ b/sunshine/crypto.cpp @@ -5,6 +5,29 @@ #include #include "crypto.h" namespace crypto { +cert_chain_t::cert_chain_t() : _certs {}, _cert_store {X509_STORE_new() }, _cert_ctx {X509_STORE_CTX_new() } {} +void cert_chain_t::add(x509_t &&cert) { + _certs.emplace_back(std::move(cert)); + + X509_STORE_add_cert(_cert_store.get(), _certs.back().get()); +} + +const char *cert_chain_t::verify(x509_t::element_type *cert) { + util::fail_guard([this]() { + X509_STORE_CTX_cleanup(_cert_ctx.get()); + }); + + X509_STORE_CTX_init(_cert_ctx.get(), _cert_store.get(), nullptr, nullptr); + X509_STORE_CTX_set_cert(_cert_ctx.get(), cert); + + auto err = X509_verify_cert(_cert_ctx.get()); + if(err != 1) { + return X509_verify_cert_error_string(X509_STORE_CTX_get_error(_cert_ctx.get())); + } + + return nullptr; +} + cipher_t::cipher_t(const crypto::aes_t &key) : ctx { EVP_CIPHER_CTX_new() }, key { key }, padding { true } {} int cipher_t::decrypt(const std::string_view &cipher, std::vector &plaintext) { int len; diff --git a/sunshine/crypto.h b/sunshine/crypto.h index 82f4b472..6ec946e5 100644 --- a/sunshine/crypto.h +++ b/sunshine/crypto.h @@ -23,6 +23,8 @@ using sha256_t = std::array; using aes_t = std::array; using x509_t = util::safe_ptr; +using x509_store_t = util::safe_ptr; +using x509_store_ctx_t = util::safe_ptr; using cipher_ctx_t = util::safe_ptr; using md_ctx_t = util::safe_ptr; using bio_t = util::safe_ptr; @@ -42,6 +44,19 @@ std::string_view signature(const x509_t &x); std::string rand(std::size_t bytes); +class cert_chain_t { +public: + KITTY_DECL_CONSTR(cert_chain_t) + + void add(x509_t &&cert); + + const char *verify(x509_t::element_type *cert); +private: + std::vector _certs; + x509_store_t _cert_store; + x509_store_ctx_t _cert_ctx; +}; + class cipher_t { public: cipher_t(const aes_t &key); diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 18851b30..4eb52fa8 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "uuid.h" #include "config.h" @@ -211,7 +212,7 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) tree.put("root..status_code", 200); } -void clientpairingsecret(pair_session_t &sess, pt::ptree &tree, const args_t &args) { +void clientpairingsecret(std::shared_ptr> &add_cert, pair_session_t &sess, pt::ptree &tree, const args_t &args) { auto &client = sess.client; auto pairingsecret = util::from_hex_vec(args.at("clientpairingsecret"), true); @@ -243,6 +244,7 @@ void clientpairingsecret(pair_session_t &sess, pt::ptree &tree, const args_t &ar if(crypto::verify256(crypto::x509(client.cert), secret, sign)) { tree.put("root.paired", 1); + add_cert->raise(crypto::x509(client.cert)); auto it = map_id_sess.find(client.uniqueID); @@ -307,7 +309,7 @@ void not_found(std::shared_ptr::Response> resp } template -void pair(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { +void pair(std::shared_ptr> &add_cert, std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); auto args = request->parse_query_string(); @@ -343,7 +345,7 @@ void pair(std::shared_ptr::Response> response, serverchallengeresp(sess_it->second, tree, args); } else if(it = args.find("clientpairingsecret"); it != std::end(args)) { - clientpairingsecret(sess_it->second, tree, args); + clientpairingsecret(add_cert, sess_it->second, tree, args); } else { tree.put("root..status_code", 404); @@ -627,12 +629,53 @@ void start() { conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); conf_intern.servercert = read_file(config::nvhttp.cert.c_str()); - https_server_t https_server { config::nvhttp.cert, config::nvhttp.pkey }; + auto ctx = std::make_shared(boost::asio::ssl::context::tls); + ctx->use_certificate_chain_file(config::nvhttp.cert); + ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem); + for(auto &[_,client] : map_id_client) { + ctx->add_certificate_authority(boost::asio::buffer(client.cert)); + } + + auto add_cert = std::make_shared>(); + crypto::cert_chain_t cert_chain; + + // When Moonlight has recently paired, the certificate has not yet been added to ctx + // Thus it needs to be verified manually + ctx->set_verify_callback([&cert_chain, add_cert](int verified, boost::asio::ssl::verify_context &ctx) { + util::fail_guard([&]() { + char subject_name[256]; + + auto x509 = ctx.native_handle(); + X509_NAME_oneline(X509_get_subject_name(X509_STORE_CTX_get_current_cert(x509)), subject_name, sizeof(subject_name)); + + std::cout << subject_name << " -- "sv << (verified ? "verfied"sv : "denied"sv) << std::endl; + }); + + if(verified) { + return 1; + } + + while(add_cert->peek()) { + cert_chain.add(add_cert->pop()); + } + + auto err_str = cert_chain.verify(X509_STORE_CTX_get_current_cert(ctx.native_handle())); + if(err_str) { + std::cout << "SSL Verification error :: "sv << err_str << std::endl; + return 0; + } + + verified = true; + return 1; + }); + + + https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once }; http_server_t http_server; https_server.default_resource = not_found; https_server.resource["^/serverinfo$"]["GET"] = serverinfo; - https_server.resource["^/pair$"]["GET"] = pair; + https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; https_server.resource["^/applist$"]["GET"] = applist; https_server.resource["^/appasset$"]["GET"] = appasset; https_server.resource["^/launch$"]["GET"] = launch; @@ -646,7 +689,7 @@ void start() { http_server.default_resource = not_found; http_server.resource["^/serverinfo$"]["GET"] = serverinfo; - http_server.resource["^/pair$"]["GET"] = pair; + http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; http_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; http_server.config.reuse_address = true; diff --git a/sunshine/utility.h b/sunshine/utility.h index 71b95040..f74338a7 100644 --- a/sunshine/utility.h +++ b/sunshine/utility.h @@ -10,6 +10,10 @@ #include #include #include +#define KITTY_DECL_CONSTR(x)\ + x(x&&) noexcept = default;\ + x&operator=(x&&) noexcept = default;\ + x(); #define KITTY_DEFAULT_CONSTR(x)\ x(x&&) noexcept = default;\