diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index fb2265f22d7..f0e94b445cd 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -47,11 +47,33 @@ struct SslCaList { }; static_assert(sizeof(SslCaList) == 16); +using SslVerifyCallback = int (APS5_VABI *)(int, unsigned int, void**, int, void*); + +struct SslConnection { + int context; + int socket; + std::string host; + SslVerifyCallback verifyCallback; + void* verifyArgument; +}; + +std::map g_connections; + void RequireContext(const char* function, int sslCtxId) { std::lock_guard lock(g_poolsMutex); if (!g_pools.contains(sslCtxId)) throw std::invalid_argument(std::string(function) + ": unknown context"); } +SslConnection& RequireConnection(const char* function, int sslConnectionId) { + const auto connection = g_connections.find(sslConnectionId); + if (connection == g_connections.end()) throw std::invalid_argument(std::string(function) + ": unknown connection"); + if (!g_pools.contains(connection->second.context)) { + throw std::invalid_argument(std::string(function) + ": the connection's context was terminated"); + } + return connection->second; +} + + } extern "C" { @@ -97,6 +119,53 @@ int APS5_VABI sceSslTerm_nid_postfix(int ssl_ctx_id) { return 0; } +int APS5_VABI sceSslCreateConnection(int sslCtxId, int sockId, const char* host) { + if (!host) throw std::invalid_argument("sceSslCreateConnection: null host"); + std::lock_guard lock(g_poolsMutex); + if (!g_pools.contains(sslCtxId)) return ERROR_INVALID_ID; + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + g_connections[id] = {sslCtxId, sockId, host, nullptr, nullptr}; + return id; +} + +int APS5_VABI sceSslDeleteConnection(int sslConnectionId) { + std::lock_guard lock(g_poolsMutex); + if (!g_connections.erase(sslConnectionId)) throw std::invalid_argument("sceSslDeleteConnection: unknown connection"); + return 0; +} + +int APS5_VABI sceSslSetVerifyCallback(int sslConnectionId, SslVerifyCallback cbfunc, void* userArg) { + std::lock_guard lock(g_poolsMutex); + SslConnection& connection = RequireConnection(__func__, sslConnectionId); + connection.verifyCallback = cbfunc; + connection.verifyArgument = userArg; + return 0; +} + +int APS5_VABI sceSslConnect(int sslConnectionId) { + (void)sslConnectionId; + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslRead(int sslConnectionId, void* buf, size_t len, int flags) { + (void)sslConnectionId; + (void)buf; + (void)len; + (void)flags; + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslWrite(int sslConnectionId, void* buf, size_t len, int flags) { + (void)sslConnectionId; + (void)buf; + (void)len; + (void)flags; + NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI sceSslClose() { NotImplemented_nid_no_patch(__func__); return 0; @@ -179,39 +248,9 @@ int APS5_VABI sceSslGetSubjectName(void) { return 0; } -int APS5_VABI sceSslCreateConnection(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslDeleteConnection(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceSslEnableVerifyOption(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSslSetVerifyCallback(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslConnect(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslRead(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceSslWrite(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - } diff --git a/core/libs/tests/GuestSsl.cpp b/core/libs/tests/GuestSsl.cpp index 4d89668ab62..1046e7ee55f 100644 --- a/core/libs/tests/GuestSsl.cpp +++ b/core/libs/tests/GuestSsl.cpp @@ -12,7 +12,12 @@ int APS5_VABI sceSslFreeCaCerts(int, void*); int APS5_VABI sceSslGetCaList(int, void*); int APS5_VABI sceSslFreeCaList(int, void*); int APS5_VABI sceSslUnloadCert(int); -int APS5_VABI sceSslSetVerifyCallback(void); +int APS5_VABI sceSslCreateConnection(int, int, const char*); +int APS5_VABI sceSslDeleteConnection(int); +int APS5_VABI sceSslSetVerifyCallback(int, int (APS5_VABI *)(int, unsigned int, void**, int, void*), void*); +int APS5_VABI sceSslConnect(int); +int APS5_VABI sceSslRead(int, void*, std::size_t, int); +int APS5_VABI sceSslWrite(int, void*, std::size_t, int); } struct SslMemoryPoolStats { @@ -36,6 +41,8 @@ static bool Throws(TCall call) { return false; } +static int APS5_VABI VerifyCallback(int, unsigned int, void**, int, void*) { return 0; } + struct SslCaCerts { void* certs; std::size_t num; @@ -51,8 +58,6 @@ int main() { constexpr int notFound = static_cast(0x8095F004); constexpr int invalidArg = static_cast(0x8095177A); int marker = 0; - Require(Throws([] { sceSslSetVerifyCallback(); })); - const int context = sceSslInit_nid_postfix(0x10000); Require(context > 0); SslMemoryPoolStats stats{1, 1, 1, 1}; @@ -90,7 +95,29 @@ int main() { Require(sceSslGetCaList(context, &list) == notFound); Require(list.certs == nullptr && list.num == 0); + constexpr int invalidId = static_cast(0x8095F006); + Require(Throws([&] { sceSslCreateConnection(context, 0, nullptr); })); + Require(sceSslCreateConnection(context + 1000, 0, "example.com") == invalidId); + + const int connection = sceSslCreateConnection(context, 0, "example.com"); + Require(connection > 0 && connection != context); + Require(sceSslSetVerifyCallback(connection, VerifyCallback, &marker) == 0); + Require(sceSslSetVerifyCallback(connection, nullptr, nullptr) == 0); + char buffer[4]{}; + Require(Throws([&] { sceSslConnect(connection); })); + Require(Throws([&] { sceSslRead(connection, buffer, sizeof(buffer), 0); })); + Require(Throws([&] { sceSslWrite(connection, buffer, sizeof(buffer), 0); })); + + const int unknown = connection + 1000; + Require(Throws([&] { sceSslSetVerifyCallback(unknown, nullptr, nullptr); })); + Require(Throws([&] { sceSslDeleteConnection(unknown); })); + Require(sceSslTerm_nid_postfix(context) == 0); + Require(Throws([&] { sceSslSetVerifyCallback(connection, nullptr, nullptr); })); + Require(sceSslCreateConnection(context, 0, "example.com") == invalidId); + Require(sceSslDeleteConnection(connection) == 0); + Require(Throws([&] { sceSslDeleteConnection(connection); })); + Require(sceSslGetCaList(context, nullptr) == invalidArg); Require(sceSslFreeCaList(context, nullptr) == invalidArg); list = {&handle, 3}; diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 0a72ff2bfab..a789cb57295 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -2,6 +2,7 @@ ### Unknown function info +- [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. The repository has no TLS client, so a connection only records its context, socket, host and verification callback, and `sceSslConnect`, `sceSslRead` and `sceSslWrite` throw `NotImplemented_nid_no_patch(__func__)` until there is a transport. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006), as `sceSslGetMemoryPoolStats` does on the console and as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/4865dd92ca6b6bb907748f125883093af038f53b/src/libs/network.cpp#L2688-L2720) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/29021b5a71bbc80f262d887e6ba18b3a72963f49/src/SharpEmu.Libs/Network/SslExports.cs#L41-L54) return for other context calls; for an unknown connection, a connection whose context was terminated and a null host the console's codes are unknown, so these throw. The socket is not checked when the connection is created, `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, and `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0; none of this is verified on the console. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetSerialNumber](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetCaList, sceSslFreeCaList, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and the 16-byte `SceSslCaList` layout (an array of certificate handles and a 32-bit count) follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs) and shadPS4's `OrbisSslCaList`, not confirmed for the PS5. On the console the list holds the certificate authorities the SSL service has; [shadPS4](https://github.com/shadps4-emu/shadPS4/pull/4104) returns a placeholder certificate from `sceSslGetCaCerts` because Rise of the Tomb Raider fails its initialization checks without one. No root certificate bundle is shipped and no loaded certificate is kept, so here a context holds none: `sceSslGetCaList` clears the list and returns `NOT_FOUND`, as `sceSslGetCaCerts` does, `sceSslFreeCaList` accepts only that cleared list, and `sceSslUnloadCert` (imported by PPSA15508) has nothing to drop and is assumed to return 0. A null list returns `0x8095177A`, which `sceSslGetCaCerts` and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/59183dbfa81ed7efdff6069177fed50de0552725/src/libs/network.cpp) return for a null argument (shadPS4, PS4, returns `0x8095F007`). An unknown context throws (KytyPS5 returns `SSL_ERROR_INVALID_ID`, `0x8095F006`, from `sceSslGetCaCerts`), and so does a list `sceSslGetCaList` did not return @@ -9,4 +10,4 @@ ### Functional -- PPSA07411 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceSslCreateConnection, sceSslDeleteConnection, sceSslEnableVerifyOption, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl), [four sce::Json::Value members (setElementAccessFailureHandler, setSpecialFloatHandler, setTypeMismatchHandler, operator bool)](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2), [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) and [sceNpSessionSignalingGetGroupFromSessionId](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) +- PPSA07411 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceSslEnableVerifyOption](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl), [four sce::Json::Value members (setElementAccessFailureHandler, setSpecialFloatHandler, setTypeMismatchHandler, operator bool)](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2), [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) and [sceNpSessionSignalingGetGroupFromSessionId](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling)