Fix client UUID resolution from SSL peer certificate
Some checks failed
ci-bundle.yml / Fix client UUID resolution from SSL peer certificate (push) Failing after 0s
ci-copr.yml / Fix client UUID resolution from SSL peer certificate (push) Failing after 0s
ci-homebrew.yml / Fix client UUID resolution from SSL peer certificate (push) Failing after 0s

This commit is contained in:
Joey Yakimowich-Payne 2026-02-11 14:39:37 -07:00
commit 0f070b144f
3 changed files with 61 additions and 4 deletions

View file

@ -279,6 +279,53 @@ namespace nvhttp {
} }
} }
/**
* @brief Resolve the real paired client UUID from the SSL peer certificate.
* @param request The HTTPS request whose peer cert identifies the client.
* @return The paired client's UUID, or empty string if not found.
*
* Moonlight sends a placeholder uniqueid ("0123456789ABCDEF") in the launch
* query string. The real identity is the SSL client certificate presented
* during the TLS handshake, which we match against our paired device list.
*/
std::string resolve_client_uuid(req_https_t request) {
auto conn = request->connection_shared();
if (!conn) {
BOOST_LOG(warning) << "resolve_client_uuid: connection expired"sv;
return {};
}
auto ssl = conn->socket->native_handle();
if (!ssl) {
BOOST_LOG(warning) << "resolve_client_uuid: no SSL handle"sv;
return {};
}
crypto::x509_t peer_cert {
#if OPENSSL_VERSION_MAJOR >= 3
SSL_get1_peer_certificate(ssl)
#else
SSL_get_peer_certificate(ssl)
#endif
};
if (!peer_cert) {
BOOST_LOG(warning) << "resolve_client_uuid: no peer certificate"sv;
return {};
}
auto peer_pem = crypto::pem(peer_cert);
for (const auto &device : client_root.named_devices) {
if (device.cert == peer_pem) {
BOOST_LOG(info) << "resolve_client_uuid: matched paired client '"sv << device.name << "' uuid="sv << device.uuid;
return device.uuid;
}
}
BOOST_LOG(warning) << "resolve_client_uuid: peer cert did not match any paired device"sv;
return {};
}
std::shared_ptr<rtsp_stream::launch_session_t> make_launch_session(bool host_audio, const args_t &args) { std::shared_ptr<rtsp_stream::launch_session_t> make_launch_session(bool host_audio, const args_t &args) {
auto launch_session = std::make_shared<rtsp_stream::launch_session_t>(); auto launch_session = std::make_shared<rtsp_stream::launch_session_t>();
@ -868,6 +915,11 @@ namespace nvhttp {
host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); host_audio = util::from_view(get_arg(args, "localAudioPlayMode"));
auto launch_session = make_launch_session(host_audio, args); auto launch_session = make_launch_session(host_audio, args);
auto resolved_uuid = resolve_client_uuid(request);
if (!resolved_uuid.empty()) {
launch_session->unique_id = std::move(resolved_uuid);
}
if (rtsp_stream::session_count() == 0) { if (rtsp_stream::session_count() == 0) {
// The display should be restored in case something fails as there are no other sessions. // The display should be restored in case something fails as there are no other sessions.
revert_display_configuration = true; revert_display_configuration = true;
@ -976,6 +1028,11 @@ namespace nvhttp {
} }
const auto launch_session = make_launch_session(host_audio, args); const auto launch_session = make_launch_session(host_audio, args);
auto resolved_uuid = resolve_client_uuid(request);
if (!resolved_uuid.empty()) {
launch_session->unique_id = std::move(resolved_uuid);
}
if (no_active_sessions) { if (no_active_sessions) {
// We want to prepare display only if there are no active sessions at // We want to prepare display only if there are no active sessions at
// the moment. This should be done before probing encoders as it could // the moment. This should be done before probing encoders as it could

View file

@ -1972,9 +1972,9 @@ namespace stream {
{"keyboard_active", kb_ago <= KB_ACTIVE_WINDOW_MS}, {"keyboard_active", kb_ago <= KB_ACTIVE_WINDOW_MS},
{"mouse_active", mouse_ago <= MOUSE_ACTIVE_WINDOW_MS}, {"mouse_active", mouse_ago <= MOUSE_ACTIVE_WINDOW_MS},
{"gamepad_active", gp_ago <= GAMEPAD_ACTIVE_WINDOW_MS}, {"gamepad_active", gp_ago <= GAMEPAD_ACTIVE_WINDOW_MS},
{"last_keyboard_ms_ago", last_kb > 0 ? kb_ago : -1}, {"last_keyboard_ms_ago", last_kb > 0 ? (int64_t) kb_ago : (int64_t) -1},
{"last_mouse_ms_ago", last_mouse > 0 ? mouse_ago : -1}, {"last_mouse_ms_ago", last_mouse > 0 ? (int64_t) mouse_ago : (int64_t) -1},
{"last_gamepad_ms_ago", last_gp > 0 ? gp_ago : -1}, {"last_gamepad_ms_ago", last_gp > 0 ? (int64_t) gp_ago : (int64_t) -1},
}; };
result.push_back(std::move(entry)); result.push_back(std::move(entry));

@ -1 +1 @@
Subproject commit 546895a93a29062bb178367b46c7afb72da9881e Subproject commit 99c1f621ebd8d119c5d2dc3a88ecf255058acec0