custom auth support

This commit is contained in:
2026-01-08 14:32:59 -03:00
parent cf4f813145
commit 19bbd9894e
2 changed files with 264 additions and 24 deletions

View File

@@ -22,6 +22,9 @@
#include "core/internal_network/sockets.h"
#include "network/network.h"
#include <common/settings.h>
#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<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> 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<const char*>(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<u8> 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<const u8> new_message(
reinterpret_cast<const u8*>(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<int>(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<s32>(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<int>(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<int>(result.second));
return result;
}
std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr) {

View File

@@ -12,6 +12,7 @@
#include <openssl/x509.h>
#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<void(void)> &&f)
: exit_function(std::move(f)), execute_on_destruction{true} {}
explicit scope_exit(std::function<void(void)> &&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<void(void)> exit_function;
bool execute_on_destruction;
std::function<void(void)> 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<const u8> data) override {
const std::string_view data_view(reinterpret_cast<const char*>(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<u8> 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);
}