Support multiple instance of Moonlight using HTTPS
This commit is contained in:
+26
-12
@@ -5,27 +5,41 @@
|
|||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
namespace crypto {
|
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) {
|
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) {
|
const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
||||||
util::fail_guard([this]() {
|
for(auto &[_,x509_store] : _certs) {
|
||||||
X509_STORE_CTX_cleanup(_cert_ctx.get());
|
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_init(_cert_ctx.get(), x509_store.get(), nullptr, nullptr);
|
||||||
X509_STORE_CTX_set_cert(_cert_ctx.get(), cert);
|
X509_STORE_CTX_set_cert(_cert_ctx.get(), cert);
|
||||||
|
|
||||||
auto err = X509_verify_cert(_cert_ctx.get());
|
auto err = X509_verify_cert(_cert_ctx.get());
|
||||||
if(err != 1) {
|
if (err == 1) {
|
||||||
return X509_verify_cert_error_string(X509_STORE_CTX_get_error(_cert_ctx.get()));
|
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 } {}
|
cipher_t::cipher_t(const crypto::aes_t &key) : ctx { EVP_CIPHER_CTX_new() }, key { key }, padding { true } {}
|
||||||
|
|||||||
+1
-2
@@ -52,8 +52,7 @@ public:
|
|||||||
|
|
||||||
const char *verify(x509_t::element_type *cert);
|
const char *verify(x509_t::element_type *cert);
|
||||||
private:
|
private:
|
||||||
std::vector<x509_t> _certs;
|
std::vector<std::pair<x509_t, x509_store_t>> _certs;
|
||||||
x509_store_t _cert_store;
|
|
||||||
x509_store_ctx_t _cert_ctx;
|
x509_store_ctx_t _cert_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+38
-17
@@ -48,11 +48,14 @@ struct conf_intern_t {
|
|||||||
|
|
||||||
struct client_t {
|
struct client_t {
|
||||||
std::string uniqueID;
|
std::string uniqueID;
|
||||||
std::string cert;
|
std::vector<std::string> certs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pair_session_t {
|
struct pair_session_t {
|
||||||
client_t client;
|
struct {
|
||||||
|
std::string uniqueID;
|
||||||
|
std::string cert;
|
||||||
|
} client;
|
||||||
|
|
||||||
std::unique_ptr<crypto::aes_t> cipher_key;
|
std::unique_ptr<crypto::aes_t> cipher_key;
|
||||||
std::vector<uint8_t> clienthash;
|
std::vector<uint8_t> clienthash;
|
||||||
@@ -94,9 +97,16 @@ void save_devices() {
|
|||||||
pt::ptree node;
|
pt::ptree node;
|
||||||
|
|
||||||
node.put("uniqueid"s, client.uniqueID);
|
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);
|
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;
|
auto &client = map_id_client.emplace(uniqID, client_t {}).first->second;
|
||||||
|
|
||||||
client.uniqueID = uniqID;
|
client.uniqueID = uniqID;
|
||||||
client.cert = node.get<std::string>("cert");
|
|
||||||
|
for(auto &[_, el] : node.get_child("certs")) {
|
||||||
|
client.certs.emplace_back(el.get_value<std::string>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
switch(op) {
|
||||||
case op_e::ADD:
|
case op_e::ADD:
|
||||||
{
|
{
|
||||||
auto uniqID = client.uniqueID;
|
auto &client = map_id_client[uniqueID];
|
||||||
map_id_client.emplace(std::move(uniqID), std::move(client));
|
client.certs.emplace_back(std::move(cert));
|
||||||
|
client.uniqueID = uniqueID;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case op_e::REMOVE:
|
case op_e::REMOVE:
|
||||||
map_id_client.erase(client.uniqueID);
|
map_id_client.erase(uniqueID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +236,7 @@ void clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cer
|
|||||||
|
|
||||||
assert((secret.size() + sign.size()) == pairingsecret.size());
|
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);
|
auto x509_sign = crypto::signature(x509);
|
||||||
|
|
||||||
std::string data;
|
std::string data;
|
||||||
@@ -248,8 +262,7 @@ void clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cer
|
|||||||
|
|
||||||
auto it = map_id_sess.find(client.uniqueID);
|
auto it = map_id_sess.find(client.uniqueID);
|
||||||
|
|
||||||
auto uniqID = client.uniqueID;
|
update_id_client(client.uniqueID, std::move(client.cert), op_e::ADD);
|
||||||
update_id_client(client, op_e::ADD);
|
|
||||||
map_id_sess.erase(it);
|
map_id_sess.erase(it);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -632,15 +645,17 @@ void start() {
|
|||||||
auto ctx = std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tls);
|
auto ctx = std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tls);
|
||||||
ctx->use_certificate_chain_file(config::nvhttp.cert);
|
ctx->use_certificate_chain_file(config::nvhttp.cert);
|
||||||
ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem);
|
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) {
|
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<safe::queue_t<crypto::x509_t>>();
|
auto add_cert = std::make_shared<safe::queue_t<crypto::x509_t>>();
|
||||||
crypto::cert_chain_t cert_chain;
|
|
||||||
|
|
||||||
// When Moonlight has recently paired, the certificate has not yet been added to ctx
|
// Ugly hack for verifying certificates, see crypto::cert_chain_t::verify() for details
|
||||||
// Thus it needs to be verified manually
|
|
||||||
ctx->set_verify_callback([&cert_chain, add_cert](int verified, boost::asio::ssl::verify_context &ctx) {
|
ctx->set_verify_callback([&cert_chain, add_cert](int verified, boost::asio::ssl::verify_context &ctx) {
|
||||||
util::fail_guard([&]() {
|
util::fail_guard([&]() {
|
||||||
char subject_name[256];
|
char subject_name[256];
|
||||||
@@ -656,7 +671,13 @@ void start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while(add_cert->peek()) {
|
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()));
|
auto err_str = cert_chain.verify(X509_STORE_CTX_get_current_cert(ctx.native_handle()));
|
||||||
|
|||||||
Reference in New Issue
Block a user