custom auth support
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user