diff --git a/sunshine/crypto.cpp b/sunshine/crypto.cpp index 05023fc6..8f928ec7 100644 --- a/sunshine/crypto.cpp +++ b/sunshine/crypto.cpp @@ -5,27 +5,41 @@ #include #include "crypto.h" namespace crypto { -cert_chain_t::cert_chain_t() : _certs {}, _cert_store {X509_STORE_new() }, _cert_ctx {X509_STORE_CTX_new() } {} +cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx {X509_STORE_CTX_new() } {} void cert_chain_t::add(x509_t &&cert) { - _certs.emplace_back(std::move(cert)); + x509_store_t x509_store { X509_STORE_new() }; - X509_STORE_add_cert(_cert_store.get(), _certs.back().get()); + X509_STORE_add_cert(x509_store.get(), cert.get()); + _certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store))); } +/* + * When certificates from two or more instances of Moonlight have been added to x509_store_t, + * only one of them will be verified by X509_verify_cert, resulting in only a single instance of + * Moonlight to be able to use Sunshine + * + * To circumvent this, x509_store_t instance will be created for each instance of the certificates. + */ const char *cert_chain_t::verify(x509_t::element_type *cert) { - util::fail_guard([this]() { - X509_STORE_CTX_cleanup(_cert_ctx.get()); - }); + for(auto &[_,x509_store] : _certs) { + 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); + X509_STORE_CTX_init(_cert_ctx.get(), x509_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())); + auto err = X509_verify_cert(_cert_ctx.get()); + if (err == 1) { + return nullptr; + } + auto err_code = X509_STORE_CTX_get_error(_cert_ctx.get()); + if (err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) { + return X509_verify_cert_error_string(err_code); + } } - return nullptr; + return X509_verify_cert_error_string(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); } cipher_t::cipher_t(const crypto::aes_t &key) : ctx { EVP_CIPHER_CTX_new() }, key { key }, padding { true } {} diff --git a/sunshine/crypto.h b/sunshine/crypto.h index 6ec946e5..22dfa42a 100644 --- a/sunshine/crypto.h +++ b/sunshine/crypto.h @@ -52,8 +52,7 @@ public: const char *verify(x509_t::element_type *cert); private: - std::vector _certs; - x509_store_t _cert_store; + std::vector> _certs; x509_store_ctx_t _cert_ctx; }; diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 4eb52fa8..ede9a5d7 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -48,11 +48,14 @@ struct conf_intern_t { struct client_t { std::string uniqueID; - std::string cert; + std::vector certs; }; struct pair_session_t { - client_t client; + struct { + std::string uniqueID; + std::string cert; + } client; std::unique_ptr cipher_key; std::vector clienthash; @@ -94,9 +97,16 @@ void save_devices() { pt::ptree node; node.put("uniqueid"s, client.uniqueID); - node.put("cert"s, client.cert); - nodes.push_back(std::make_pair("", node)); + pt::ptree cert_nodes; + for(auto &cert : client.certs) { + pt::ptree cert_node; + cert_node.put_value(cert); + cert_nodes.push_back(std::make_pair(""s, cert_node)); + } + node.add_child("certs"s, cert_nodes); + + nodes.push_back(std::make_pair(""s, node)); } pt::write_json(config::nvhttp.file_devices, root); @@ -125,20 +135,24 @@ void load_devices() { auto &client = map_id_client.emplace(uniqID, client_t {}).first->second; client.uniqueID = uniqID; - client.cert = node.get("cert"); + + for(auto &[_, el] : node.get_child("certs")) { + client.certs.emplace_back(el.get_value()); + } } } -void update_id_client(client_t &client, op_e op) { +void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) { switch(op) { case op_e::ADD: { - auto uniqID = client.uniqueID; - map_id_client.emplace(std::move(uniqID), std::move(client)); + auto &client = map_id_client[uniqueID]; + client.certs.emplace_back(std::move(cert)); + client.uniqueID = uniqueID; } break; case op_e::REMOVE: - map_id_client.erase(client.uniqueID); + map_id_client.erase(uniqueID); break; } @@ -222,7 +236,7 @@ void clientpairingsecret(std::shared_ptr> &add_cer assert((secret.size() + sign.size()) == pairingsecret.size()); - auto x509 = crypto::x509(sess.client.cert); + auto x509 = crypto::x509(client.cert); auto x509_sign = crypto::signature(x509); std::string data; @@ -248,8 +262,7 @@ void clientpairingsecret(std::shared_ptr> &add_cer auto it = map_id_sess.find(client.uniqueID); - auto uniqID = client.uniqueID; - update_id_client(client, op_e::ADD); + update_id_client(client.uniqueID, std::move(client.cert), op_e::ADD); map_id_sess.erase(it); } else { @@ -632,15 +645,17 @@ void start() { 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); + + crypto::cert_chain_t cert_chain; for(auto &[_,client] : map_id_client) { - ctx->add_certificate_authority(boost::asio::buffer(client.cert)); + for(auto &cert : client.certs) { + cert_chain.add(crypto::x509(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 + // Ugly hack for verifying certificates, see crypto::cert_chain_t::verify() for details ctx->set_verify_callback([&cert_chain, add_cert](int verified, boost::asio::ssl::verify_context &ctx) { util::fail_guard([&]() { char subject_name[256]; @@ -656,7 +671,13 @@ void start() { } while(add_cert->peek()) { - cert_chain.add(add_cert->pop()); + char subject_name[256]; + + auto cert = add_cert->pop(); + X509_NAME_oneline(X509_get_subject_name(cert.get()), subject_name, sizeof(subject_name)); + + std::cout << "Added cert ["sv << subject_name << "]"sv << std::endl; + cert_chain.add(std::move(cert)); } auto err_str = cert_chain.verify(X509_STORE_CTX_get_current_cert(ctx.native_handle()));