diff --git a/Makefile b/Makefile index aadb6ee98..4c1107ce5 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),riscos) @@ -176,6 +174,7 @@ ifeq ($(PLAT),dos) LDFLAGS = -g OEXT = .exe BUILD_DIR = build/dos + BEARSSL = 0 endif @@ -192,12 +191,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..6c0f30026 100644 --- a/misc/windows/min-wincrypt.h +++ b/misc/windows/min-wincrypt.h @@ -2,17 +2,79 @@ #define CC_CRYPT32_FUNC #endif +typedef void* HCERTSTORE; +typedef void* PCCERT_CONTEXT; + +#define szOID_PKIX_KP_SERVER_AUTH "1.3.6.1.5.5.7.3.1" +#define szOID_SERVER_GATED_CRYPTO "1.3.6.1.4.1.311.10.3.3" +#define szOID_SGC_NETSCAPE "2.16.840.1.113730.4.1" + +#define CERT_STORE_PROV_MEMORY ((LPCSTR)2) +#define CERT_STORE_ADD_ALWAYS 4 +#define X509_ASN_ENCODING 0x1 + typedef struct _CRYPTOAPI_BLOB { DWORD cbData; 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; + +typedef struct _CERT_TRUST_STATUS { + DWORD dwErrorStatus; + DWORD dwInfoStatus; +} CERT_TRUST_STATUS, *PCERT_TRUST_STATUS; + +typedef struct _CERT_CHAIN_CONTEXT { + DWORD cbSize; + CERT_TRUST_STATUS TrustStatus; + DWORD cChain; + void* rgpChain; + DWORD cLowerQualityChainContext; + void* rgpLowerQualityChainContext; + BOOL fHasRevocationFreshnessTime; + DWORD dwRevocationFreshnessTime; + DWORD dwCreateFlags; + GUID ChainId; +} CERT_CHAIN_CONTEXT, *PCERT_CHAIN_CONTEXT; +typedef const CERT_CHAIN_CONTEXT* PCCERT_CHAIN_CONTEXT; + + 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 HCERTSTORE (WINAPI *_CertOpenStore)(LPCSTR storeProvider, DWORD encodingType, void* cryptProv, DWORD flags, const void* para); +CC_CRYPT32_FUNC BOOL (WINAPI *_CertCloseStore)(HCERTSTORE certStore, DWORD flags); +CC_CRYPT32_FUNC BOOL (WINAPI *_CertAddEncodedCertificateToStore)(HCERTSTORE certStore, DWORD certEncodingType, const BYTE* certEncoded, DWORD lenEncoded, DWORD addDisposition, PCCERT_CONTEXT* certContext); + +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); + +CC_CRYPT32_FUNC BOOL (WINAPI *_CertFreeCertificateContext)(PCCERT_CONTEXT certContext); + static void Crypt32_LoadDynamicFuncs(void) { static const struct DynamicLibSym funcs[] = { - DynamicLib_OptSym(CryptProtectData), DynamicLib_OptSym(CryptUnprotectData) + DynamicLib_OptSym(CryptProtectData), DynamicLib_OptSym(CryptUnprotectData), + DynamicLib_ReqSym(CertGetCertificateChain), DynamicLib_ReqSym(CertFreeCertificateChain), + DynamicLib_ReqSym(CertOpenStore), DynamicLib_ReqSym(CertCloseStore), + DynamicLib_ReqSym(CertAddEncodedCertificateToStore), + DynamicLib_ReqSym(CertFreeCertificateContext), }; static const cc_string crypt32 = String_FromConst("CRYPT32.DLL"); @@ -20,4 +82,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..a12917b7b 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: @@ -135,7 +139,7 @@ Setting up TCC: Compiling with TCC: 1. Navigate to the directory with ClassiCube's source code 2. In `ExtMath.c`, change `fabsf` to `fabs` and `sqrtf` to `sqrt` -3. Run `tcc.exe -o ClassiCube.exe *.c -lwinmm -lgdi32 -luser32 -lcomdlg32 -lshell32`
+3. Run `tcc.exe -o ClassiCube.exe src/*.c third_party/bearssl/*.c -lwinmm -lgdi32 -luser32 -lcomdlg32 -lshell32`
(Note: You may need to specify the full path to `tcc.exe` instead of just `tcc.exe`) ## Compiling - Linux @@ -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..561dc6858 100644 --- a/src/Certs.c +++ b/src/Certs.c @@ -288,6 +288,78 @@ 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 +/* Compatibility versions so compiling works on older Windows SDKs */ +#include "../misc/windows/min-wincrypt.h" /* #include */ + +void CertsBackend_Init(void) { + Crypt32_LoadDynamicFuncs(); +} + +static const LPCSTR const usage[] = { + szOID_PKIX_KP_SERVER_AUTH, + szOID_SERVER_GATED_CRYPTO, + szOID_SGC_NETSCAPE +}; + +static BOOL BuildChain(struct X509CertContext* x509, HCERTSTORE store, PCCERT_CONTEXT* end_cert, PCCERT_CHAIN_CONTEXT* chain) { + struct X509Cert* cert = &x509->certs[0]; + CERT_CHAIN_PARA para = { 0 }; + int i; + + BOOL ok = _CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, cert->data, cert->offset, + CERT_STORE_ADD_ALWAYS, end_cert); + if (!ok || !(*end_cert)) return FALSE; + + for (i = 1; i < x509->numCerts; i++) + { + cert = &x509->certs[i]; + ok = _CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, cert->data, cert->offset, + CERT_STORE_ADD_ALWAYS, NULL); + + } + + para.cbSize = sizeof(para); + para.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + para.RequestedUsage.Usage.cUsageIdentifier = Array_Elems(usage); + para.RequestedUsage.Usage.rgpszUsageIdentifier = (LPSTR*)usage; + + return _CertGetCertificateChain(NULL, *end_cert, NULL, NULL, ¶, 0, NULL, chain); +} + +int Certs_VerifyChain(struct X509CertContext* x509) { + PCCERT_CHAIN_CONTEXT chain = NULL; + PCCERT_CONTEXT end_cert = NULL; + HCERTSTORE store; + DWORD res = 200; + + if (!_CertOpenStore) return ERR_NOT_SUPPORTED; + store = _CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL); + if (!store) return ERR_NOT_SUPPORTED; + + if (BuildChain(x509, store, &end_cert, &chain)) { + res = chain->TrustStatus.dwErrorStatus; + if (res) Platform_Log1("Cert validation failed: %h", &res); + } + + _CertFreeCertificateChain(chain); + _CertFreeCertificateContext(end_cert); + _CertCloseStore(store, 0); + return res; +} #endif #endif diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj index 3ea71e143..80aa3886a 100644 --- a/src/ClassiCube.vcxproj +++ b/src/ClassiCube.vcxproj @@ -188,7 +188,7 @@ true 5.02 main - opengl32.lib;Winmm.lib;crypt32.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;crypt32.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) @@ -208,7 +208,7 @@ Windows true main - opengl32.lib;Winmm.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) @@ -230,7 +230,7 @@ true 5.02 main - opengl32.lib;Winmm.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) @@ -252,7 +252,7 @@ true 5.02 main - opengl32.lib;Winmm.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;ucrtd.lib;vcruntimed.lib;msvcrtd.lib;%(AdditionalDependencies) @@ -281,7 +281,7 @@ 5.02 Default main - opengl32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) @@ -308,7 +308,7 @@ true true main - opengl32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) @@ -338,7 +338,7 @@ 5.02 Default main - opengl32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) @@ -368,7 +368,7 @@ 5.02 Default main - opengl32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) + crypt32.lib;Winmm.lib;libucrt.lib;libvcruntime.lib;%(AdditionalDependencies) @@ -452,9 +452,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters index 052217a08..12c91f621 100644 --- a/src/ClassiCube.vcxproj.filters +++ b/src/ClassiCube.vcxproj.filters @@ -127,6 +127,9 @@ {cd88d7cb-c252-4b8f-8f90-c776cfc1e6b6} + + {dec80279-6e40-46a8-b024-ee04e8931638} + @@ -635,6 +638,363 @@ Source Files + + Source Files\Network + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + + + Source Files\BearSSL + diff --git a/src/Core.h b/src/Core.h index f468ce1ec..375972ca2 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/Platform_Windows.c b/src/Platform_Windows.c index 160298225..a9dd02b00 100644 --- a/src/Platform_Windows.c +++ b/src/Platform_Windows.c @@ -17,16 +17,10 @@ #define _UNICODE #endif #include -/* -#include -#include -#include -#include -*/ -/* Compatibility versions so compiling works on older Windows SDKs */ -#include "../misc/windows/min-winsock2.h" -#include "../misc/windows/min-shellapi.h" -#include "../misc/windows/min-wincrypt.h" +/* Use own minimal versions of WinAPI headers so that compiling works on older Windows SDKs */ +#include "../misc/windows/min-winsock2.h" /* #include #include */ +#include "../misc/windows/min-shellapi.h" /* #include */ +#include "../misc/windows/min-wincrypt.h" /* #include */ #include "../misc/windows/min-kernel32.h" static HANDLE heap; 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"