#include "prx/libc/include/General.hpp" #include "prx/libkernel/KernelErrors.hpp" #include "SceTypes.hpp" #include #include #include #include #include #include extern "C" { const char* APS5_VABI sceKernelGetFsSandboxRandomWord(); int APS5_VABI sceKernelOpen(const char*, int, unsigned short); int APS5_VABI sceKernelClose(int); std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t); int APS5_VABI sceKernelStat(const char*, FileStat*); void* APS5_VABI fopen_nid_postfix(const char*, const char*); std::size_t APS5_VABI fread_nid_postfix(void*, std::size_t, std::size_t, void*); int APS5_VABI fclose_nid_postfix(void*); } static void Check(bool value, int line) { if (!value) { std::fprintf(stderr, "Sandbox certificates check failed at line %d\n", line); std::abort(); } } #define Require(value) Check((value), __LINE__) static std::string ReadKernel(const std::string& path) { const int descriptor = sceKernelOpen(path.c_str(), 0, 0); Require(descriptor >= 0); std::string text; char buffer[4096]; for (std::int64_t count; (count = sceKernelRead(descriptor, buffer, sizeof(buffer))) != 0;) { Require(count > 0); text.append(buffer, static_cast(count)); } Require(sceKernelClose(descriptor) == 0); return text; } static std::string ReadStream(const std::string& path) { void* stream = fopen_nid_postfix(path.c_str(), "rb"); Require(stream != nullptr); std::string text; char buffer[4096]; for (std::size_t count; (count = fread_nid_postfix(buffer, 1, sizeof(buffer), stream)) != 0;) text.append(buffer, count); Require(fclose_nid_postfix(stream) == 0); return text; } static void CheckOverrides(const std::string& directory, const std::string& path) { const auto root = std::filesystem::current_path(); const auto supplied = root / std::filesystem::path(path).relative_path(); std::filesystem::create_directories(supplied.parent_path()); const std::string expected = "explicit guest certificate bundle\n"; { std::ofstream file(supplied, std::ios::binary | std::ios::trunc); file << expected; Require(static_cast(file)); } Require(ReadKernel(path) == expected); Require(ReadStream(path) == expected); Require(ResolvePath_nid_no_patch(path.c_str()) == supplied); Require(std::filesystem::remove(supplied)); for (auto parent = supplied.parent_path(); parent != root; parent = parent.parent_path()) Require(std::filesystem::remove(parent)); const auto alias = root / "sandbox_certificate_override"; std::filesystem::create_directory(alias); { std::ofstream file(alias / "CA_LIST.cer", std::ios::binary | std::ios::trunc); file << expected; Require(static_cast(file)); } AddPathAlias_nid_no_patch(directory.c_str(), alias.string().c_str()); Require(ReadKernel(path) == expected && ReadStream(path) == expected); BlockPathAlias_nid_no_patch(directory.c_str()); Require(sceKernelOpen(path.c_str(), 0, 0) == SCE_KERNEL_ERROR_ENOENT); Require(fopen_nid_postfix(path.c_str(), "rb") == nullptr); AddPathAlias_nid_no_patch(directory.c_str(), alias.string().c_str()); RemovePathAlias_nid_no_patch(directory.c_str()); Require(std::filesystem::remove(alias / "CA_LIST.cer")); Require(std::filesystem::remove(alias)); } int main() { #ifndef _WIN32 const auto bundle = std::filesystem::absolute("host_bundle.pem"); const std::string expected = "-----BEGIN CERTIFICATE-----\nMIIBAA==\n-----END CERTIFICATE-----\n"; { std::ofstream file(bundle, std::ios::binary | std::ios::trunc); file << expected; } Require(setenv("SSL_CERT_FILE", bundle.c_str(), 1) == 0); #endif const std::string directory = std::string("/") + sceKernelGetFsSandboxRandomWord() + "/common/cert"; const std::string path = directory + "/CA_LIST.cer"; CheckOverrides(directory, path); const auto text = ReadKernel(path); const auto begin = text.find("-----BEGIN CERTIFICATE-----"); Require(begin != std::string::npos); Require(text.find("-----END CERTIFICATE-----", begin) != std::string::npos); #ifndef _WIN32 Require(text == expected); #endif Require(ReadStream(path) == text); Require(ReadKernel(directory + "/./ca_list.cer") == text); FileStat status{}; Require(sceKernelStat(path.c_str(), &status) == 0); Require(status.st_size == static_cast(text.size())); const auto cached = std::filesystem::current_path() / "cert_cache" / "CA_LIST.cer"; Require(std::filesystem::is_regular_file(cached)); Require((std::filesystem::status(cached).permissions() & std::filesystem::perms::owner_write) == std::filesystem::perms::none); Require(sceKernelOpen((directory + "/CA_LIST2.cer").c_str(), 0, 0) == SCE_KERNEL_ERROR_ENOENT); Require(sceKernelOpen((directory + "/CA_LIST.cer.bak").c_str(), 0, 0) == SCE_KERNEL_ERROR_ENOENT); Require(fopen_nid_postfix((directory + "/CA_LIST2.cer").c_str(), "rb") == nullptr); return 0; }