#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; __attribute__((weak)) SslServiceType __nx_ssl_service_type = SslServiceType_Default; 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); const char *serv = "ssl"; if (__nx_ssl_service_type == SslServiceType_System) { if (hosversionAtLeast(15,0,0)) serv = "ssl:s"; else __nx_ssl_service_type = SslServiceType_Default; } Result rc = smGetService(&g_sslSrv, serv); 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 _sslCmdNoIO(Service* srv, u32 cmd_id) { return _sslObjectDispatch(srv, cmd_id); } static Result _sslCmdInU32NoOut(Service* srv, u32 inval, u32 cmd_id) { return _sslObjectDispatchIn(srv, cmd_id, inval); } static Result _sslCmdInU64NoOut(Service* srv, u64 inval, u32 cmd_id) { return _sslObjectDispatchIn(srv, cmd_id, inval); } static Result _sslCmdInU8U32NoOut(Service* srv, u8 val0, u32 val1, u32 cmd_id) { const struct { u8 val0; u8 pad[3]; u32 val1; } in = { val0, {0}, val1 }; return _sslObjectDispatchIn(srv, cmd_id, in); } static Result _sslCmdInTwoU32sNoOut(Service* srv, u32 val0, u32 val1, u32 cmd_id) { const struct { u32 val0; u32 val1; } in = { val0, val1 }; return _sslObjectDispatchIn(srv, cmd_id, in); } static Result _sslCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) { return _sslObjectDispatchOut(srv, cmd_id, *out); } static Result _sslCmdNoInOutU64(Service* srv, u64 *out, u32 cmd_id) { return _sslObjectDispatchOut(srv, cmd_id, *out); } static Result _sslCmdInBufNoOut(Service* srv, const void* buffer, size_t size, u32 cmd_id) { return _sslObjectDispatch(srv, cmd_id, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buffer, size } }, ); } static Result _sslCmdNoInOutBufTwoOutU32s(Service* srv, u32 *out0, u32 *out1, void* buffer, size_t size, u32 cmd_id) { struct { u32 out0; u32 out1; } out; Result rc = _sslObjectDispatchOut(srv, cmd_id, out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, .buffers = { { buffer, size } }, ); if (R_SUCCEEDED(rc)) { if (out0) *out0 = out.out0; if (out1) *out1 = out.out1; } return rc; } static Result _sslCmdInBufOutU32(Service* srv, const void* buffer, size_t size, u32 *out, u32 cmd_id) { return _sslObjectDispatchOut(srv, cmd_id, *out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buffer, size } }, ); } static Result _sslCmdOutBufOutU32(Service* srv, void* buffer, size_t size, u32 *out, u32 cmd_id) { return _sslObjectDispatchOut(srv, cmd_id, *out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, .buffers = { { buffer, size } }, ); } Result sslCreateContext(SslContext *c, u32 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 }; u32 cmd_id = __nx_ssl_service_type == SslServiceType_System ? 100 : 0; return _sslObjectDispatchIn(&g_sslSrv, cmd_id, 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, u32 *total_out) { 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; } } if (R_SUCCEEDED(rc) && total_out) *total_out = out_count; 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 } }, ); } Result sslClearTls12FallbackFlag(void) { if (hosversionBefore(14,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (!serviceIsActive(&g_sslSrv)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoIO(&g_sslSrv, 9); } Result sslSetThreadCoreMask(u64 mask) { if (hosversionBefore(15,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (!serviceIsActive(&g_sslSrv) || __nx_ssl_service_type != SslServiceType_System) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdInU64NoOut(&g_sslSrv, mask, 101); } Result sslGetThreadCoreMask(u64 *out) { if (hosversionBefore(15,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (!serviceIsActive(&g_sslSrv) || __nx_ssl_service_type != SslServiceType_System) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoInOutU64(&g_sslSrv, out, 102); } // ISslContext void sslContextClose(SslContext *c) { // sdknso uses sslContextGetConnectionCount here, returning the error from there on fail / throwing an error if the output count is non-zero. We won't do that. _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); 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* pkcs12, u32 pkcs12_size, const char *pw, u32 pw_size, u64 *id) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (pkcs12==NULL || (pw==NULL && pw_size) || (pw!=NULL && !pw_size)) return MAKERESULT(Module_Libnx, LibnxError_BadInput); return _sslObjectDispatchOut(&c->s, 5, *id, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In, SfBufferAttr_HipcMapAlias | SfBufferAttr_In, }, .buffers = { { pkcs12, pkcs12_size }, { pw, pw_size }, }, ); } 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 _sslCmdInBufNoOut(&c->s, str, str_bufsize, 9); } 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 } }, ); } Result sslContextImportClientCertKeyPki(SslContext *c, const void* cert, u32 cert_size, const void* key, u32 key_size, SslCertificateFormat format, u64 *id) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); u32 tmp=format; return _sslObjectDispatchInOut(&c->s, 12, tmp, *id, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In, SfBufferAttr_HipcMapAlias | SfBufferAttr_In, }, .buffers = { { cert, cert_size }, { key, key_size }, }, ); } Result sslContextGeneratePrivateKeyAndCert(SslContext *c, void* cert, u32 cert_size, void* key, u32 key_size, u32 val, const SslKeyAndCertParams *params, u32 *out_certsize, u32 *out_keysize) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); struct { u32 out_certsize; u32 out_keysize; } out; Result rc = _sslObjectDispatchInOut(&c->s, 13, val, out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out, SfBufferAttr_HipcMapAlias | SfBufferAttr_Out, SfBufferAttr_HipcMapAlias | SfBufferAttr_In, }, .buffers = { { cert, cert_size }, { key, key_size }, { params, sizeof(*params) }, }, ); if (R_SUCCEEDED(rc)) { if (out_certsize) *out_certsize = out.out_certsize; if (out_keysize) *out_keysize = out.out_keysize; } return rc; } Result sslContextCreateConnectionForSystem(SslContext *c, SslConnection *conn) { if (hosversionBefore(15,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (!serviceIsActive(&c->s) || __nx_ssl_service_type != SslServiceType_System) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslObjectDispatch(&c->s, 100, .out_num_objects = 1, .out_objects = &conn->s, ); } // ISslConnection void sslConnectionClose(SslConnection *c) { _sslObjectClose(&c->s); } Result sslConnectionSetSocketDescriptor(SslConnection *c, int sockfd, int *out_sockfd) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslObjectDispatchInOut(&c->s, 0, sockfd, *out_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 _sslCmdInBufNoOut(&c->s, str, str_bufsize, 1); } 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 _sslCmdOutBufOutU32(&c->s, str, str_bufsize, out, 5); } Result sslConnectionGetVerifyOption(SslConnection *c, u32 *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoInOutU32(&c->s, out, 6); } Result sslConnectionGetIoMode(SslConnection *c, SslIoMode *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); u32 tmp=0; Result rc = _sslCmdNoInOutU32(&c->s, &tmp, 7); if (R_SUCCEEDED(rc) && out) *out = tmp; return rc; } Result sslConnectionDoHandshake(SslConnection *c, u32 *out_size, u32 *total_certs, void* server_certbuf, u32 server_certbuf_size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (out_size) *out_size = 0; if (total_certs) *total_certs = 0; if (server_certbuf==NULL || !server_certbuf_size) return _sslCmdNoIO(&c->s, 8); // DoHandshake return _sslCmdNoInOutBufTwoOutU32s(&c->s, out_size, total_certs, server_certbuf, server_certbuf_size, 9); // DoHandshakeGetServerCert } Result sslConnectionGetServerCertDetail(const void* certbuf, u32 certbuf_size, u32 cert_index, void** cert, u32 *cert_size) { if (certbuf==NULL || cert==NULL || cert_size==NULL || certbuf_size < sizeof(SslServerCertDetailHeader) + cert_index*sizeof(SslServerCertDetailEntry)) return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso doesn't have a certbuf_size param for this. sdknso also uses a struct for output, but that just contains a size/addr anyway. SslServerCertDetailHeader *hdr = (SslServerCertDetailHeader*)certbuf; if (hdr->magicnum != 0x4E4D684374726543 || hdr->cert_total <= cert_index) // "CertChMN" return MAKERESULT(Module_Libnx, LibnxError_BadInput); SslServerCertDetailEntry *entry = (SslServerCertDetailEntry*)(++hdr); entry+= cert_index; if (certbuf_size <= entry->offset || certbuf_size < entry->size || certbuf_size < entry->offset + entry->size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); *cert = (void*)certbuf + entry->offset; *cert_size = entry->size; return 0; } Result sslConnectionRead(SslConnection *c, void* buffer, u32 size, u32 *out_size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (buffer==NULL || !size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); return _sslCmdOutBufOutU32(&c->s, buffer, size, out_size, 10); } Result sslConnectionWrite(SslConnection *c, const void* buffer, u32 size, u32 *out_size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (buffer==NULL || !size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); return _sslCmdInBufOutU32(&c->s, buffer, size, out_size, 11); } Result sslConnectionPending(SslConnection *c, s32 *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoInOutU32(&c->s, (u32*)out, 12); } Result sslConnectionPeek(SslConnection *c, void* buffer, u32 size, u32 *out_size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (buffer==NULL || !size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); return _sslCmdOutBufOutU32(&c->s, buffer, size, out_size, 13); } Result sslConnectionPoll(SslConnection *c, u32 in_pollevent, u32 *out_pollevent, u32 timeout) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); const struct { u32 in_pollevent; u32 timeout; } in = { in_pollevent, timeout }; return _sslObjectDispatchInOut(&c->s, 14, in, *out_pollevent); } Result sslConnectionGetVerifyCertError(SslConnection *c) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoIO(&c->s, 15); } Result sslConnectionGetNeededServerCertBufferSize(SslConnection *c, u32 *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoInOutU32(&c->s, out, 16); } Result sslConnectionSetSessionCacheMode(SslConnection *c, SslSessionCacheMode mode) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdInU32NoOut(&c->s, mode, 17); } Result sslConnectionGetSessionCacheMode(SslConnection *c, SslSessionCacheMode *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); u32 tmp=0; Result rc = _sslCmdNoInOutU32(&c->s, &tmp, 18); if (R_SUCCEEDED(rc) && out) *out = tmp; return rc; } Result sslConnectionFlushSessionCache(SslConnection *c) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdNoIO(&c->s, 19); } Result sslConnectionSetRenegotiationMode(SslConnection *c, SslRenegotiationMode mode) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdInU32NoOut(&c->s, mode, 20); } Result sslConnectionGetRenegotiationMode(SslConnection *c, SslRenegotiationMode *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); u32 tmp=0; Result rc = _sslCmdNoInOutU32(&c->s, &tmp, 21); if (R_SUCCEEDED(rc) && out) *out = tmp; return rc; } Result sslConnectionSetOption(SslConnection *c, SslOptionType option, bool flag) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _sslCmdInU8U32NoOut(&c->s, flag!=0, option, 22); } Result sslConnectionGetOption(SslConnection *c, SslOptionType option, bool *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); u32 tmp=option; u8 tmpout=0; Result rc = _sslObjectDispatchInOut(&c->s, 23, tmp, tmpout); if (R_SUCCEEDED(rc) && out) *out = tmpout & 1; return rc; } Result sslConnectionGetVerifyCertErrors(SslConnection *c, u32 *out0, u32 *out1, Result *errors, u32 count) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (out0==NULL || errors==NULL) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (out1) *out1 = 0; u32 tmp=0; Result rc = _sslCmdNoInOutBufTwoOutU32s(&c->s, out0, &tmp, errors, count*sizeof(Result), 24); if (R_SUCCEEDED(rc) && out1) *out1 = tmp; if (R_SUCCEEDED(rc) && *out0 != tmp) rc = MAKERESULT(123, 112); return rc; } Result sslConnectionGetCipherInfo(SslConnection *c, SslCipherInfo *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(4,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); u32 tmp=1; return _sslObjectDispatchIn(&c->s, 25, tmp, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, .buffers = { { out, sizeof(*out) } }, ); } Result sslConnectionSetNextAlpnProto(SslConnection *c, const u8 *buffer, u32 size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(9,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (buffer==NULL || !size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); return _sslCmdInBufNoOut(&c->s, buffer, size, 26); } Result sslConnectionGetNextAlpnProto(SslConnection *c, SslAlpnProtoState *state, u32 *out, u8 *buffer, u32 size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(9,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (buffer==NULL || !size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (state) *state = SslAlpnProtoState_NoSupport; if (out) *out = 0; buffer[0] = 0; u32 tmp=0; Result rc = _sslCmdNoInOutBufTwoOutU32s(&c->s, &tmp, out, buffer, size, 27); if (R_SUCCEEDED(rc) && state) *state = tmp; return rc; } Result sslConnectionSetDtlsSocketDescriptor(SslConnection *c, int sockfd, const void* buf, size_t size, int *out_sockfd) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslObjectDispatchInOut(&c->s, 28, sockfd, *out_sockfd, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buf, size }, }, ); } Result sslConnectionGetDtlsHandshakeTimeout(SslConnection *c, u64 *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslObjectDispatch(&c->s, 29, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, .buffers = { { out, sizeof(*out) }, }, ); } Result sslConnectionSetPrivateOption(SslConnection *c, SslPrivateOptionType option, u32 value) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (hosversionBefore(17,0,0)) return _sslCmdInU8U32NoOut(&c->s, value!=0, option, 30); else return _sslCmdInTwoU32sNoOut(&c->s, option, value, 30); } Result sslConnectionSetSrtpCiphers(SslConnection *c, const u16 *ciphers, u32 count) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslCmdInBufNoOut(&c->s, ciphers, count*sizeof(u16), 31); } Result sslConnectionGetSrtpCipher(SslConnection *c, u16 *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslObjectDispatchOut(&c->s, 32, *out); } Result sslConnectionExportKeyingMaterial(SslConnection *c, u8 *outbuf, u32 outbuf_size, const char *label, u32 label_size, const void* context, u32 context_size) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslObjectDispatch(&c->s, 33, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out, SfBufferAttr_HipcMapAlias | SfBufferAttr_In, SfBufferAttr_HipcMapAlias | SfBufferAttr_In, }, .buffers = { { outbuf, outbuf_size }, { label, label_size }, { context, context_size }, }, ); } Result sslConnectionSetIoTimeout(SslConnection *c, u32 timeout) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslCmdInU32NoOut(&c->s, timeout, 34); } Result sslConnectionGetIoTimeout(SslConnection *c, u32 *out) { if (!serviceIsActive(&c->s)) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(16,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _sslCmdNoInOutU32(&c->s, out, 35); }