From d4714cab524e2470c92832869305e20c61e0ec62 Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 21 Jun 2021 13:43:57 +0000 Subject: [PATCH] Rework tls/dtls stuff into a more formal crypto interface instead of lots of ifdefs all over the place. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5891 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- CMakeLists.txt | 25 ----- engine/client/m_download.c | 55 ++++++----- engine/client/sys_linux.c | 31 ++++--- engine/common/bothdefs.h | 4 +- engine/common/common.c | 3 - engine/common/config_freecs.h | 1 - engine/common/config_fteqw.h | 1 - engine/common/config_fteqw_noweb.h | 1 - engine/common/config_minimal.h | 1 - engine/common/config_nocompat.h | 1 - engine/common/config_wastes.h | 1 - engine/common/net.h | 2 + engine/common/net_ssl_gnutls.c | 60 ++++++------ engine/common/net_ssl_winsspi.c | 99 ++++++-------------- engine/common/net_wins.c | 144 +++++++++++++++++++---------- engine/common/netinc.h | 43 ++++----- engine/common/plugin.c | 3 + 17 files changed, 229 insertions(+), 246 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77b5afbb..7f2ae1c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,25 +274,6 @@ ELSEIF(${WIN32}) ELSEIF(${UNIX} AND NOT FTE_USE_SDL) #linux(ish) #openbsd will have issues with snd_linux.c - IF(FTE_PRIVATE_USE_ONLY) - #the openssl license is incompatible with the GPL, so while we have code to use it distributing the binaries built with it is not a (legal) option. - #note that openssl 3.0.0 upwards are apache-2 licensed, which IS gpl-3 compatible. debian has not caught up with that yet, however. - FIND_PACKAGE(OpenSSL) - IF(NOT OPENSSL_FOUND) - MESSAGE(WARNING "openssl library NOT available. HTTPS/DTLS will not be available.") - ELSEIF(OPENSSL_VERSION_MAJOR LESS 3 AND NOT FTE_PRIVATE_USE_ONLY) - MESSAGE(WARNING "openssl library version is not 3 or above. Ignoring due to incompatible license.") - ELSE() - MESSAGE(WARNING "Using openssl.") - SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_OPENSSL) - - SET(FTE_ARCH_FILES ${FTE_ARCH_FILES} engine/common/net_ssl_openssl.c) - SET(FTE_LIBS ${FTE_LIBS} ${OPENSSL_LIBRARIES}) - SET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES} engine/common/net_ssl_openssl.c) - SET(FTESV_LIBS ${FTESV_LIBS} ${OPENSSL_LIBRARIES}) - ENDIF() - ENDIF() - FIND_PACKAGE(GnuTLS) IF(NOT GNUTLS_FOUND) MESSAGE(WARNING "gnutls library NOT available. HTTPS/DTLS will not be available.") @@ -328,7 +309,6 @@ ELSEIF(${UNIX} AND NOT FTE_USE_SDL) #linux(ish) engine/client/sys_linux.c engine/common/sys_linux_threads.c engine/common/net_ssl_gnutls.c -# engine/common/net_ssl_openssl.c engine/client/snd_al.c engine/client/snd_alsa.c @@ -391,14 +371,9 @@ ELSEIF(${UNIX} AND NOT FTE_USE_SDL) #linux(ish) engine/server/sv_sys_unix.c engine/common/sys_linux_threads.c engine/common/net_ssl_gnutls.c -# engine/common/net_ssl_openssl.c ) SET(FTESV_LIBS ${FTESV_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} pthread) -# SET(FTE_DEFINES ${FTE_DEFINES};HAVE_OPENSSL) -# SET(FTESV_DEFINES ${FTESV_DEFINES};HAVE_OPENSSL) -# SET(FTE_LIBS ${FTE_LIBS} ssl crypto) -# SET(FTESV_LIBS ${FTE_LIBS} ssl crypto) ELSEIF(1) #SDL # FIND_PACKAGE(Freetype REQUIRED) # INCLUDE_DIRECTORIES(engine/libs engine/libs/freetype2/include) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 55c08707..0f5ee47f 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -361,9 +361,12 @@ void PM_ValidateAuthenticity(package_t *p, enum hashvalidation_e validated) size_t hashsize = 0; qbyte signdata[1024]; size_t signsize = 0; - int r; + int r, i; char authority[MAX_QPATH], *sig; + const qbyte *pubkey; + size_t pubkeysize; + #ifndef _DEBUG #pragma message("Temporary code.") //this is temporary code and should be removed once everything else has been fixed. @@ -412,28 +415,25 @@ void PM_ValidateAuthenticity(package_t *p, enum hashvalidation_e validated) sig++; } else + { + strcpy(authority, "Spike"); //legacy bollocks sig = p->signature; + } hashsize = Base16_DecodeBlock(p->filesha512, hashdata, sizeof(hashdata)); signsize = Base64_DecodeBlock(sig, NULL, signdata, sizeof(signdata)); r = VH_UNSUPPORTED;//preliminary } - (void)signsize; - (void)hashsize; + pubkey = Auth_GetKnownCertificate(authority, &pubkeysize); + if (!pubkey) + r = VH_AUTHORITY_UNKNOWN; //try and get one of our providers to verify it... -#ifdef HAVE_WINSSPI - if (r == VH_UNSUPPORTED) - r = SSPI_VerifyHash(hashdata, hashsize, authority, signdata, signsize); -#endif -#ifdef HAVE_GNUTLS - if (r == VH_UNSUPPORTED) - r = GNUTLS_VerifyHash(hashdata, hashsize, authority, signdata, signsize); -#endif -#ifdef HAVE_OPENSSL - if (r == VH_UNSUPPORTED) - r = OSSL_VerifyHash(hashdata, hashsize, authority, signdata, signsize); -#endif + for (i = 0; r==VH_UNSUPPORTED && i < cryptolib_count; i++) + { + if (cryptolib[i] && cryptolib[i]->VerifyHash) + r = cryptolib[i]->VerifyHash(hashdata, hashsize, pubkey, pubkeysize, signdata, signsize); + } p->flags &= ~(DPF_SIGNATUREACCEPTED|DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN); if (r == VH_CORRECT) @@ -1379,8 +1379,11 @@ static qboolean PM_ParsePackageList(const char *f, unsigned int parseflags, cons char authority[MAX_OSPATH]; char signdata[MAX_OSPATH]; char signature_base64[MAX_OSPATH]; + qbyte *pubkey; + size_t pubkeysize; size_t signsize; enum hashvalidation_e r; + int i; hashfunc_t *hf = &hash_sha512; void *hashdata = Z_Malloc(hf->digestsize); void *hashctx = Z_Malloc(hf->contextsize); @@ -1395,20 +1398,16 @@ static qboolean PM_ParsePackageList(const char *f, unsigned int parseflags, cons Z_Free(hashctx); r = VH_UNSUPPORTED;//preliminary - (void)signsize; + pubkey = Auth_GetKnownCertificate(authority, &pubkeysize); + if (!pubkey) + r = VH_AUTHORITY_UNKNOWN; + //try and get one of our providers to verify it... - #ifdef HAVE_WINSSPI - if (r == VH_UNSUPPORTED) - r = SSPI_VerifyHash(hashdata, hf->digestsize, authority, signdata, signsize); - #endif - #ifdef HAVE_GNUTLS - if (r == VH_UNSUPPORTED) - r = GNUTLS_VerifyHash(hashdata, hf->digestsize, authority, signdata, signsize); - #endif - #ifdef HAVE_OPENSSL - if (r == VH_UNSUPPORTED) - r = OSSL_VerifyHash(hashdata, hf->digestsize, authority, signdata, signsize); - #endif + for (i = 0; r==VH_UNSUPPORTED && i < cryptolib_count; i++) + { + if (cryptolib[i] && cryptolib[i]->VerifyHash) + r = cryptolib[i]->VerifyHash(hashdata, hf->digestsize, pubkey, pubkeysize, signdata, signsize); + } Z_Free(hashdata); source.validated = r; diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index bbfdf716..19f93e54 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -1076,7 +1076,19 @@ char *Sys_ConsoleInput(void) return NULL; } -#ifdef HAVE_GNUTLS +//begin meta generation helper +#include "fs.h" +static int Crypto_GenerateSignature(qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax) +{ + int i; + int sigsize = -1; + for (i = 0; sigsize==-1 && iGenerateSignature) + sigsize = cryptolib[i]->GenerateSignature(hashdata, hashsize, signdata, signsizemax); + } + return sigsize; +} static void DoSign(const char *fname, int signtype) { qbyte digest[1024]; @@ -1112,25 +1124,20 @@ static void DoSign(const char *fname, int signtype) Base16_EncodeBlock(digest, h->digestsize, base64, sizeof(base64)); printf(" \\\"sha512=%s\\\"", base64); - sigsize = GNUTLS_GenerateSignature(digest, h->digestsize, signature, sizeof(signature)); + sigsize = Crypto_GenerateSignature(digest, h->digestsize, signature, sizeof(signature)); Base64_EncodeBlock(signature, sigsize, base64, sizeof(base64)); printf(" \\\"sign=%s:%s\\\"\n", auth, base64); } else if (signtype == 2) - { //signature "auth" "signdata" - //printf(" \\\"dlsize=%zu\\\"", ts); - - //Base16_EncodeBlock(digest, h->digestsize, base64, sizeof(base64)); - //printf(" \\\"sha512=%s\\\"", base64); - - sigsize = GNUTLS_GenerateSignature(digest, h->digestsize, signature, sizeof(signature)); + { //spits out the raw signature. + sigsize = Crypto_GenerateSignature(digest, h->digestsize, signature, sizeof(signature)); Base64_EncodeBlock(signature, sigsize, base64, sizeof(base64)); printf("%s\n", base64); } } } -#endif +//end meta helpers #ifdef _POSIX_C_SOURCE static void SigCont(int code) @@ -1285,7 +1292,7 @@ int main (int c, const char **v) if (COM_CheckParm("-nostdout")) nostdout = 1; -#ifdef HAVE_GNUTLS +//begin meta generation helpers //fteqw -privcert privcert.key -pubcert pubcert.key -sign binaryfile.pk3 i = COM_CheckParm("-sign"); if (!i) @@ -1301,7 +1308,7 @@ int main (int c, const char **v) DoSign(com_argv[i+1], atoi(com_argv[i+0]+5)); return EXIT_SUCCESS; } -#endif +//end if (parms.binarydir) Sys_Printf("Binary is located at \"%s\"\n", parms.binarydir); diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 74b42656..7df61e2a 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -419,10 +419,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef USE_EGL #endif -#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) || defined(HAVE_WINSSPI) +#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) #define HAVE_SSL #endif -#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) || defined(HAVE_WINSSPI) +#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) //FIXME: HAVE_WINSSPI does not work as a server. //FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade. //FIXME: we don't cache server certs diff --git a/engine/common/common.c b/engine/common/common.c index 929310a3..d3a1c902 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -5566,9 +5566,6 @@ static void COM_Version_f (void) #ifdef HAVE_GNUTLS //on linux Con_Printf(" GnuTLS"); #endif -#ifdef HAVE_OPENSSL //on linux. hardlinked, so typically set only via the makefile. - Con_Printf(" OpenSSL"); -#endif #ifdef HAVE_WINSSPI //on windows Con_Printf(" WINSSPI"); #endif diff --git a/engine/common/config_freecs.h b/engine/common/config_freecs.h index 8aee0f2a..f9bcf7ce 100644 --- a/engine/common/config_freecs.h +++ b/engine/common/config_freecs.h @@ -159,7 +159,6 @@ //FIXME: Stuff that Spike has added that Eukara needs to decide whether to keep or not. #define HAVE_OPUS //#define HAVE_SPEEX -//#define HAVE_OPENSSL //#define IMAGEFMT_HDR //#define IMAGEFMT_PBM //#define IMAGEFMT_PSD diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index 1417df40..98eb4adb 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -147,7 +147,6 @@ #define HAVE_PACKET //we can send unreliable messages! #define HAVE_TCP //we can create/accept TCP connections. #define HAVE_GNUTLS //on linux -//#define HAVE_OPENSSL //on linux. hardlinked, so typically set only via the makefile. #define HAVE_WINSSPI //on windows #define FTPSERVER //sv_ftp cvar. #define WEBCLIENT //uri_get+any internal downloads etc diff --git a/engine/common/config_fteqw_noweb.h b/engine/common/config_fteqw_noweb.h index 93c9061a..b492601d 100644 --- a/engine/common/config_fteqw_noweb.h +++ b/engine/common/config_fteqw_noweb.h @@ -10,7 +10,6 @@ #undef FTPSERVER #undef HAVE_TCP #undef HAVE_GNUTLS -#undef HAVE_OPENSSL #undef HAVE_WINSSPI #undef SUPPORT_ICE #undef SUBSERVERS diff --git a/engine/common/config_minimal.h b/engine/common/config_minimal.h index fc1ed9df..01b6f754 100644 --- a/engine/common/config_minimal.h +++ b/engine/common/config_minimal.h @@ -149,7 +149,6 @@ #define HAVE_PACKET //we can send unreliable messages! //#define HAVE_TCP //we can create/accept TCP connections. //#define HAVE_GNUTLS //on linux -//#define HAVE_OPENSSL //on linux. hardlinked, so typically set only via the makefile. //#define HAVE_WINSSPI //on windows //#define FTPSERVER //sv_ftp cvar. //#define WEBCLIENT //uri_get+any internal downloads etc diff --git a/engine/common/config_nocompat.h b/engine/common/config_nocompat.h index c3b54ae0..dc0d5858 100644 --- a/engine/common/config_nocompat.h +++ b/engine/common/config_nocompat.h @@ -147,7 +147,6 @@ #define HAVE_PACKET //we can send unreliable messages! #define HAVE_TCP //we can create/accept TCP connections. #define HAVE_GNUTLS //on linux -//#define HAVE_OPENSSL //on linux. hardlinked, so typically set only via the makefile. #define HAVE_WINSSPI //on windows //#define FTPSERVER //sv_ftp cvar. #define WEBCLIENT //uri_get+any internal downloads etc diff --git a/engine/common/config_wastes.h b/engine/common/config_wastes.h index 385421f2..99d020b0 100644 --- a/engine/common/config_wastes.h +++ b/engine/common/config_wastes.h @@ -179,7 +179,6 @@ #undef WAYLANDQUAKE #undef SERVER_DEMO_PLAYBACK /* deprecated */ #undef DECOMPRESS_BPTC -#undef HAVE_OPENSSL /* incomplete */ #undef IMAGEFMT_HDR #undef IMAGEFMT_PBM #undef IMAGEFMT_PSD diff --git a/engine/common/net.h b/engine/common/net.h index f5406ebb..5013bb6a 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -194,6 +194,8 @@ void NET_DTLS_Timeouts(struct ftenet_connections_s *col); #endif extern cvar_t timeout; extern cvar_t tls_ignorecertificateerrors; //evil evil evil. +struct ftecrypto_s; +qboolean NET_RegisterCrypto(void *module, struct ftecrypto_s *driver); //============================================================================ diff --git a/engine/common/net_ssl_gnutls.c b/engine/common/net_ssl_gnutls.c index c9f85202..2eff530d 100644 --- a/engine/common/net_ssl_gnutls.c +++ b/engine/common/net_ssl_gnutls.c @@ -903,7 +903,7 @@ static gnutls_certificate_credentials_t xcred[2]; static gnutls_datum_t cookie_key; #endif -vfsfile_t *SSL_OpenPrivKey(char *nativename, size_t nativesize) +static vfsfile_t *SSL_OpenPrivKey(char *nativename, size_t nativesize) { #define privname "privkey.pem" vfsfile_t *privf; @@ -926,7 +926,7 @@ vfsfile_t *SSL_OpenPrivKey(char *nativename, size_t nativesize) return privf; #undef privname } -vfsfile_t *SSL_OpenPubKey(char *nativename, size_t nativesize) +static vfsfile_t *SSL_OpenPubKey(char *nativename, size_t nativesize) { #define fullchainname "fullchain.pem" #define pubname "cert.pem" @@ -1231,7 +1231,7 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole return true; } -vfsfile_t *GNUTLS_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver) +static vfsfile_t *GNUTLS_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver) { gnutlsfile_t *newf; @@ -1270,7 +1270,7 @@ vfsfile_t *GNUTLS_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isse return &newf->funcs; } -int GNUTLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) +static int GNUTLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) { gnutls_datum_t cb; gnutlsfile_t *f = (gnutlsfile_t*)vf; @@ -1293,9 +1293,9 @@ int GNUTLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) } //crypto: generates a signed blob -int GNUTLS_GenerateSignature(qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax) +static int GNUTLS_GenerateSignature(const qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax) { - gnutls_datum_t hash = {hashdata, hashsize}; + gnutls_datum_t hash = {(qbyte*)hashdata, hashsize}; gnutls_datum_t sign = {NULL, 0}; gnutls_certificate_credentials_t cred; @@ -1324,24 +1324,21 @@ int GNUTLS_GenerateSignature(qbyte *hashdata, size_t hashsize, qbyte *signdata, } //crypto: verifies a signed blob matches an authority's public cert. windows equivelent https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program-signing-a-hash-and-verifying-the-hash-signature -enum hashvalidation_e GNUTLS_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize) +static enum hashvalidation_e GNUTLS_VerifyHash(const qbyte *hashdata, size_t hashsize, const qbyte *pubkeydata, size_t pubkeysize, const qbyte *signdata, size_t signsize) { - gnutls_datum_t hash = {hashdata, hashsize}; - gnutls_datum_t sign = {signdata, signsize}; + gnutls_datum_t hash = {(qbyte*)hashdata, hashsize}; + gnutls_datum_t sign = {(qbyte*)signdata, signsize}; int r; - gnutls_datum_t rawcert; + gnutls_datum_t rawcert = {(qbyte*)pubkeydata, pubkeysize}; #if 1 - size_t sz; gnutls_pubkey_t pubkey; gnutls_x509_crt_t cert; - rawcert.data = Auth_GetKnownCertificate(authority, &sz); if (!rawcert.data) return VH_AUTHORITY_UNKNOWN; if (!Init_GNUTLS()) return VH_UNSUPPORTED; - rawcert.size = sz; qgnutls_pubkey_init(&pubkey); qgnutls_x509_crt_init(&cert); @@ -1437,7 +1434,7 @@ static neterr_t GNUDTLS_Transmit(void *ctx, const qbyte *data, size_t datasize) return NETERR_SENT; } -static neterr_t GNUDTLS_Received(void *ctx, qbyte *data, size_t datasize) +static neterr_t GNUDTLS_Received(void *ctx, sizebuf_t *message) { int cli_addr = 0xdeadbeef; int ret; @@ -1450,7 +1447,7 @@ static neterr_t GNUDTLS_Received(void *ctx, qbyte *data, size_t datasize) memset(&f->prestate, 0, sizeof(f->prestate)); ret = qgnutls_dtls_cookie_verify(&cookie_key, &cli_addr, sizeof(cli_addr), - data, datasize, + message->data, message->cursize, &f->prestate); if (ret < 0) @@ -1473,8 +1470,8 @@ static neterr_t GNUDTLS_Received(void *ctx, qbyte *data, size_t datasize) f->handshaking = true; } - f->readdata = data; - f->readsize = datasize; + f->readdata = message->data; + f->readsize = message->cursize; if (f->handshaking) { @@ -1487,7 +1484,7 @@ static neterr_t GNUDTLS_Received(void *ctx, qbyte *data, size_t datasize) return NETERR_DISCONNECTED; } - ret = qgnutls_record_recv(f->session, net_message_buffer, sizeof(net_message_buffer)); + ret = qgnutls_record_recv(f->session, message->data, message->maxsize); //Sys_Printf("DTLS_Received returned %i of %i\n", ret, f->readsize); f->readsize = 0; if (ret <= 0) @@ -1505,8 +1502,8 @@ static neterr_t GNUDTLS_Received(void *ctx, qbyte *data, size_t datasize) // Sys_Printf("DTLS_Received temp error\n"); return NETERR_CLOGGED; } - net_message.cursize = ret; - data[ret] = 0; + message->cursize = ret; + message->data[ret] = 0; // Sys_Printf("DTLS_Received returned %s\n", data); return NETERR_SENT; } @@ -1540,26 +1537,37 @@ static const dtlsfuncs_t dtlsfuncs_gnutls = GNUDTLS_Received, GNUDTLS_Timeouts, }; -const dtlsfuncs_t *GNUDTLS_InitServer(void) +static const dtlsfuncs_t *GNUDTLS_InitServer(void) { if (!SSL_InitGlobal(true)) return NULL; //unable to init a server certificate. don't allow dtls to init. return &dtlsfuncs_gnutls; } -const dtlsfuncs_t *GNUDTLS_InitClient(void) +static const dtlsfuncs_t *GNUDTLS_InitClient(void) { return &dtlsfuncs_gnutls; } +#else +#define GNUDTLS_InitServer NULL +#define GNUDTLS_InitClient NULL #endif +ftecrypto_t crypto_gnutls = +{ + "GNUTLS", + GNUTLS_OpenVFS, + GNUTLS_GetChannelBinding, + GNUDTLS_InitClient, + GNUDTLS_InitServer, + GNUTLS_VerifyHash, + GNUTLS_GenerateSignature, +}; + #else #warning "GNUTLS version is too old (3.0+ required). Please clean and then recompile with CFLAGS=-DNO_GNUTLS" +ftecrypto_t crypto_gnutls; qboolean SSL_InitGlobal(qboolean isserver) {return false;} -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean isserver) {return NULL;} -int GNUTLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) {return -1;} -const dtlsfuncs_t *GNUDTLS_InitClient(void) {return NULL;} -const dtlsfuncs_t *GNUDTLS_InitServer(void) {return NULL;} #endif #endif diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 2d3c9f0b..79a9c87d 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -996,7 +996,7 @@ static qboolean QDECL SSPI_Close (struct vfsfile_s *file) } #include -vfsfile_t *SSPI_OpenVFS(const char *servername, vfsfile_t *source, qboolean server) +static vfsfile_t *SSPI_OpenVFS(const char *servername, vfsfile_t *source, qboolean server) { sslfile_t *newf; int i = 0; @@ -1071,7 +1071,7 @@ typedef struct _SecPkgContext_Bindings SEC_CHANNEL_BINDINGS *Bindings; } SecPkgContext_Bindings, *PSecPkgContext_Bindings; #endif -int SSPI_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) +static int SSPI_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) { int ret; sslfile_t *f = (sslfile_t*)vf; @@ -1105,59 +1105,7 @@ int SSPI_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize) } #include "netinc.h" -#if 0 -struct fakedtls_s -{ - void *cbctx; - neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize); -}; -static void *FAKEDTLS_CreateContext(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) -{ - struct fakedtls_s *ctx = Z_Malloc(sizeof(*ctx)); - ctx->cbctx = cbctx; - ctx->push = push; - return ctx; -} -static void FAKEDTLS_DestroyContext(void *vctx) -{ - Z_Free(vctx); -} -static neterr_t FAKEDTLS_Transmit(void *vctx, const qbyte *data, size_t datasize) -{ - struct fakedtls_s *ctx = vctx; - neterr_t r; - *(int*)data ^= 0xdeadbeef; - r = ctx->push(ctx->cbctx, data, datasize); - *(int*)data ^= 0xdeadbeef; - return r; -} -static neterr_t FAKEDTLS_Received(void *ctx, qbyte *data, size_t datasize) -{ - *(int*)data ^= 0xdeadbeef; - return NETERR_SENT; -} -static neterr_t FAKEDTLS_Timeouts(void *ctx) -{ -// fakedtls_s *f = (fakedtls_s *)ctx; - return NETERR_SENT; -} -static const dtlsfuncs_t dtlsfuncs_fakedtls = -{ - FAKEDTLS_CreateContext, - FAKEDTLS_DestroyContext, - FAKEDTLS_Transmit, - FAKEDTLS_Received, - FAKEDTLS_Timeouts, -}; -const dtlsfuncs_t *FAKEDTLS_InitServer(void) -{ - return &dtlsfuncs_fakedtls; -} -const dtlsfuncs_t *FAKEDTLS_InitClient(void) -{ - return &dtlsfuncs_fakedtls; -} -#elif defined(HAVE_DTLS) +#if defined(HAVE_DTLS) static void *SSPI_DTLS_CreateContext(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) { int i = 0; @@ -1236,15 +1184,15 @@ static neterr_t SSPI_DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize return ret; } -static neterr_t SSPI_DTLS_Received(void *ctx, qbyte *data, size_t datasize) +static neterr_t SSPI_DTLS_Received(void *ctx, sizebuf_t *msg) { int ret; sslfile_t *f = (sslfile_t *)ctx; //Con_Printf("DTLS_Received: %i\n", datasize); - f->incrypt.data = data; - f->incrypt.avail = f->incrypt.datasize = datasize; + f->incrypt.data = msg->data; + f->incrypt.avail = f->incrypt.datasize = msg->cursize; if (f->handshaking) { @@ -1259,12 +1207,12 @@ static neterr_t SSPI_DTLS_Received(void *ctx, qbyte *data, size_t datasize) SSPI_Decode(f); ret = NETERR_SENT; - memcpy(net_message_buffer, f->inraw.data, f->inraw.avail); - net_message.cursize = f->inraw.avail; + if (f->inraw.avail > msg->maxsize) + msg->cursize = f->inraw.avail; + else + msg->cursize = msg->maxsize; + memcpy(msg->data, f->inraw.data, msg->cursize); f->inraw.avail = 0; - - net_message_buffer[net_message.cursize] = 0; -// Con_Printf("returning %i bytes: %s\n", net_message.cursize, net_message_buffer); } f->incrypt.data = NULL; return ret; @@ -1288,14 +1236,14 @@ static const dtlsfuncs_t dtlsfuncs_schannel = SSPI_DTLS_Received, SSPI_DTLS_Timeouts, }; -const dtlsfuncs_t *SSPI_DTLS_InitServer(void) +/*static const dtlsfuncs_t *SSPI_DTLS_InitServer(void) { //FIXME: at this point, schannel is still returning errors when I try acting as a server. //so just block any attempt to use this as a server. //clients don't need/get certs. - return NULL; -} -const dtlsfuncs_t *SSPI_DTLS_InitClient(void) + return &dtlsfuncs_schannel; +}*/ +static const dtlsfuncs_t *SSPI_DTLS_InitClient(void) { return &dtlsfuncs_schannel; } @@ -1305,12 +1253,11 @@ const dtlsfuncs_t *SSPI_DTLS_InitClient(void) //#include //windows sucks too much to actually include this. oh well. #define STATUS_SUCCESS ((NTSTATUS)0x00000000) #define STATUS_INVALID_SIGNATURE ((NTSTATUS)0xC000A000) -enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize) +static enum hashvalidation_e SSPI_VerifyHash(const qbyte *hashdata, size_t hashsize, const qbyte *pemcert, size_t pemcertsize, const qbyte *signdata, size_t signsize) { NTSTATUS status; BCRYPT_KEY_HANDLE pubkey; - size_t sz; - const char *pem = Auth_GetKnownCertificate(authority, &sz); + const char *pem = pemcert; const char *pemend; qbyte *der; size_t dersize; @@ -1368,7 +1315,7 @@ enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const ch } //yay, now we can do what we actually wanted in the first place. - status = pBCryptVerifySignature(pubkey, NULL, hashdata, hashsize, signdata, signsize, 0); + status = pBCryptVerifySignature(pubkey, NULL, (qbyte*)hashdata, hashsize, (qbyte*)signdata, signsize, 0); pBCryptDestroyKey(pubkey); if (status == STATUS_SUCCESS) return VH_CORRECT; //its okay @@ -1377,4 +1324,14 @@ enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const ch return VH_UNSUPPORTED; //some weird transient error...? } +ftecrypto_t crypto_sspi = +{ + "WinSSPI", + SSPI_OpenVFS, + SSPI_GetChannelBinding, + SSPI_DTLS_InitClient, + NULL,//SSPI_DTLS_InitServer, + SSPI_VerifyHash, + NULL,//SSPI_GenerateHash, +}; #endif diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 6523b376..6b22c836 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -125,7 +125,8 @@ static cvar_t net_dns_ipv6 = CVARD("net_dns_ipv6", "1", "If 0, disables dns cvar_t net_enabled = CVARD("net_enabled", "1", "If 0, disables all network access, including name resolution and socket creation. Does not affect loopback/internal connections."); #if defined(HAVE_SSL) cvar_t tls_ignorecertificateerrors = CVARFD("tls_ignorecertificateerrors", "0", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOUNSAFEEXPAND|CVAR_NOSET, "This should NEVER be set to 1!"); -static cvar_t tls_provider = CVARFD("tls_provider", "0", CVAR_NOTFROMSERVER, "Controls which TLS provider to use.\n0: Auto.\n1: GNUTLS\n2: OpenSSL\n3: SSPI"); +static void QDECL NET_TLS_Provider_Changed(struct cvar_s *var, char *oldvalue); +static cvar_t tls_provider = CVARFCD("tls_provider", "", CVAR_NOTFROMSERVER, NET_TLS_Provider_Changed, "Controls which TLS provider to use."); #endif #if defined(TCPCONNECT) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV)) #ifdef HAVE_SERVER @@ -210,6 +211,64 @@ static neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, co static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to); +static void *cryptolibmodule[cryptolib_count]; +ftecrypto_t *cryptolib[cryptolib_count] = +{ + NULL, + NULL, + NULL, + NULL, +#ifdef HAVE_WINSSPI + &crypto_sspi, +#endif +#ifdef HAVE_GNUTLS + &crypto_gnutls, +#endif +}; +static void NET_TLS_Provider_Changed(struct cvar_s *var, char *oldvalue) +{ + int i; + var->ival = 0; + if (!*var->string || !strcmp(var->string, "0")) + return; + for (i = 0; i < cryptolib_count; i++) + { + if (cryptolib[i] && !Q_strcasecmp(var->string, cryptolib[i]->drivername)) + var->ival = i+1; + } + if (host_initialized && !var->ival) + { + Con_Printf("%s: \"%s\" not loaded, valid values are:", var->name, var->string); + for (i = 0; i < cryptolib_count; i++) + if (cryptolib[i]) + Con_Printf(" %s", cryptolib[i]->drivername); + Con_Printf("\n"); + } +} +qboolean NET_RegisterCrypto(void *module, ftecrypto_t *driver) +{ + int i; + if (!driver) + { + for (i = 0; i < cryptolib_count; i++) + if (cryptolibmodule[i] == module) + cryptolibmodule[i] = NULL, cryptolib[i] = NULL; + Cvar_ForceCallback(&tls_provider); + return true; + } + else + { + for (i = 0; i < cryptolib_count; i++) + if (!cryptolib[i]) + { + cryptolibmodule[i] = module, cryptolib[i] = driver; + Cvar_ForceCallback(&tls_provider); + return true; + } + return false; + } +} + //============================================================================= int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) @@ -2247,6 +2306,7 @@ void *Auth_GetKnownCertificate(const char *certname, size_t *size) qbyte *cert; } certs[] = { //the contents of a -pubcert FILE + //note: not enforced for pk3 files (which we will otherwise happily randomly download of random servers anyway). {"Spike", "-----BEGIN CERTIFICATE-----\n" "MIIDnTCCAgUCCjE1ODQ4ODg2OTEwDQYJKoZIhvcNAQELBQAwEDEOMAwGA1UEAxMF\n" "U3Bpa2UwHhcNMjAwMzIyMTQ1MTMwWhcNMzAwMzIwMTQ1MTMxWjAQMQ4wDAYDVQQD\n" @@ -2329,6 +2389,7 @@ void *TLS_GetKnownCertificate(const char *certname, size_t *size) vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver) { + int i; vfsfile_t *f = NULL; char hostname[MAX_OSPATH]; @@ -2356,18 +2417,13 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver else *hostname = 0; -#ifdef HAVE_GNUTLS - if (!f && (!tls_provider.ival || tls_provider.ival==1)) - f = GNUTLS_OpenVFS(hostname, source, isserver); -#endif -#ifdef HAVE_OPENSSL - if (!f && (!tls_provider.ival || tls_provider.ival==2)) - f = OSSL_OpenVFS(hostname, source, isserver); -#endif -#ifdef HAVE_WINSSPI - if (!f && (!tls_provider.ival || tls_provider.ival==3)) - f = SSPI_OpenVFS(hostname, source, isserver); -#endif + if (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1]) + f = !cryptolib[tls_provider.ival-1]->OpenStream?NULL:cryptolib[tls_provider.ival-1]->OpenStream(hostname, source, isserver); + else for (i = 0; !f && i < cryptolib_count; i++) + { + if (cryptolib[i] && cryptolib[i]->OpenStream) + f = cryptolib[i]->OpenStream(hostname, source, isserver); + } if (!f) //it all failed. { Con_Printf("%s: no tls provider available\n", peername); @@ -2378,18 +2434,12 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize) { int r = -1; -#ifdef HAVE_GNUTLS - if (r == -1) - r = GNUTLS_GetChannelBinding(stream, data, datasize); -#endif -#ifdef HAVE_OPENSSL - if (r == -1) - r = OSSL_GetChannelBinding(stream, data, datasize); -#endif -#ifdef HAVE_WINSSPI - if (r == -1) - r = SSPI_GetChannelBinding(stream, data, datasize); -#endif + int i; + for (i = 0; r==-1 && i < cryptolib_count; i++) + { + if (cryptolib[i] && cryptolib[i]->GetChannelBinding) + r = cryptolib[i]->GetChannelBinding(stream, data, datasize); + } return r; } #endif @@ -2880,35 +2930,27 @@ void NET_DTLS_Timeouts(ftenet_connections_t *col) const dtlsfuncs_t *DTLS_InitServer(void) { const dtlsfuncs_t *f = NULL; -#ifdef HAVE_GNUTLS - if (!f && (!tls_provider.ival || tls_provider.ival==1)) - f = GNUDTLS_InitServer(); -#endif -#ifdef HAVE_OPENSSL - if (!f && (!tls_provider.ival || tls_provider.ival==2)) - f = OSSL_InitServer(); -#endif -#ifdef HAVE_WINSSPI - if (!f && (!tls_provider.ival || tls_provider.ival==3)) - f = SSPI_DTLS_InitServer(); -#endif + int i; + if (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1]) + f = !cryptolib[tls_provider.ival-1]->DTLS_InitServer?NULL:cryptolib[tls_provider.ival-1]->DTLS_InitServer(); + else for (i = 0; !f && i < cryptolib_count; i++) + { + if (cryptolib[i] && cryptolib[i]->DTLS_InitServer) + f = cryptolib[i]->DTLS_InitServer(); + } return f; } const dtlsfuncs_t *DTLS_InitClient(void) { const dtlsfuncs_t *f = NULL; -#ifdef HAVE_GNUTLS - if (!f && (!tls_provider.ival || tls_provider.ival==1)) - f = GNUDTLS_InitClient(); -#endif -#ifdef HAVE_OPENSSL - if (!f && (!tls_provider.ival || tls_provider.ival==2)) - f = OSSL_InitClient(); -#endif -#ifdef HAVE_WINSSPI - if (!f && (!tls_provider.ival || tls_provider.ival==3)) - f = SSPI_DTLS_InitClient(); -#endif + int i; + if (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1]) + f = !cryptolib[tls_provider.ival-1]->DTLS_InitClient?NULL:cryptolib[tls_provider.ival-1]->DTLS_InitClient(); + else for (i = 0; !f && i < cryptolib_count; i++) + { + if (cryptolib[i] && cryptolib[i]->DTLS_InitClient) + f = cryptolib[i]->DTLS_InitClient(); + } return f; } @@ -3013,7 +3055,7 @@ qboolean NET_DTLS_Decode(ftenet_connections_t *col) if (NET_CompareAdr(&peer->addr, &net_from)) { peer->timeout = realtime+timeout.value; //refresh the timeout if our peer is still alive. - switch(peer->funcs->Received(peer->dtlsstate, net_message.data, net_message.cursize)) + switch(peer->funcs->Received(peer->dtlsstate, &net_message)) { case NETERR_DISCONNECTED: Sys_Printf("disconnected %p\n", peer->dtlsstate); @@ -6278,6 +6320,8 @@ ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connections_t hostonly[port-host] = 0; newcon->tcpstreams->clientstream = FS_OpenSSL(hostonly, newcon->tcpstreams->clientstream, false); + if (!newcon->tcpstreams->clientstream) + return NULL; } #endif diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 49362046..077a1f75 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -351,35 +351,32 @@ typedef struct dtlsfuncs_s void *(*CreateContext)(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver); //if remotehost is null then their certificate will not be validated. void (*DestroyContext)(void *ctx); neterr_t (*Transmit)(void *ctx, const qbyte *data, size_t datasize); - neterr_t (*Received)(void *ctx, qbyte *data, size_t datasize); + neterr_t (*Received)(void *ctx, sizebuf_t *message); //operates in-place... neterr_t (*Timeouts)(void *ctx); void (*GetPeerCertificate)(void *ctx); } dtlsfuncs_t; const dtlsfuncs_t *DTLS_InitServer(void); const dtlsfuncs_t *DTLS_InitClient(void); #endif -#ifdef HAVE_WINSSPI - vfsfile_t *SSPI_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver); - int SSPI_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize); - const struct dtlsfuncs_s *SSPI_DTLS_InitServer(void); //returns NULL if there's no cert available. - const struct dtlsfuncs_s *SSPI_DTLS_InitClient(void); //should always return something, if implemented. - enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize); -#endif -#ifdef HAVE_GNUTLS - vfsfile_t *GNUTLS_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver); - int GNUTLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize); - const struct dtlsfuncs_s *GNUDTLS_InitServer(void); //returns NULL if there's no cert available. - const struct dtlsfuncs_s *GNUDTLS_InitClient(void); //should always return something, if implemented. - enum hashvalidation_e GNUTLS_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize); - int GNUTLS_GenerateSignature(qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax); -#endif -#ifdef HAVE_OPENSSL - vfsfile_t *OSSL_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver); - int OSSL_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize); - const struct dtlsfuncs_s *OSSL_InitServer(void); //returns NULL if there's no cert available. - const struct dtlsfuncs_s *OSSL_InitClient(void); //should always return something, if implemented. - enum hashvalidation_e OSSL_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize); -#endif +typedef struct ftecrypto_s +{ + const char *drivername; + + //tlsey things + vfsfile_t *(*OpenStream)(const char *hostname, vfsfile_t *source, qboolean isserver); //establish a tls connection around a tcp stream + int (*GetChannelBinding)(vfsfile_t *vf, qbyte *binddata, size_t *bindsize); //returns -1 if functions don't match those from OpenStream + + //dtls entry points + const struct dtlsfuncs_s *(*DTLS_InitClient)(void); //should always return something, if implemented. + const struct dtlsfuncs_s *(*DTLS_InitServer)(void); //returns NULL if there's no cert available. + + //digital signature stuff. note: uses sha2_512 + enum hashvalidation_e (*VerifyHash)(const qbyte *hashdata, size_t hashsize, const qbyte *certdata, size_t certsize, const qbyte *signdata, size_t signsize); + int (*GenerateSignature)(const qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax); +} ftecrypto_t; +#define cryptolib_count 6 +extern ftecrypto_t crypto_sspi, crypto_gnutls; +extern ftecrypto_t *cryptolib[cryptolib_count]; diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 05b2084d..18ea771a 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -401,6 +401,8 @@ static qboolean QDECL PlugBI_ExportInterface(const char *name, void *interfacept return Media_RegisterEncoder(currentplug, interfaceptr); #endif #endif + if (!strcmp(name, "Crypto")) + return NET_RegisterCrypto(currentplug, interfaceptr); #ifdef HAVE_CLIENT if (!strcmp(name, plugvrfuncs_name)) return R_RegisterVRDriver(currentplug, interfaceptr); @@ -1529,6 +1531,7 @@ void Plug_Close(plugin_t *plug) #ifdef HAVE_CLIENT S_UnregisterSoundInputModule(plug); #endif + NET_RegisterCrypto(plug, NULL); FS_UnRegisterFileSystemModule(plug); Mod_UnRegisterAllModelFormats(plug);