From 62fb4c6b371850bbf4c3844510772864300a4fb8 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Mon, 13 Apr 2020 22:58:27 -0400 Subject: [PATCH] Added initial support for the ssl service. --- nx/include/switch.h | 1 + nx/include/switch/services/ssl.h | 407 +++++++++++++++++++++++++ nx/source/services/ssl.c | 496 +++++++++++++++++++++++++++++++ 3 files changed, 904 insertions(+) create mode 100644 nx/include/switch/services/ssl.h create mode 100644 nx/source/services/ssl.c diff --git a/nx/include/switch.h b/nx/include/switch.h index 03043d60..570b2c5e 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -90,6 +90,7 @@ extern "C" { #include "switch/services/ts.h" #include "switch/services/pm.h" #include "switch/services/set.h" +#include "switch/services/ssl.h" #include "switch/services/lr.h" #include "switch/services/spl.h" #include "switch/services/ncm.h" diff --git a/nx/include/switch/services/ssl.h b/nx/include/switch/services/ssl.h new file mode 100644 index 00000000..a6594e4c --- /dev/null +++ b/nx/include/switch/services/ssl.h @@ -0,0 +1,407 @@ +/** + * @file fs.h + * @brief SSL service IPC wrapper. + * @author yellows8 + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../sf/service.h" + +/// CaCertificateId +typedef enum { + SslCaCertificateId_All = -1, ///< All + + SslCaCertificateId_NintendoCAG3 = 1, ///< NintendoCAG3 + SslCaCertificateId_NintendoClass2CAG3 = 2, ///< NintendoClass2CAG3 + + SslCaCertificateId_AmazonRootCA1 = 1000, ///< AmazonRootCA1 + SslCaCertificateId_StarfieldServicesRootCertificateAuthorityG2 = 1001, ///< StarfieldServicesRootCertificateAuthorityG2 + SslCaCertificateId_AddTrustExternalCARoot = 1002, ///< AddTrustExternalCARoot + SslCaCertificateId_COMODOCertificationAuthority = 1003, ///< COMODOCertificationAuthority + SslCaCertificateId_UTNDATACorpSGC = 1004, ///< UTNDATACorpSGC + SslCaCertificateId_UTNUSERFirstHardware = 1005, ///< UTNUSERFirstHardware + SslCaCertificateId_BaltimoreCyberTrustRoot = 1006, ///< BaltimoreCyberTrustRoot + SslCaCertificateId_CybertrustGlobalRoot = 1007, ///< CybertrustGlobalRoot + SslCaCertificateId_VerizonGlobalRootCA = 1008, ///< VerizonGlobalRootCA + SslCaCertificateId_DigiCertAssuredIDRootCA = 1009, ///< DigiCertAssuredIDRootCA + SslCaCertificateId_DigiCertAssuredIDRootG2 = 1010, ///< DigiCertAssuredIDRootG2 + SslCaCertificateId_DigiCertGlobalRootCA = 1011, ///< DigiCertGlobalRootCA + SslCaCertificateId_DigiCertGlobalRootG2 = 1012, ///< DigiCertGlobalRootG2 + SslCaCertificateId_DigiCertHighAssuranceEVRootCA = 1013, ///< DigiCertHighAssuranceEVRootCA + SslCaCertificateId_EntrustnetCertificationAuthority2048 = 1014, ///< EntrustnetCertificationAuthority2048 + SslCaCertificateId_EntrustRootCertificationAuthority = 1015, ///< EntrustRootCertificationAuthority + SslCaCertificateId_EntrustRootCertificationAuthorityG2 = 1016, ///< EntrustRootCertificationAuthorityG2 + SslCaCertificateId_GeoTrustGlobalCA2 = 1017, ///< GeoTrustGlobalCA2 + SslCaCertificateId_GeoTrustGlobalCA = 1018, ///< GeoTrustGlobalCA + SslCaCertificateId_GeoTrustPrimaryCertificationAuthorityG3 = 1019, ///< GeoTrustPrimaryCertificationAuthorityG3 + SslCaCertificateId_GeoTrustPrimaryCertificationAuthority = 1020, ///< GeoTrustPrimaryCertificationAuthority + SslCaCertificateId_GlobalSignRootCA = 1021, ///< GlobalSignRootCA + SslCaCertificateId_GlobalSignRootCAR2 = 1022, ///< GlobalSignRootCAR2 + SslCaCertificateId_GlobalSignRootCAR3 = 1023, ///< GlobalSignRootCAR3 + SslCaCertificateId_GoDaddyClass2CertificationAuthority = 1024, ///< GoDaddyClass2CertificationAuthority + SslCaCertificateId_GoDaddyRootCertificateAuthorityG2 = 1025, ///< GoDaddyRootCertificateAuthorityG2 + SslCaCertificateId_StarfieldClass2CertificationAuthority = 1026, ///< StarfieldClass2CertificationAuthority + SslCaCertificateId_StarfieldRootCertificateAuthorityG2 = 1027, ///< StarfieldRootCertificateAuthorityG2 + SslCaCertificateId_thawtePrimaryRootCAG3 = 1028, ///< thawtePrimaryRootCAG3 + SslCaCertificateId_thawtePrimaryRootCA = 1029, ///< thawtePrimaryRootCA + SslCaCertificateId_VeriSignClass3PublicPrimaryCertificationAuthorityG3 = 1030, ///< VeriSignClass3PublicPrimaryCertificationAuthorityG3 + SslCaCertificateId_VeriSignClass3PublicPrimaryCertificationAuthorityG5 = 1031, ///< VeriSignClass3PublicPrimaryCertificationAuthorityG5 + SslCaCertificateId_VeriSignUniversalRootCertificationAuthority = 1032, ///< VeriSignUniversalRootCertificationAuthority + SslCaCertificateId_DSTRootCAX3 = 1033, ///< DSTRootCAX3 +} SslCaCertificateId; + +/// TrustedCertStatus +typedef enum { + SslTrustedCertStatus_Invalid = -1, ///< Invalid + SslTrustedCertStatus_Removed = 0, ///< Removed + SslTrustedCertStatus_EnabledTrusted = 1, ///< EnabledTrusted + SslTrustedCertStatus_EnabledNotTrusted = 2, ///< EnabledNotTrusted + SslTrustedCertStatus_Revoked = 3, ///< Revoked +} SslTrustedCertStatus; + +/// FlushSessionCacheOptionType +typedef enum { + SslFlushSessionCacheOptionType_SingleHost = 0, ///< SingleHost. Uses the input string. + SslFlushSessionCacheOptionType_AllHosts = 1, ///< AllHosts. Doesn't use the input string. +} SslFlushSessionCacheOptionType; + +/// DebugOptionType +typedef enum { + SslDebugOptionType_AllowDisableVerifyOption = 0, ///< AllowDisableVerifyOption +} SslDebugOptionType; + +/// SslVersion +typedef enum { + SslVersion_Auto = 0x1, ///< Auto + SslVersion_TlsV10 = 0x8, ///< TlsV10 + SslVersion_TlsV11 = 0x10, ///< TlsV11 + SslVersion_TlsV12 = 0x20, ///< TlsV12 +} SslVersion; + +/// CertificateFormat +typedef enum { + SslCertificateFormat_Pem = 1, ///< Pem + SslCertificateFormat_Der = 2, ///< Der +} SslCertificateFormat; + +/// InternalPki +typedef enum { + SslInternalPki_DeviceClientCertDefault = 1, ///< DeviceClientCertDefault +} SslInternalPki; + +/// ContextOption +typedef enum { + SslContextOption_CrlImportDateCheckEnable = 1, ///< CrlImportDateCheckEnable +} SslContextOption; + +/// VerifyOption +typedef enum { + SslVerifyOption_PeerCa = BIT(0), ///< PeerCa + SslVerifyOption_HostName = BIT(1), ///< HostName + SslVerifyOption_DateCheck = BIT(2), ///< DateCheck + SslVerifyOption_EvCertPartial = BIT(3), ///< EvCertPartial + SslVerifyOption_EvPolicyOid = BIT(4), ///< [6.0.0+] EvPolicyOid + SslVerifyOption_EvCertFingerprint = BIT(5), ///< [6.0.0+] EvCertFingerprint +} SslVerifyOption; + +/// IoMode +typedef enum { + SslIoMode_Blocking = 1, ///< Blocking + SslIoMode_NonBlocking = 2, ///< NonBlocking +} SslIoMode; + +/// PollEvent +typedef enum { + SslPollEvent_Read = BIT(0), ///< Read + SslPollEvent_Write = BIT(1), ///< Write + SslPollEvent_Except = BIT(2), ///< Except +} SslPollEvent; + +/// SessionCacheMode +typedef enum { + SslSessionCacheMode_None = 0, ///< None + SslSessionCacheMode_SessionId = 1, ///< SessionId + SslSessionCacheMode_SessionTicket = 2, ///< SessionTicket +} SslSessionCacheMode; + +/// RenegotiationMode +typedef enum { + SslRenegotiationMode_None = 0, ///< None + SslRenegotiationMode_Secure = 1, ///< Secure +} SslRenegotiationMode; + +/// OptionType +typedef enum { + SslOptionType_DoNotCloseSocket = 0, ///< DoNotCloseSocket + SslOptionType_GetServerCertChain = 1, ///< [3.0.0+] GetServerCertChain + SslOptionType_SkipDefaultVerify = 2, ///< [5.0.0+] SkipDefaultVerify + SslOptionType_EnableAlpn = 3, ///< [9.0.0+] EnableAlpn +} SslOptionType; + +/// AlpnProtoState +typedef enum { + SslAlpnProtoState_NoSupport = 0, ///< NoSupport + SslAlpnProtoState_Negotiated = 1, ///< Negotiated + SslAlpnProtoState_NoOverlap = 2, ///< NoOverlap + SslAlpnProtoState_Selected = 3, ///< Selected + SslAlpnProtoState_EarlyValue = 4, ///< EarlyValue +} SslAlpnProtoState; + +/// SslContext +typedef struct { + Service s; ///< ISslContext +} SslContext; + +/// SslConnection +typedef struct { + Service s; ///< ISslConnection + int sockfd; ///< sockfd returned by the SetSocketDescriptor cmd. +} SslConnection; + +/// BuiltInCertificateInfo +typedef struct { + u32 cert_id; ///< CaCertificateId + u32 status; ///< \ref SslTrustedCertStatus + u64 cert_size; ///< CertificateSize + u8 *cert_data; ///< CertificateData (converted from an offset to a ptr), in DER format. +} SslBuiltInCertificateInfo; + +/// Initialize ssl. A default value of 0x3 can be used for num_sessions. This must be 0x1-0x4. +Result sslInitialize(u32 num_sessions); + +/// Exit ssl. +void sslExit(void); + +/// Gets the Service object for the actual ssl service session. +Service* sslGetServiceSession(void); + +/** + * @brief CreateContext + * @param[out] c \ref SslContext + * @param[in] ssl_version \ref SslVersion + */ +Result sslCreateContext(SslContext *c, SslVersion ssl_version); + +/** + * @brief GetContextCount + * @note Not used by official sw. + * @param[out] out Output value. + */ +Result sslGetContextCount(u32 *out); + +/** + * @brief GetCertificates + * @param[in] buffer Output buffer. The start of this buffer is an array of \ref SslBuiltInCertificateInfo, with the specified count. The cert data (SslBuiltInCertificateInfo::data) is located after this array. + * @param[in] size Output buffer size, this should be the size from \ref sslGetCertificateBufSize. + * @param[in] ca_cert_ids Input array of \ref SslCaCertificateId. + * @param[in] count Size of the ca_cert_ids array in entries. + */ +Result sslGetCertificates(void* buffer, u32 size, u32 *ca_cert_ids, u32 count); + +/** + * @brief GetCertificateBufSize + * @param[in] ca_cert_ids Input array of \ref SslCaCertificateId. + * @param[in] count Size of the ca_cert_ids array in entries. + * @param[out] out Output size. + */ +Result sslGetCertificateBufSize(u32 *ca_cert_ids, u32 count, u32 *out); + +/** + * @brief FlushSessionCache + * @note Only available on [5.0.0+]. + * @param[in] str Input string. Must be NULL with ::SslFlushSessionCacheOptionType_AllHosts. + * @param[in] str_bufsize String buffer size, excluding NUL-terminator. Hence, this should be actual_bufsize-1. This must be 0 with ::SslFlushSessionCacheOptionType_AllHosts. + * @param[in] type \ref SslFlushSessionCacheOptionType + * @param[out] out Output value. + */ +Result sslFlushSessionCache(const char *str, size_t str_bufsize, SslFlushSessionCacheOptionType type, u32 *out); + +/** + * @brief SetDebugOption + * @note Only available on [6.0.0+]. + * @note The official impl of this doesn't actually use the cmd. + * @param[in] buffer Input buffer, must not be NULL. The u8 from here is copied to state. + * @param[in] size Buffer size, must not be 0. + * @param[in] type \ref SslDebugOptionType + */ +Result sslSetDebugOption(const void* buffer, size_t size, SslDebugOptionType type); + +/** + * @brief GetDebugOption + * @note Only available on [6.0.0+]. + * @param[out] buffer Output buffer, must not be NULL. An u8 is written here loaded from state. + * @param[in] size Buffer size, must not be 0. + * @param[in] type \ref SslDebugOptionType + */ +Result sslGetDebugOption(void* buffer, size_t size, SslDebugOptionType type); + +///@name ISslContext +///@{ + +/** + * @brief Closes a Context object. + * @param c \ref SslContext + */ +void sslContextClose(SslContext *c); + +/** + * @brief SetOption + * @note Prior to 4.x this is stubbed. + * @param c \ref SslContext + * @param[in] option \ref SslContextOption + * @param[in] value Value to set. With ::SslContextOption_CrlImportDateCheckEnable, this must be 0 or 1. + */ +Result sslContextSetOption(SslContext *c, SslContextOption option, s32 value); + +/** + * @brief GetOption + * @note Prior to 4.x this is stubbed. + * @param c \ref SslContext + * @param[in] option \ref SslContextOption + * @param[out] out Output value. + */ +Result sslContextGetOption(SslContext *c, SslContextOption option, s32 *out); + +/** + * @brief CreateConnection + * @param c \ref SslContext + * @param[out] conn Output \ref SslConnection. + */ +Result sslContextCreateConnection(SslContext *c, SslConnection *conn); + +/** + * @brief GetConnectionCount + * @note Not used by official sw. + * @param c \ref SslContext + * @param[out] out Output value. + */ +Result sslContextGetConnectionCount(SslContext *c, u32 *out); + +/** + * @brief ImportServerPki + * @param c \ref SslContext + * @param[in] buffer Input buffer, must not be NULL. + * @param[in] size Input buffer size. + * @param[in] format \ref SslCertificateFormat + * @param[out] id Output Id. + */ +Result sslContextImportServerPki(SslContext *c, const void* buffer, u32 size, SslCertificateFormat format, u64 *id); + +/** + * @brief ImportClientPki + * @param c \ref SslContext + * @param[in] buf0 First input buffer, must not be NULL. + * @param[in] size0 First input buffer size. + * @param[in] buf1 Second input buffer, this can only be NULL if size1 is 0. + * @param[in] size1 Second input buffer size, this can only be 0 if buf1 is NULL. + * @param[out] id Output Id. + */ +Result sslContextImportClientPki(SslContext *c, const void* buf0, u32 size0, const void* buf1, u32 size1, u64 *id); + +/** + * @brief Remove the specified *Pki, or on [3.0.0+] Crl. + * @param c \ref SslContext + * @param[in] id Id + */ +Result sslContextRemovePki(SslContext *c, u64 id); + +/** + * @brief RegisterInternalPki + * @param c \ref SslContext + * @param[in] internal_pki \ref SslInternalPki + * @param[out] id Output Id. + */ +Result sslContextRegisterInternalPki(SslContext *c, SslInternalPki internal_pki, u64 *id); + +/** + * @brief AddPolicyOid + * @param c \ref SslContext + * @param[in] str Input string. + * @param[in] str_bufsize String buffer size, excluding NUL-terminator. Hence, this should be actual_bufsize-1. This must not be >0xff. + */ +Result sslContextAddPolicyOid(SslContext *c, const char* str, u32 str_bufsize); + +/** + * @brief ImportCrl + * @note Only available on [3.0.0+]. + * @param c \ref SslContext + * @param[in] buffer Input buffer, must not be NULL. + * @param[in] size Input buffer size. + * @param[out] id Output Id. + */ +Result sslContextImportCrl(SslContext *c, const void* buffer, u32 size, u64 *id); + +///@} + +///@name ISslConnection +///@{ + +/** + * @brief Closes a Connection object. + * @note This will use close() with the sockfd previously set by \ref sslConnectionSetSocketDescriptor if needed, hence sockets must have been initialized prior to using this. + * @param c \ref SslConnection + */ +void sslConnectionClose(SslConnection *c); + +/** + * @brief SetSocketDescriptor + * @param c \ref SslConnection + * @param[in] sockfd sockfd + */ +Result sslConnectionSetSocketDescriptor(SslConnection *c, int sockfd); + +/** + * @brief SetHostName + * @param c \ref SslConnection + * @param[in] str Input string. + * @param[in] str_bufsize String buffer size. This must not be >0xff. + */ +Result sslConnectionSetHostName(SslConnection *c, const char* str, u32 str_bufsize); + +/** + * @brief SetVerifyOption + * @param c \ref SslConnection + * @param[in] verify_option Input bitmask of \ref SslVerifyOption. + */ +Result sslConnectionSetVerifyOption(SslConnection *c, u32 verify_option); + +/** + * @brief SetIoMode + * @param c \ref SslConnection + * @param[in] mode \ref SslIoMode + */ +Result sslConnectionSetIoMode(SslConnection *c, SslIoMode mode); + +/** + * @brief GetSocketDescriptor + * @param c \ref SslConnection + * @param[out] sockfd Output sockfd. + */ +Result sslConnectionGetSocketDescriptor(SslConnection *c, int *sockfd); + +/** + * @brief GetHostName + * @param c \ref SslConnection + * @param[out] str Output string buffer. + * @param[in] str_bufsize String buffer size, must be large enough for the entire output string. + * @param[out] out Output string length. + */ +Result sslConnectionGetHostName(SslConnection *c, char* str, u32 str_bufsize, u32 *out); + +/** + * @brief SetSessionCacheMode + * @param c \ref SslConnection + * @param[in] mode \ref SslSessionCacheMode + */ +Result sslConnectionSetSessionCacheMode(SslConnection *c, SslSessionCacheMode mode); + +/** + * @brief SetRenegotiationMode + * @param c \ref SslConnection + * @param[in] mode \ref SslRenegotiationMode + */ +Result sslConnectionSetRenegotiationMode(SslConnection *c, SslRenegotiationMode mode); + +///@} + diff --git a/nx/source/services/ssl.c b/nx/source/services/ssl.c new file mode 100644 index 00000000..75a2db1b --- /dev/null +++ b/nx/source/services/ssl.c @@ -0,0 +1,496 @@ +#include +#include +#include "service_guard.h" +#include "sf/sessionmgr.h" +#include "runtime/hosversion.h" +#include "services/ssl.h" + +static Service g_sslSrv; +static SessionMgr g_sslSessionMgr; + +static Result _sslSetInterfaceVersion(u32 version); + +NX_INLINE bool _sslObjectIsChild(Service* s) +{ + return s->session == g_sslSrv.session; +} + +static void _sslObjectClose(Service* s) { + if (!_sslObjectIsChild(s)) { + serviceClose(s); + } + else { + int slot = sessionmgrAttachClient(&g_sslSessionMgr); + uint32_t object_id = serviceGetObjectId(s); + serviceAssumeDomain(s); + cmifMakeCloseRequest(armGetTls(), object_id); + svcSendSyncRequest(sessionmgrGetClientSession(&g_sslSessionMgr, slot)); + sessionmgrDetachClient(&g_sslSessionMgr, slot); + } +} + +NX_INLINE Result _sslObjectDispatchImpl( + Service* s, u32 request_id, + const void* in_data, u32 in_data_size, + void* out_data, u32 out_data_size, + SfDispatchParams disp +) { + int slot = -1; + if (_sslObjectIsChild(s)) { + slot = sessionmgrAttachClient(&g_sslSessionMgr); + if (slot < 0) __builtin_unreachable(); + disp.target_session = sessionmgrGetClientSession(&g_sslSessionMgr, slot); + serviceAssumeDomain(s); + } + + Result rc = serviceDispatchImpl(s, request_id, in_data, in_data_size, out_data, out_data_size, disp); + + if (slot >= 0) { + sessionmgrDetachClient(&g_sslSessionMgr, slot); + } + + return rc; +} + +#define _sslObjectDispatch(_s,_rid,...) \ + _sslObjectDispatchImpl((_s),(_rid),NULL,0,NULL,0,(SfDispatchParams){ __VA_ARGS__ }) + +#define _sslObjectDispatchIn(_s,_rid,_in,...) \ + _sslObjectDispatchImpl((_s),(_rid),&(_in),sizeof(_in),NULL,0,(SfDispatchParams){ __VA_ARGS__ }) + +#define _sslObjectDispatchOut(_s,_rid,_out,...) \ + _sslObjectDispatchImpl((_s),(_rid),NULL,0,&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ }) + +#define _sslObjectDispatchInOut(_s,_rid,_in,_out,...) \ + _sslObjectDispatchImpl((_s),(_rid),&(_in),sizeof(_in),&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ }) + +NX_GENERATE_SERVICE_GUARD_PARAMS(ssl, (u32 num_sessions), (num_sessions)); + +Result _sslInitialize(u32 num_sessions) { + if (num_sessions > 4) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + Result rc = smGetService(&g_sslSrv, "ssl"); + + if (R_SUCCEEDED(rc)) { + rc = serviceConvertToDomain(&g_sslSrv); + } + + if (hosversionAtLeast(3,0,0)) { + u32 version = 0x1; + + if (hosversionAtLeast(5,0,0)) + version = 0x2; + if (hosversionAtLeast(6,0,0)) + version = 0x3; + + rc = _sslSetInterfaceVersion(version); + } + + if (R_SUCCEEDED(rc)) + rc = sessionmgrCreate(&g_sslSessionMgr, g_sslSrv.session, num_sessions); + + return rc; +} + +void _sslCleanup(void) { + // Close extra sessions + sessionmgrClose(&g_sslSessionMgr); + + // We can't assume g_sslSrv is a domain here because serviceConvertToDomain might have failed + serviceClose(&g_sslSrv); +} + +Service* sslGetServiceSession(void) { + return &g_sslSrv; +} + +static Result _sslCmdInU32NoOut(Service* srv, u32 inval, u32 cmd_id) { + return _sslObjectDispatchIn(srv, cmd_id, inval); +} + +static Result _sslCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) { + return _sslObjectDispatchOut(srv, cmd_id, *out); +} + +Result sslCreateContext(SslContext *c, SslVersion ssl_version) { + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + const struct { + u32 ssl_version; + u32 pad; + u64 pid_placeholder; + } in = { ssl_version, 0, 0 }; + + return _sslObjectDispatchIn(&g_sslSrv, 0, in, + .in_send_pid = true, + .out_num_objects = 1, + .out_objects = &c->s, + ); +} + +Result sslGetContextCount(u32 *out) { + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdNoInOutU32(&g_sslSrv, out, 1); +} + +static Result _sslGetCertificates(void* buffer, u32 size, u32 *ca_cert_ids, u32 count, u32 *out) { + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (hosversionBefore(3,0,0)) { + Result rc = _sslObjectDispatch(&g_sslSrv, 2, + .buffer_attrs = { + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out, + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + }, + .buffers = { + { buffer, size }, + { ca_cert_ids, count*sizeof(*ca_cert_ids) }, + }, + ); + if (R_SUCCEEDED(rc) && out) *out = count; + return rc; + } + + return _sslObjectDispatchOut(&g_sslSrv, 2, *out, + .buffer_attrs = { + SfBufferAttr_HipcMapAlias | SfBufferAttr_Out, + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + }, + .buffers = { + { buffer, size }, + { ca_cert_ids, count*sizeof(*ca_cert_ids) }, + }, + ); +} + +Result sslGetCertificates(void* buffer, u32 size, u32 *ca_cert_ids, u32 count) { + if (buffer==NULL || !size || ca_cert_ids==NULL || !count) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + u32 out_count=0; + Result rc = _sslGetCertificates(buffer, size, ca_cert_ids, count, &out_count); + + if (R_SUCCEEDED(rc)) { + SslBuiltInCertificateInfo *certs = (SslBuiltInCertificateInfo*)buffer; + for (u32 i=0; i= (uintptr_t)size || certs[i].cert_size >= size || (uintptr_t)certs[i].cert_data + (uintptr_t)certs[i].cert_size > (uintptr_t)size) + return MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen); + + certs[i].cert_data+= (uintptr_t)buffer; + } + } + + return rc; +} + +Result sslGetCertificateBufSize(u32 *ca_cert_ids, u32 count, u32 *out) { + if (ca_cert_ids==NULL || !count) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslObjectDispatchOut(&g_sslSrv, 3, *out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { ca_cert_ids, count*sizeof(*ca_cert_ids) } }, + ); +} + +static Result _sslSetInterfaceVersion(u32 version) { // [3.0.0+] + serviceAssumeDomain(&g_sslSrv); + return serviceDispatchIn(&g_sslSrv, 5, version); +} + +static Result _sslFlushSessionCache(const char *str, size_t str_size, u32 type, u32 *out) { + if (hosversionBefore(5,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslObjectDispatchInOut(&g_sslSrv, 6, type, *out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { str, str_size } }, + ); +} + +Result sslFlushSessionCache(const char *str, size_t str_bufsize, SslFlushSessionCacheOptionType type, u32 *out) { + size_t str_size=0; + if (out) *out = 0; + + if (type == SslFlushSessionCacheOptionType_SingleHost) { + if (str==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + str_size = strnlen(str, str_bufsize); + + if (!str_size) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + str_size++; + } + else if (type == SslFlushSessionCacheOptionType_AllHosts) { + if (str || str_bufsize) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + else { + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + return _sslFlushSessionCache(str, str_size, type, out); +} + +Result sslSetDebugOption(const void* buffer, size_t size, SslDebugOptionType type) { + if (hosversionBefore(6,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (buffer==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + u32 tmp=type; + return _sslObjectDispatchIn(&g_sslSrv, 7, tmp, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { buffer, size } }, + ); +} + +Result sslGetDebugOption(void* buffer, size_t size, SslDebugOptionType type) { + if (hosversionBefore(6,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&g_sslSrv)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + u32 tmp=type; + return _sslObjectDispatchIn(&g_sslSrv, 8, tmp, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { buffer, size } }, + ); +} + +// ISslContext + +void sslContextClose(SslContext *c) { + _sslObjectClose(&c->s); +} + +Result sslContextSetOption(SslContext *c, SslContextOption option, s32 value) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + const struct { + u32 option; + s32 value; + } in = { option, value }; + + return _sslObjectDispatchIn(&c->s, 0, in); +} + +Result sslContextGetOption(SslContext *c, SslContextOption option, s32 *out) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + u32 tmp=option; + return _sslObjectDispatchInOut(&c->s, 1, tmp, *out); +} + +Result sslContextCreateConnection(SslContext *c, SslConnection *conn) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + memset(conn, 0, sizeof(*conn)); + conn->sockfd = -1; + + return _sslObjectDispatch(&c->s, 2, + .out_num_objects = 1, + .out_objects = &conn->s, + ); +} + +Result sslContextGetConnectionCount(SslContext *c, u32 *out) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdNoInOutU32(&c->s, out, 3); +} + +Result sslContextImportServerPki(SslContext *c, const void* buffer, u32 size, SslCertificateFormat format, u64 *id) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (buffer==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + u32 tmp = format; + return _sslObjectDispatchInOut(&c->s, 4, tmp, *id, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { buffer, size } }, + ); +} + +Result sslContextImportClientPki(SslContext *c, const void* buf0, u32 size0, const void* buf1, u32 size1, u64 *id) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (buf0==NULL || (buf1==NULL && size1) || (buf1!=NULL && !size1)) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + return _sslObjectDispatchOut(&c->s, 5, *id, + .buffer_attrs = { + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + SfBufferAttr_HipcMapAlias | SfBufferAttr_In, + }, + .buffers = { + { buf0, size0 }, + { buf1, size1 }, + }, + ); +} + +Result sslContextRemovePki(SslContext *c, u64 id) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + Result rc=0; + u32 cnt = hosversionBefore(3,0,0) ? 2 : 3; + + for (u32 i=0; is, cmd_id, id); + + if (i<2 && R_VALUE(rc) != MAKERESULT(123, 214)) break; + } + + return rc; +} + +Result sslContextRegisterInternalPki(SslContext *c, SslInternalPki internal_pki, u64 *id) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + u32 tmp = internal_pki; + return _sslObjectDispatchInOut(&c->s, 8, tmp, *id); +} + +Result sslContextAddPolicyOid(SslContext *c, const char* str, u32 str_bufsize) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (str==NULL || str_bufsize > 0xff) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + return _sslObjectDispatch(&c->s, 9, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { str, str_bufsize } }, + ); +} + +Result sslContextImportCrl(SslContext *c, const void* buffer, u32 size, u64 *id) { + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (buffer==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + return _sslObjectDispatchOut(&c->s, 10, *id, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { buffer, size } }, + ); +} + +// ISslConnection + +void sslConnectionClose(SslConnection *c) { + if (c->sockfd >= 0) close(c->sockfd); + _sslObjectClose(&c->s); + + memset(c, 0, sizeof(*c)); + c->sockfd = -1; +} + +Result sslConnectionSetSocketDescriptor(SslConnection *c, int sockfd) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslObjectDispatchInOut(&c->s, 0, sockfd, c->sockfd); +} + +Result sslConnectionSetHostName(SslConnection *c, const char* str, u32 str_bufsize) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (str==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + return _sslObjectDispatch(&c->s, 1, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { str, str_bufsize } }, + ); +} + +Result sslConnectionSetVerifyOption(SslConnection *c, u32 verify_option) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdInU32NoOut(&c->s, verify_option, 2); +} + +Result sslConnectionSetIoMode(SslConnection *c, SslIoMode mode) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdInU32NoOut(&c->s, mode, 3); +} + +Result sslConnectionGetSocketDescriptor(SslConnection *c, int *sockfd) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdNoInOutU32(&c->s, (u32*)sockfd, 4); +} + +Result sslConnectionGetHostName(SslConnection *c, char* str, u32 str_bufsize, u32 *out) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (str==NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + return _sslObjectDispatchOut(&c->s, 5, *out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { str, str_bufsize } }, + ); +} + +Result sslConnectionSetSessionCacheMode(SslConnection *c, SslSessionCacheMode mode) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdInU32NoOut(&c->s, mode, 17); +} + +Result sslConnectionSetRenegotiationMode(SslConnection *c, SslRenegotiationMode mode) { + if (!serviceIsActive(&c->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return _sslCmdInU32NoOut(&c->s, mode, 20); +} +