From 1972cbe08082e080e48b9f6dabf7619dbdc8a015 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 4 Jul 2025 22:11:22 +1000 Subject: [PATCH] WIP moving Windows to BearSSL --- Makefile | 26 ++- misc/windows/min-wincrypt.h | 28 ++- readme.md | 16 +- src/Certs.c | 30 +++ src/Core.h | 18 +- src/SSL.c | 405 +----------------------------------- 6 files changed, 88 insertions(+), 435 deletions(-) diff --git a/Makefile b/Makefile index ae2e94970..9c9ea1353 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ TARGET := $(ENAME) TRACK_DEPENDENCIES=1 # link using C Compiler by default LINK = $(CC) +# Whether to add BearSSL source files to list of files to compile +BEARSSL=1 ################################################################# @@ -73,13 +75,11 @@ endif ifeq ($(PLAT),linux) LIBS = -lX11 -lXi -lpthread -lGL -ldl BUILD_DIR = build/linux - BEARSSL = 1 endif ifeq ($(PLAT),sunos) LIBS = -lsocket -lX11 -lXi -lGL BUILD_DIR = build/solaris - BEARSSL = 1 endif ifeq ($(PLAT),hp-ux) @@ -87,7 +87,6 @@ ifeq ($(PLAT),hp-ux) LDFLAGS = LIBS = -lm -lX11 -lXi -lXext -L/opt/graphics/OpenGL/lib -lGL -lpthread BUILD_DIR = build/hpux - BEARSSL = 1 endif ifeq ($(PLAT),darwin) @@ -96,7 +95,6 @@ ifeq ($(PLAT),darwin) LDFLAGS = -rdynamic -framework Cocoa -framework OpenGL -framework IOKit -lobjc BUILD_DIR = build/macos TARGET = $(ENAME).app - BEARSSL = 1 endif ifeq ($(PLAT),freebsd) @@ -104,7 +102,6 @@ ifeq ($(PLAT),freebsd) LDFLAGS = -L /usr/local/lib -rdynamic LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread BUILD_DIR = build/freebsd - BEARSSL = 1 endif ifeq ($(PLAT),openbsd) @@ -112,7 +109,6 @@ ifeq ($(PLAT),openbsd) LDFLAGS = -L /usr/X11R6/lib -L /usr/local/lib -rdynamic LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread BUILD_DIR = build/openbsd - BEARSSL = 1 endif ifeq ($(PLAT),netbsd) @@ -120,7 +116,6 @@ ifeq ($(PLAT),netbsd) LDFLAGS = -L /usr/X11R7/lib -L /usr/pkg/lib -rdynamic -Wl,-R/usr/X11R7/lib LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread BUILD_DIR = build/netbsd - BEARSSL = 1 endif ifeq ($(PLAT),dragonfly) @@ -128,7 +123,6 @@ ifeq ($(PLAT),dragonfly) LDFLAGS = -L /usr/local/lib -rdynamic LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread BUILD_DIR = build/flybsd - BEARSSL = 1 endif ifeq ($(PLAT),haiku) @@ -138,7 +132,6 @@ ifeq ($(PLAT),haiku) LINK = $(CXX) LIBS = -lGL -lnetwork -lbe -lgame -ltracker BUILD_DIR = build/haiku - BEARSSL = 1 endif ifeq ($(PLAT),beos) @@ -148,20 +141,25 @@ ifeq ($(PLAT),beos) LINK = $(CXX) LIBS = -lGL -lnetwork -lbe -lgame -ltracker BUILD_DIR = build/beos - TRACK_DEPENDENCIES=0 + TRACK_DEPENDENCIES = 0 + BEARSSL = 0 endif ifeq ($(PLAT),serenityos) LIBS = -lgl -lSDL2 BUILD_DIR = build/serenity - BEARSSL = 1 endif ifeq ($(PLAT),irix) CC = gcc LIBS = -lGL -lX11 -lXi -lpthread -ldl BUILD_DIR = build/irix - BEARSSL = 1 +endif + +ifeq ($(PLAT),rpi) + CFLAGS += -DCC_BUILD_RPI + LIBS = -lpthread -lX11 -lXi -lEGL -lGLESv2 -ldl + BUILD_DIR = build/rpi endif ifeq ($(PLAT),dos) @@ -170,6 +168,7 @@ ifeq ($(PLAT),dos) LDFLAGS = -g OEXT = .exe BUILD_DIR = build/dos + BEARSSL = 0 endif @@ -186,12 +185,11 @@ ifdef TERMINAL LIBS := $(subst mwindows,mconsole,$(LIBS)) endif -ifdef BEARSSL +ifeq ($(BEARSSL),1) BUILD_DIRS += $(BUILD_DIR)/third_party/bearssl BEARSSL_SOURCES = $(wildcard third_party/bearssl/*.c) BEARSSL_OBJECTS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(BEARSSL_SOURCES)) OBJECTS += $(BEARSSL_OBJECTS) - CFLAGS += -DCC_SSL_BACKEND=CC_SSL_BACKEND_BEARSSL -DCC_NET_BACKEND=CC_NET_BACKEND_BUILTIN endif ifdef RELEASE diff --git a/misc/windows/min-wincrypt.h b/misc/windows/min-wincrypt.h index 715ee0f7a..b542bceeb 100644 --- a/misc/windows/min-wincrypt.h +++ b/misc/windows/min-wincrypt.h @@ -7,12 +7,36 @@ typedef struct _CRYPTOAPI_BLOB { BYTE* pbData; } DATA_BLOB; +typedef struct _CTL_USAGE { + DWORD cUsageIdentifier; + LPSTR *rgpszUsageIdentifier; +} CERT_ENHKEY_USAGE; + +#define USAGE_MATCH_TYPE_AND 0x0 +#define USAGE_MATCH_TYPE_OR 0x1 + +typedef struct _CERT_USAGE_MATCH { + DWORD dwType; + CERT_ENHKEY_USAGE Usage; +} CERT_USAGE_MATCH; + +typedef struct _CERT_CHAIN_PARA { + DWORD cbSize; + CERT_USAGE_MATCH RequestedUsage; +} CERT_CHAIN_PARA; + + CC_CRYPT32_FUNC BOOL (WINAPI *_CryptProtectData )(DATA_BLOB* dataIn, PCWSTR dataDescr, PVOID entropy, PVOID reserved, PVOID promptStruct, DWORD flags, DATA_BLOB* dataOut); CC_CRYPT32_FUNC BOOL (WINAPI *_CryptUnprotectData)(DATA_BLOB* dataIn, PWSTR* dataDescr, PVOID entropy, PVOID reserved, PVOID promptStruct, DWORD flags, DATA_BLOB* dataOut); +CC_CRYPT32_FUNC BOOL (WINAPI *_CertGetCertificateChain)(PVOID chainEngine, PCCERT_CONTEXT certContext, LPFILETIME time, HCERTSTORE additionalStore, CERT_CHAIN_PARA* chainPara, DWORD flags, PVOID reserved, PCCERT_CHAIN_CONTEXT* chainContext); + +CC_CRYPT32_FUNC void (WINAPI *_CertFreeCertificateChain)(PCCERT_CHAIN_CONTEXT chainContext); + static void Crypt32_LoadDynamicFuncs(void) { static const struct DynamicLibSym funcs[] = { - DynamicLib_OptSym(CryptProtectData), DynamicLib_OptSym(CryptUnprotectData) + DynamicLib_OptSym(CryptProtectData), DynamicLib_OptSym(CryptUnprotectData), + DynamicLib_OptSym(CertGetCertificateChain), DynamicLib_OptSym(CertFreeCertificateChain), }; static const cc_string crypt32 = String_FromConst("CRYPT32.DLL"); @@ -20,4 +44,4 @@ static void Crypt32_LoadDynamicFuncs(void) { if (crypt32_lib) return; DynamicLib_LoadAll(&crypt32, funcs, Array_Elems(funcs), &crypt32_lib); -} \ No newline at end of file +} diff --git a/readme.md b/readme.md index 450f9a12a..9bbc24b7a 100644 --- a/readme.md +++ b/readme.md @@ -117,14 +117,18 @@ Assuming that you used the installer from https://sourceforge.net/projects/mingw 1. Install MinGW-W64 2. Use either *Run Terminal* from Start Menu or run *mingw-w64.bat* in the installation folder 3. Navigate to the directory with ClassiCube's source code -4. Run `gcc -fno-math-errno *.c -o ClassiCube.exe -mwindows -lwinmm` +4. Run either: + * `make mingw` - produces a simple non-optimised executable, easier to debug + * `make mingw RELEASE=1` - produces an optimised executable, harder to debug ##### Using MinGW Assuming that you used the installer from https://osdn.net/projects/mingw/ : 1. Install MinGW. You need mingw32-base-bin and msys-base-bin packages. 2. Run *msys.bat* in the *C:\MinGW\msys\1.0* folder. -2. Navigate to the directory with ClassiCube's source code -4. Run `gcc -fno-math-errno *.c -o ClassiCube.exe -mwindows -lwinmm` +3. Navigate to the directory with ClassiCube's source code +4. Run either: + * `make mingw` - produces a simple non-optimised executable, easier to debug + * `make mingw RELEASE=1` - produces an optimised executable, harder to debug ##### Using TCC (Tiny C Compiler) Setting up TCC: @@ -149,16 +153,16 @@ For Ubuntu, these are the `libx11-dev`, `libxi-dev` and `libgl1-mesa-dev` packag ##### Cross compiling for Windows (32 bit): 1. Install MinGW-w64 if necessary. (Ubuntu: `gcc-mingw-w64` package) -2. Run ```i686-w64-mingw32-gcc -fno-math-errno src/*.c -o ClassiCube.exe -mwindows -lwinmm``` +2. Run ```make mingw CC=i686-w64-mingw32-gcc``` ##### Cross compiling for Windows (64 bit): 1. Install MinGW-w64 if necessary. (Ubuntu: `gcc-mingw-w64` package) -2. Run ```x86_64-w64-mingw32-gcc -fno-math-errno src/*.c -o ClassiCube.exe -mwindows -lwinmm``` +2. Run ```make mingw CC=x86_64-w64-mingw32-gcc``` ##### Raspberry Pi Although the regular linux compiliation flags will work fine, to take full advantage of the hardware: -```gcc -fno-math-errno src/*.c -o ClassiCube -DCC_BUILD_RPI -rdynamic -lpthread -lX11 -lXi -lEGL -lGLESv2 -ldl``` +```make rpi``` ## Compiling - macOS 1. Install a C compiler if necessary. The easiest way of obtaining one is by installing **Xcode**. diff --git a/src/Certs.c b/src/Certs.c index baf3046c3..8d607c9f6 100644 --- a/src/Certs.c +++ b/src/Certs.c @@ -288,6 +288,36 @@ int Certs_VerifyChain(struct X509CertContext* x509) { return JavaSCall_Int(env, JAVA_sslVerifyChain, NULL); } +#elif CC_CRT_BACKEND == CC_CRT_BACKEND_WINCRYPTO +#define CC_CRYPT32_FUNC extern +#include "Funcs.h" + +#define WIN32_LEAN_AND_MEAN +#define NOSERVICE +#define NOMCX +#define NOIME +#ifndef UNICODE +#define UNICODE +#define _UNICODE +#endif +#include +/* +#include +*/ +/* Compatibility versions so compiling works on older Windows SDKs */ +#include "../misc/windows/min-wincrypt.h" + +void CertsBackend_Init(void) { + Crypt32_LoadDynamicFuncs(); +} + +int Certs_VerifyChain(struct X509CertContext* x509) { + if (!_CertGetCertificateChain) return ERR_NOT_SUPPORTED; + + //_CertGetCertificateChain(NULL, PCCERT_CONTEXT certContext, NULL, HCERTSTORE additionalStore, PCERT_CHAIN_PARA chainPara, DWORD flags, PVOID reserved, PCCERT_CHAIN_CONTEXT* chainContext); + return ERR_NOT_SUPPORTED; +} + #endif #endif diff --git a/src/Core.h b/src/Core.h index 9cf21c090..6fab874e2 100644 --- a/src/Core.h +++ b/src/Core.h @@ -147,16 +147,15 @@ typedef cc_uint8 cc_bool; #define CC_GFX_BACKEND_VULKAN 6 #define CC_GFX_BACKEND_GL11 7 -#define CC_SSL_BACKEND_NONE 1 -#define CC_SSL_BACKEND_BEARSSL 2 -#define CC_SSL_BACKEND_SCHANNEL 3 +#define CC_SSL_BACKEND_NONE 1 +#define CC_SSL_BACKEND_BEARSSL 2 -#define CC_NET_BACKEND_BUILTIN 1 -#define CC_NET_BACKEND_LIBCURL 2 +#define CC_NET_BACKEND_BUILTIN 1 -#define CC_CRT_BACKEND_OPENSSL 1 -#define CC_CRT_BACKEND_APPLESEC 2 -#define CC_CRT_BACKEND_ANDROID 3 +#define CC_CRT_BACKEND_OPENSSL 1 +#define CC_CRT_BACKEND_APPLESEC 2 +#define CC_CRT_BACKEND_ANDROID 3 +#define CC_CRT_BACKEND_WINCRYPTO 4 #define CC_AUD_BACKEND_OPENAL 1 #define CC_AUD_BACKEND_WINMM 2 @@ -213,7 +212,8 @@ typedef cc_uint8 cc_bool; #elif defined _WIN32 && !defined __WINSCW__ #define CC_BUILD_WIN #define DEFAULT_NET_BACKEND CC_NET_BACKEND_BUILTIN - #define DEFAULT_SSL_BACKEND CC_SSL_BACKEND_SCHANNEL + #define DEFAULT_SSL_BACKEND CC_SSL_BACKEND_BEARSSL + #define DEFAULT_CRT_BACKEND CC_CRT_BACKEND_WINCRYPTO #define DEFAULT_AUD_BACKEND CC_AUD_BACKEND_WINMM #define DEFAULT_GFX_BACKEND CC_GFX_BACKEND_D3D9 #define DEFAULT_WIN_BACKEND CC_WIN_BACKEND_WIN32 diff --git a/src/SSL.c b/src/SSL.c index 9c665035b..12bc3222a 100644 --- a/src/SSL.c +++ b/src/SSL.c @@ -1,410 +1,7 @@ #include "SSL.h" #include "Errors.h" -#if CC_SSL_BACKEND == CC_SSL_BACKEND_SCHANNEL -#define WIN32_LEAN_AND_MEAN -#define NOSERVICE -#define NOMCX -#define NOIME -#define NOMINMAX -#include -#define SECURITY_WIN32 -#include -#include -#include "Platform.h" -#include "String.h" -#include "Funcs.h" - -/* https://gist.github.com/mmozeiko/c0dfcc8fec527a90a02145d2cc0bfb6d */ -/* https://web.archive.org/web/20210116110926/http://www.coastrd.com/c-schannel-smtp */ - -/* https://hpbn.co/transport-layer-security-tls/ */ -#define TLS_MAX_PACKET_SIZE (16384 + 512) /* 16kb record size + header/mac/padding */ -/* TODO: Check against sizes.cbMaximumMessage */ - -static void* schannel_lib; -static INIT_SECURITY_INTERFACE_A _InitSecurityInterfaceA; -static cc_bool _verifyCerts; - -static ACQUIRE_CREDENTIALS_HANDLE_FN_A FP_AcquireCredentialsHandleA; -static FREE_CREDENTIALS_HANDLE_FN FP_FreeCredentialsHandle; -static INITIALIZE_SECURITY_CONTEXT_FN_A FP_InitializeSecurityContextA; -static ACCEPT_SECURITY_CONTEXT_FN FP_AcceptSecurityContext; -static COMPLETE_AUTH_TOKEN_FN FP_CompleteAuthToken; -static DELETE_SECURITY_CONTEXT_FN FP_DeleteSecurityContext; -static QUERY_CONTEXT_ATTRIBUTES_FN_A FP_QueryContextAttributesA; -static FREE_CONTEXT_BUFFER_FN FP_FreeContextBuffer; -static ENCRYPT_MESSAGE_FN FP_EncryptMessage; -static DECRYPT_MESSAGE_FN FP_DecryptMessage; - -void SSLBackend_Init(cc_bool verifyCerts) { - /* secur32.dll is available on Win9x and later */ - /* Security.dll is available on NT 4 and later */ - - /* Officially, InitSecurityInterfaceA and then AcquireCredentialsA from */ - /* secur32.dll (or security.dll) should be called - however */ - /* AcquireCredentialsA fails with SEC_E_SECPKG_NOT_FOUND on Win 9x */ - /* But if you instead directly call those functions from schannel.dll, */ - /* then it DOES work. (and on later Windows versions, those functions */ - /* exported from schannel.dll are just DLL forwards to secur32.dll */ - static const struct DynamicLibSym funcs[] = { - DynamicLib_ReqSym(InitSecurityInterfaceA) - }; - static const cc_string schannel = String_FromConst("schannel.dll"); - _verifyCerts = verifyCerts; - /* TODO: Load later?? it's unsafe to do on a background thread though */ - DynamicLib_LoadAll(&schannel, funcs, Array_Elems(funcs), &schannel_lib); -} - -cc_bool SSLBackend_DescribeError(cc_result res, cc_string* dst) { - switch (res) { - case SEC_E_UNTRUSTED_ROOT: - String_AppendConst(dst, "The website's SSL certificate was issued by an authority that is not trusted"); - return true; - case SEC_E_CERT_EXPIRED: - String_AppendConst(dst, "The website's SSL certificate has expired"); - return true; - case TRUST_E_CERT_SIGNATURE: - String_AppendConst(dst, "The signature of the website's SSL certificate cannot be verified"); - return true; - case SEC_E_UNSUPPORTED_FUNCTION: - /* https://learn.microsoft.com/en-us/windows/win32/secauthn/schannel-error-codes-for-tls-and-ssl-alerts */ - /* TLS1_ALERT_PROTOCOL_VERSION maps to this error code */ - String_AppendConst(dst, "The website uses an incompatible SSL/TLS version"); - return true; - } - return false; -} - - -struct SSLContext { - cc_socket socket; - CredHandle handle; - CtxtHandle context; - SecPkgContext_StreamSizes sizes; - DWORD flags; - int bufferLen; - int leftover; /* number of unprocessed bytes leftover from last successful DecryptMessage */ - int decryptedSize; - char* decryptedData; - char incoming[TLS_MAX_PACKET_SIZE]; -}; - -/* Undefined in older MinGW versions */ -#define _SP_PROT_TLS1_1_CLIENT 0x00000200 -#define _SP_PROT_TLS1_2_CLIENT 0x00000800 - -static SECURITY_STATUS SSL_CreateHandle(struct SSLContext* ctx) { - SCHANNEL_CRED cred = { 0 }; - cred.dwVersion = SCHANNEL_CRED_VERSION; - cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | (_verifyCerts ? SCH_CRED_AUTO_CRED_VALIDATION : SCH_CRED_MANUAL_CRED_VALIDATION); - cred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | _SP_PROT_TLS1_1_CLIENT | _SP_PROT_TLS1_2_CLIENT; - - /* TODO: SCHANNEL_NAME_A ? */ - return FP_AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, - &cred, NULL, NULL, &ctx->handle, NULL); -} - -static cc_result SSL_RecvRaw(struct SSLContext* ctx) { - cc_uint32 read; - cc_result res; - - /* server is sending too much garbage data instead of proper TLS packets ?? */ - if (ctx->bufferLen == sizeof(ctx->incoming)) return ERR_INVALID_ARGUMENT; - - res = Socket_Read(ctx->socket, ctx->incoming + ctx->bufferLen, - sizeof(ctx->incoming) - ctx->bufferLen, &read); - - if (res) return res; - if (!read) return ERR_END_OF_STREAM; - - ctx->bufferLen += read; - return 0; -} - - -/* Sends the initial TLS handshake ClientHello message to the server */ -static SECURITY_STATUS SSL_Connect(struct SSLContext* ctx, const char* hostname) { - SecBuffer out_buffers[1]; - SecBufferDesc out_desc; - SECURITY_STATUS res; - DWORD flags = ctx->flags; - - out_buffers[0].BufferType = SECBUFFER_TOKEN; - out_buffers[0].pvBuffer = NULL; - out_buffers[0].cbBuffer = 0; - - out_desc.ulVersion = SECBUFFER_VERSION; - out_desc.cBuffers = Array_Elems(out_buffers); - out_desc.pBuffers = out_buffers; - - res = FP_InitializeSecurityContextA(&ctx->handle, NULL, (char*)hostname, flags, 0, 0, - NULL, 0, &ctx->context, &out_desc, &flags, NULL); - if (res != SEC_I_CONTINUE_NEEDED) return res; - res = 0; - - /* Send initial handshake to the server (if there is one) */ - if (out_buffers[0].pvBuffer) { - res = Socket_WriteAll(ctx->socket, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer); - FP_FreeContextBuffer(out_buffers[0].pvBuffer); - } - return res; -} - -/* Performs (Negotiates) the rest of the TLS handshake */ -static SECURITY_STATUS SSL_Negotiate(struct SSLContext* ctx) { - SecBuffer in_buffers[2]; - SecBuffer out_buffers[1]; - SecBufferDesc in_desc; - SecBufferDesc out_desc; - cc_uint32 leftover_len; - SECURITY_STATUS sec; - cc_result res; - DWORD flags; - - for (;;) - { - /* buffer 0 = data received from server which SChannel processes */ - /* buffer 1 = any leftover data which SChannel didn't process this time */ - /* (this data must be persisted, as it will be used next time around) */ - in_buffers[0].BufferType = SECBUFFER_TOKEN; - in_buffers[0].pvBuffer = ctx->incoming; - in_buffers[0].cbBuffer = ctx->bufferLen; - in_buffers[1].BufferType = SECBUFFER_EMPTY; - in_buffers[1].pvBuffer = NULL; - in_buffers[1].cbBuffer = 0; - - out_buffers[0].BufferType = SECBUFFER_TOKEN; - out_buffers[0].pvBuffer = NULL; - out_buffers[0].cbBuffer = 0; - - in_desc.ulVersion = SECBUFFER_VERSION; - in_desc.cBuffers = Array_Elems(in_buffers); - in_desc.pBuffers = in_buffers; - - out_desc.ulVersion = SECBUFFER_VERSION; - out_desc.cBuffers = Array_Elems(out_buffers); - out_desc.pBuffers = out_buffers; - - flags = ctx->flags; - sec = FP_InitializeSecurityContextA(&ctx->handle, &ctx->context, NULL, flags, 0, 0, - &in_desc, 0, NULL, &out_desc, &flags, NULL); - - if (in_buffers[1].BufferType == SECBUFFER_EXTRA) { - /* SChannel didn't process the entirety of the input buffer */ - /* So move the leftover data back to the front of the input buffer */ - leftover_len = in_buffers[1].cbBuffer; - Mem_Move(ctx->incoming, ctx->incoming + (ctx->bufferLen - leftover_len), leftover_len); - ctx->bufferLen = leftover_len; - } else if (sec != SEC_E_INCOMPLETE_MESSAGE) { - /* SChannel processed entirely of input buffer */ - ctx->bufferLen = 0; - } - - /* Handshake completed */ - if (sec == SEC_E_OK) break; - - /* Need to send data to the server */ - if (sec == SEC_I_CONTINUE_NEEDED) { - res = Socket_WriteAll(ctx->socket, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer); - FP_FreeContextBuffer(out_buffers[0].pvBuffer); /* TODO always free? */ - - if (res) return res; - continue; - } - - if (sec != SEC_E_INCOMPLETE_MESSAGE) return sec; - /* SEC_E_INCOMPLETE_MESSAGE case - need to read more data from the server first */ - if ((res = SSL_RecvRaw(ctx))) return res; - } - - FP_QueryContextAttributesA(&ctx->context, SECPKG_ATTR_STREAM_SIZES, &ctx->sizes); - return 0; -} - - -static void SSL_LoadSecurityFunctions(PSecurityFunctionTableA sspiFPs) { - FP_AcquireCredentialsHandleA = sspiFPs->AcquireCredentialsHandleA; - FP_FreeCredentialsHandle = sspiFPs->FreeCredentialsHandle; - FP_InitializeSecurityContextA = sspiFPs->InitializeSecurityContextA; - FP_AcceptSecurityContext = sspiFPs->AcceptSecurityContext; - FP_CompleteAuthToken = sspiFPs->CompleteAuthToken; - FP_DeleteSecurityContext = sspiFPs->DeleteSecurityContext; - FP_QueryContextAttributesA = sspiFPs->QueryContextAttributesA; - FP_FreeContextBuffer = sspiFPs->FreeContextBuffer; - - FP_EncryptMessage = sspiFPs->EncryptMessage; - FP_DecryptMessage = sspiFPs->DecryptMessage; - /* Old Windows versions don't have EncryptMessage/DecryptMessage, */ - /* but have the older SealMessage/UnsealMessage functions instead */ - if (!FP_EncryptMessage) FP_EncryptMessage = (ENCRYPT_MESSAGE_FN)sspiFPs->Reserved3; - if (!FP_DecryptMessage) FP_DecryptMessage = (DECRYPT_MESSAGE_FN)sspiFPs->Reserved4; -} - -cc_result SSL_Init(cc_socket socket, const cc_string* host_, void** out_ctx) { - PSecurityFunctionTableA sspiFPs; - struct SSLContext* ctx; - SECURITY_STATUS res; - cc_winstring host; - if (!_InitSecurityInterfaceA) return HTTP_ERR_NO_SSL; - - if (!FP_InitializeSecurityContextA) { - sspiFPs = _InitSecurityInterfaceA(); - if (!sspiFPs) return ERR_NOT_SUPPORTED; - SSL_LoadSecurityFunctions(sspiFPs); - } - - ctx = Mem_TryAllocCleared(1, sizeof(struct SSLContext)); - if (!ctx) return ERR_OUT_OF_MEMORY; - *out_ctx = (void*)ctx; - - ctx->flags = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; - if (!_verifyCerts) ctx->flags |= ISC_REQ_MANUAL_CRED_VALIDATION; - - ctx->socket = socket; - Platform_EncodeString(&host, host_); - - if ((res = SSL_CreateHandle(ctx))) return res; - if ((res = SSL_Connect(ctx, host.ansi))) return res; - if ((res = SSL_Negotiate(ctx))) return res; - return 0; -} - - -static cc_result SSL_ReadDecrypted(struct SSLContext* ctx, cc_uint8* data, cc_uint32 count, cc_uint32* read) { - int len = min(count, ctx->decryptedSize); - Mem_Copy(data, ctx->decryptedData, len); - - if (len == ctx->decryptedSize) { - /* incoming buffer stores decrypted data and then any leftover ciphertext */ - /* So move the leftover ciphertext back to the start of the input buffer */ - /* TODO: Share function with handshake function */ - Mem_Move(ctx->incoming, ctx->incoming + (ctx->bufferLen - ctx->leftover), ctx->leftover); - ctx->bufferLen = ctx->leftover; - ctx->leftover = 0; - - ctx->decryptedData = NULL; - ctx->decryptedSize = 0; - } else { - ctx->decryptedData += len; - ctx->decryptedSize -= len; - } - - *read = len; - return 0; -} - -cc_result SSL_Read(void* ctx_, cc_uint8* data, cc_uint32 count, cc_uint32* read) { - struct SSLContext* ctx = ctx_; - SecBuffer buffers[4]; - SecBufferDesc desc; - SECURITY_STATUS sec; - cc_result res; - - /* decrypted data from previously */ - if (ctx->decryptedData) return SSL_ReadDecrypted(ctx, data, count, read); - - for (;;) - { - /* if any ciphertext data, then try to decrypt it */ - if (ctx->bufferLen) { - /* https://learn.microsoft.com/en-us/windows/win32/secauthn/stream-contexts */ - buffers[0].BufferType = SECBUFFER_DATA; - buffers[0].pvBuffer = ctx->incoming; - buffers[0].cbBuffer = ctx->bufferLen; - buffers[1].BufferType = SECBUFFER_EMPTY; - buffers[2].BufferType = SECBUFFER_EMPTY; - buffers[3].BufferType = SECBUFFER_EMPTY; - - desc.ulVersion = SECBUFFER_VERSION; - desc.cBuffers = Array_Elems(buffers); - desc.pBuffers = buffers; - - sec = FP_DecryptMessage(&ctx->context, &desc, 0, NULL); - if (sec == SEC_E_OK) { - /* After successful decryption the SecBuffers will be: */ - /* buffers[0] = headers */ - /* buffers[1] = content */ - /* buffers[2] = trailers */ - /* buffers[3] = extra, if any leftover unprocessed data */ - ctx->decryptedData = buffers[1].pvBuffer; - ctx->decryptedSize = buffers[1].cbBuffer; - ctx->leftover = buffers[3].BufferType == SECBUFFER_EXTRA ? buffers[3].cbBuffer : 0; - - return SSL_ReadDecrypted(ctx, data, count, read); - } - - /* TODO properly close the connection with TLS shutdown when this happens */ - if (sec == SEC_I_CONTEXT_EXPIRED) return SSL_ERR_CONTEXT_DEAD; - - if (sec != SEC_E_INCOMPLETE_MESSAGE) return sec; - /* SEC_E_INCOMPLETE_MESSAGE case - still need to read more data from the server first */ - } - - /* not enough data received yet to decrypt, so need to read more data from the server */ - if ((res = SSL_RecvRaw(ctx))) return res; - } - return 0; -} - -static cc_result SSL_WriteChunk(struct SSLContext* s, const cc_uint8* data, cc_uint32 count) { - char buffer[TLS_MAX_PACKET_SIZE]; - SecBuffer buffers[3]; - SecBufferDesc desc; - SECURITY_STATUS res; - int total; - - /* https://learn.microsoft.com/en-us/windows/win32/secauthn/encryptmessage--schannel */ - buffers[0].BufferType = SECBUFFER_STREAM_HEADER; - buffers[0].pvBuffer = buffer; - buffers[0].cbBuffer = s->sizes.cbHeader; - buffers[1].BufferType = SECBUFFER_DATA; - buffers[1].pvBuffer = buffer + s->sizes.cbHeader; - buffers[1].cbBuffer = count; - buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; - buffers[2].pvBuffer = buffer + s->sizes.cbHeader + count; - buffers[2].cbBuffer = s->sizes.cbTrailer; - - /* See https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-encryptmessage */ - /* ".. The message is encrypted in place, overwriting the original contents of the structure" */ - Mem_Copy(buffers[1].pvBuffer, data, count); - - desc.ulVersion = SECBUFFER_VERSION; - desc.cBuffers = Array_Elems(buffers); - desc.pBuffers = buffers; - if ((res = FP_EncryptMessage(&s->context, 0, &desc, 0))) return res; - - /* NOTE: Okay to write in one go, since all three buffers will be contiguous */ - /* (as TLS record header size will always be the same size) */ - total = buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer; - return Socket_WriteAll(s->socket, buffer, total); -} - -cc_result SSL_WriteAll(void* ctx, const cc_uint8* data, cc_uint32 count) { - struct SSLContext* s = ctx; - cc_result res; - - /* TODO: Don't loop here? move to HTTPConnection instead?? */ - while (count) - { - int len = min(count, s->sizes.cbMaximumMessage); - if ((res = SSL_WriteChunk(s, data, len))) return res; - - data += len; - count -= len; - } - return 0; -} - -cc_result SSL_Free(void* ctx_) { - /* TODO send TLS close */ - struct SSLContext* ctx = (struct SSLContext*)ctx_; - FP_DeleteSecurityContext(&ctx->context); - FP_FreeCredentialsHandle(&ctx->handle); - Mem_Free(ctx); - return 0; -} -#elif CC_SSL_BACKEND == CC_SSL_BACKEND_BEARSSL +#if CC_SSL_BACKEND == CC_SSL_BACKEND_BEARSSL #include "String.h" #include "Certs.h" #include "../third_party/bearssl/bearssl.h"