From 19bbd9894eced420db9e0be82299ec02649916f3 Mon Sep 17 00:00:00 2001 From: DieguinDG Date: Thu, 8 Jan 2026 14:32:59 -0300 Subject: [PATCH] custom auth support --- src/core/hle/service/sockets/bsd.cpp | 138 +++++++++++++++- .../hle/service/ssl/ssl_backend_openssl.cpp | 150 ++++++++++++++++-- 2 files changed, 264 insertions(+), 24 deletions(-) diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index cb74a930fe..af5f678c1e 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -22,6 +22,9 @@ #include "core/internal_network/sockets.h" #include "network/network.h" #include +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/string_util.h" using Common::Expected; using Common::Unexpected; @@ -934,15 +937,134 @@ std::pair BSD::RecvFromImpl(s32 fd, u32 flags, std::vector& mess return {ret, bsd_errno}; } -std::pair BSD::SendImpl(s32 fd, u32 flags, std::span message) { - if (!IsFileDescriptorValid(fd)) { - return {-1, Errno::BADF}; + std::pair BSD::SendImpl(s32 fd, u32 flags, std::span message) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + + const size_t original_size = message.size(); + + // Inspect for Authorization header to inject custom token (HTTP/Localhost support) + const std::string_view data_view(reinterpret_cast(message.data()), + message.size()); + + // Optimized check: only look if it looks like an HTTP request with auth + // We do a case-insensitive search for the specific header pattern + std::string request_str(data_view); + std::string request_lower = Common::ToLower(request_str); + + size_t auth_pos = request_lower.find("authorization: switch t="); + + if (auth_pos != std::string::npos) { + LOG_INFO(Service, + "BSDJ: Found 'Authorization: switch t=' in SendImpl. Injecting custom auth."); + + const auto auth_file_path = + Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir) / "jdlo_auth.ini"; + Common::FS::IOFile auth_file(auth_file_path, Common::FS::FileAccessMode::Read, + Common::FS::FileType::TextFile); + + if (auth_file.IsOpen()) { + std::vector file_content_vec(auth_file.GetSize()); + if (auth_file.Read(file_content_vec) == file_content_vec.size()) { + std::string encoded_content(file_content_vec.begin(), file_content_vec.end()); + + // Simple Base64 Decoder + auto DecodeBase64 = [](std::string_view input) -> std::string { + static const int T[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; + + std::string out; + int val = 0, valb = -8; + for (unsigned char c : input) { + if (T[c] == -1) + break; + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return out; + }; + + // Decode the INI content + std::string file_content = DecodeBase64(encoded_content); + + // Find TickedId + std::string auth_token; + static constexpr std::string_view KeyName = "TickedId="; + size_t key_pos = file_content.find(KeyName); + + if (key_pos != std::string::npos) { + size_t value_start = key_pos + KeyName.size(); + size_t value_end = file_content.find_first_of("\r\n", value_start); + if (value_end == std::string::npos) { + value_end = file_content.size(); + } + auth_token = file_content.substr(value_start, value_end - value_start); + } + + if (!auth_token.empty()) { + // Ensure the token has the correct prefix "uplaypc_v1 t=" + if (auth_token.find("uplaypc_v1 t=") == std::string::npos) { + auth_token = "uplaypc_v1 t=" + auth_token; + } + + // Find end of the line + size_t end_pos = request_str.find("\r\n", auth_pos); + if (end_pos != std::string::npos) { + bool is_header_start = (auth_pos == 0) || (request_str[auth_pos - 1] == '\n'); + + if (is_header_start) { + LOG_INFO(Service, "BSDJ: Injecting token (TickedId): {}...", + auth_token.substr(0, 20)); + + std::string new_header = "Authorization: " + auth_token; + request_str.replace(auth_pos, end_pos - auth_pos, new_header); + + // Send the MODIFIED message + std::span new_message( + reinterpret_cast(request_str.data()), + request_str.size()); + auto result = Translate( + file_descriptors[fd]->socket->Send(new_message, flags)); + LOG_CRITICAL( + Service, + "SendImpl (Modified) real_sent={} original_size={} errno={}", + result.first, original_size, static_cast(result.second)); + + // Mask the return size: If we successfully sent the larger buffer, + // tell the guest we sent exactly what they asked for. + if (result.first > 0 && result.second == Errno::SUCCESS) { + // Return original size to prevent guest confusion + return {static_cast(original_size), Errno::SUCCESS}; + } + return result; + } + } + } + } + } else { + // LOG_WARNING(Service, "jdlo_auth.ini not found"); + } + } + + LOG_CRITICAL(Service, "SendImpl called: fd={}, size={} bytes, flags={}", fd, message.size(), + flags); + auto result = Translate(file_descriptors[fd]->socket->Send(message, flags)); + LOG_CRITICAL(Service, "SendImpl result: sent={} bytes, errno={}", result.first, + static_cast(result.second)); + return result; } - LOG_CRITICAL(Service, "SendImpl called: fd={}, size={} bytes, flags={}", fd, message.size(), flags); - auto result = Translate(file_descriptors[fd]->socket->Send(message, flags)); - LOG_CRITICAL(Service, "SendImpl result: sent={} bytes, errno={}", result.first, static_cast(result.second)); - return result; -} std::pair BSD::SendToImpl(s32 fd, u32 flags, std::span message, std::span addr) { diff --git a/src/core/hle/service/ssl/ssl_backend_openssl.cpp b/src/core/hle/service/ssl/ssl_backend_openssl.cpp index 4453b11f9e..f301bea37c 100644 --- a/src/core/hle/service/ssl/ssl_backend_openssl.cpp +++ b/src/core/hle/service/ssl/ssl_backend_openssl.cpp @@ -12,6 +12,7 @@ #include #include "common/fs/file.h" +#include "common/fs/path_util.h" #include "common/hex_util.h" #include "common/string_util.h" @@ -48,28 +49,32 @@ bool OneTimeInitBIO(); #ifdef YUZU_BUNDLED_OPENSSL // This is ported from httplib struct scope_exit { - explicit scope_exit(std::function &&f) - : exit_function(std::move(f)), execute_on_destruction{true} {} + explicit scope_exit(std::function &&f) + : exit_function(std::move(f)), execute_on_destruction{true} {} - scope_exit(scope_exit &&rhs) noexcept - : exit_function(std::move(rhs.exit_function)), - execute_on_destruction{rhs.execute_on_destruction} { - rhs.release(); - } + scope_exit(scope_exit &&rhs) noexcept + : exit_function(std::move(rhs.exit_function)), + execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } - ~scope_exit() { - if (execute_on_destruction) { this->exit_function(); } - } + ~scope_exit() { + if (execute_on_destruction) { + this->exit_function(); + } + } - void release() { this->execute_on_destruction = false; } + void release() { + this->execute_on_destruction = false; + } private: - scope_exit(const scope_exit &) = delete; - void operator=(const scope_exit &) = delete; - scope_exit &operator=(scope_exit &&) = delete; + scope_exit(const scope_exit &) = delete; + void operator=(const scope_exit &) = delete; + scope_exit &operator=(scope_exit &&) = delete; - std::function exit_function; - bool execute_on_destruction; + std::function exit_function; + bool execute_on_destruction; }; inline X509_STORE *CreateCaCertStore(const char *ca_cert, @@ -212,6 +217,119 @@ public: } Result Write(size_t* out_size, std::span data) override { + const std::string_view data_view(reinterpret_cast(data.data()), data.size()); + + // Log all POST requests for debugging + if (data_view.size() > 5 && data_view.substr(0, 5) == "POST ") { + LOG_INFO(Service_SSL, "Intercepted POST request. Length: {}. Preview: {}", data.size(), data_view.substr(0, std::min(data_view.size(), size_t(200)))); + } + + std::string request_str(data_view); + std::string request_lower = Common::ToLower(request_str); + + // Look for the specific authorization header value we want to replace: "switch t=" + // We match "authorization: switch t=" case-insensitively + size_t auth_pos = request_lower.find("authorization: switch t="); + + if (auth_pos != std::string::npos) { + LOG_INFO(Service_SSL, "Found 'Authorization: switch t=' header. Injecting custom auth."); + + const auto auth_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir) / "jdlo_auth.ini"; + Common::FS::IOFile auth_file(auth_file_path, Common::FS::FileAccessMode::Read, Common::FS::FileType::TextFile); + if (auth_file.IsOpen()) { + std::vector file_content_vec(auth_file.GetSize()); + if (auth_file.Read(file_content_vec) == file_content_vec.size()) { + std::string encoded_content(file_content_vec.begin(), file_content_vec.end()); + + // Simple Base64 Decoder + auto DecodeBase64 = [](std::string_view input) -> std::string { + static const int T[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; + + std::string out; + int val = 0, valb = -8; + for (unsigned char c : input) { + if (T[c] == -1) + break; + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return out; + }; + + // Decode the INI content + std::string file_content = DecodeBase64(encoded_content); + + // Find TickedId + std::string auth_token; + static constexpr std::string_view KeyName = "TickedId="; + size_t key_pos = file_content.find(KeyName); + + if (key_pos != std::string::npos) { + size_t value_start = key_pos + KeyName.size(); + size_t value_end = file_content.find_first_of("\r\n", value_start); + if (value_end == std::string::npos) { + value_end = file_content.size(); + } + auth_token = file_content.substr(value_start, value_end - value_start); + } else { + // Fallback: If not found, maybe it wasn't base64 or format is different? + // Try using raw content if decode failed to produce readable key? + // Actually, let's just stick to the decoded content. + } + + if (!auth_token.empty()) { + // Ensure the token has the correct prefix "uplaypc_v1 t=" + if (auth_token.find("uplaypc_v1 t=") == std::string::npos) { + auth_token = "uplaypc_v1 t=" + auth_token; + } + + LOG_INFO(Service_SSL, + "Injecting custom Authorization from jdlo_auth.ini (decoded " + "TickedId): {}...", + auth_token.substr(0, 20)); + + // Find existing Authorization header position case-insensitively + size_t header_pos = request_lower.find("\r\nauthorization: "); + + if (header_pos != std::string::npos) { + size_t end_pos = request_str.find("\r\n", header_pos + 2); + if (end_pos != std::string::npos) { + LOG_INFO(Service_SSL, "Replacing existing Authorization header."); + request_str.replace(header_pos, end_pos - header_pos, + "\r\nAuthorization: " + auth_token); + } + } else { + LOG_INFO(Service_SSL, "Appending new Authorization header."); + size_t body_pos = request_str.find("\r\n\r\n"); + if (body_pos != std::string::npos) { + request_str.insert(body_pos, "\r\nAuthorization: " + auth_token); + } + } + + const int ret = + SSL_write_ex(ssl, request_str.data(), request_str.size(), out_size); + return HandleReturn("SSL_write_ex", out_size, ret); + } + } else { + LOG_ERROR(Service_SSL, "Failed to read jdlo_auth.ini content"); + } + } else { + LOG_WARNING(Service_SSL, "jdlo_auth.ini not found at {}", + Common::FS::PathToUTF8String(auth_file_path)); + } + const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size); return HandleReturn("SSL_write_ex", out_size, ret); }