CVE-2024-31221: Insufficient Information (NVD-CWE-noinfo)
value: Sunshine is a self-hosted game stream host for Moonlight. Starting in version 0.10.0 and prior to version 0.23.0, after unpairing all devices in the web UI interface and then pairing only one device, all of the previously devices will be temporarily paired. Version 0.23.0 contains a patch for the issue. As a workaround, restarting Sunshine after unpairing all devices prevents the vulnerability.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-04-08T15:15Z
Repository: https://github.com/LizardByte/Sunshine
Language: C++
Files changed: src/crypto.cpp, src/crypto.h, src/nvhttp.cpp
Commit date: 2024-04-06 16:39:16 -0400
Commit message: fix(security): ensure unpairing takes effect without restart (#2365)


Diff: diff --git a/src/crypto.cpp b/src/crypto.cpp
index e92e6e9..9a5ef5a 100644
--- a/src/crypto.cpp
+++ b/src/crypto.cpp
@@ -15,10 +15,14 @@ namespace crypto {
     x509_store_t x509_store { X509_STORE_new() };
 
     X509_STORE_add_cert(x509_store.get(), cert.get());
     _certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
   }
+  void
+  cert_chain_t::clear() {
+    _certs.clear();
+  }
 
   static int
   openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
     int err_code = X509_STORE_CTX_get_error(ctx);
 
diff --git a/src/crypto.h b/src/crypto.h
index eb355f5..410d3c8 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -71,10 +71,13 @@ namespace crypto {
     KITTY_DECL_CONSTR(cert_chain_t)
 
     void
     add(x509_t &&cert);
 
+    void
+    clear();
+
     const char *
     verify(x509_t::element_type *cert);
 
   private:
     std::vector<std::pair<x509_t, x509_store_t>> _certs;
diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp
index b8bddb4..695820f 100644
--- a/src/nvhttp.cpp
+++ b/src/nvhttp.cpp
@@ -40,10 +40,12 @@ using namespace std::literals;
 namespace nvhttp {
 
   namespace fs = std::filesystem;
   namespace pt = boost::property_tree;
 
+  crypto::cert_chain_t cert_chain;
+
   class SunshineHttpsServer: public SimpleWeb::Server<SimpleWeb::HTTPS> {
   public:
     SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file):
         SimpleWeb::Server<SimpleWeb::HTTPS>::Server(certification_file, private_key_file) {}
 
@@ -1015,28 +1017,27 @@ namespace nvhttp {
     }
 
     conf_intern.pkey = file_handler::read_file(config::nvhttp.pkey.c_str());
     conf_intern.servercert = file_handler::read_file(config::nvhttp.cert.c_str());
 
-    crypto::cert_chain_t cert_chain;
     for (auto &[_, client] : map_id_client) {
       for (auto &cert : client.certs) {
         cert_chain.add(crypto::x509(cert));
       }
     }
 
     auto add_cert = std::make_shared<safe::queue_t<crypto::x509_t>>(30);
 
-    // /resume doesn't always get the parameter "localAudioPlayMode"
-    // /launch will store it in host_audio
+    // resume doesn't always get the parameter "localAudioPlayMode"
+    // launch will store it in host_audio
     bool host_audio {};
 
     https_server_t https_server { config::nvhttp.cert, config::nvhttp.pkey };
     http_server_t http_server;
 
     // Verify certificates after establishing connection
-    https_server.verify = [&cert_chain, add_cert](SSL *ssl) {
+    https_server.verify = [add_cert](SSL *ssl) {
       crypto::x509_t x509 { SSL_get_peer_certificate(ssl) };
       if (!x509) {
         BOOST_LOG(info) << "unknown -- denied"sv;
         return 0;
       }
@@ -1146,8 +1147,9 @@ namespace nvhttp {
    * ```
    */
   void
   erase_all_clients() {
     map_id_client.clear();
+    cert_chain.clear();
     save_state();
   }
 }  // namespace nvhttp

Vulnerable code:     crypto::cert_chain_t cert_chain;
    // /resume doesn't always get the parameter "localAudioPlayMode"
    // /launch will store it in host_audio
    https_server.verify = [&cert_chain, add_cert](SSL *ssl) {
Fixed code:   void
  cert_chain_t::clear() {
    _certs.clear();
  }
    void
    clear();

  crypto::cert_chain_t cert_chain;

    // resume doesn't always get the parameter "localAudioPlayMode"
    // launch will store it in host_audio
    https_server.verify = [add_cert](SSL *ssl) {
    cert_chain.clear();