From 67bd64d35fc96c536ae2be74cfe4369d981d8191 Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 25 Dec 2014 20:10:05 +0000 Subject: [PATCH] update autoupdate code. now finally using https. triptohell.info's self-signed public cert has been hardcoded, avoiding cert authority mitm attacks (damn corporate proxies!) http downloads supposedly now also supports https. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4806 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_main.c | 34 +++++++++++----- engine/client/sys_win.c | 2 +- engine/common/net_ssl_winsspi.c | 66 ++++++++++++++++++++++++++++--- engine/common/net_wins.c | 5 +++ engine/http/httpclient.c | 70 +++++++++++++++++++++++++++++---- 5 files changed, 153 insertions(+), 24 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 34b65d78a..b17041446 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3194,16 +3194,27 @@ CL_Download_f void CL_Download_f (void) { // char *p, *q; - char *url; - - url = Cmd_Argv(1); + char *url = Cmd_Argv(1); + char *localname = Cmd_Argv(2); #ifdef WEBCLIENT - if (!strnicmp(url, "http://", 7) || !strnicmp(url, "ftp://", 6)) + if (!strnicmp(url, "http://", 7) || !strnicmp(url, "https://", 8) || !strnicmp(url, "ftp://", 6)) { if (Cmd_IsInsecure()) return; - HTTP_CL_Get(url, Cmd_Argv(2), NULL);//"test.txt"); + if (!*localname) + { + localname = strrchr(url, '/'); + if (localname) + localname++; + else + { + Con_TPrintf ("no local name specified\n"); + return; + } + } + + HTTP_CL_Get(url, localname, NULL);//"test.txt"); return; } #endif @@ -3213,6 +3224,9 @@ void CL_Download_f (void) url += 5; } + if (!*localname) + localname = url; + if ((cls.state == ca_disconnected || cls.demoplayback) && cls.demoplayback != DPB_EZTV) { Con_TPrintf ("Must be connected.\n"); @@ -3225,23 +3239,23 @@ void CL_Download_f (void) return; } - if (Cmd_Argc() != 2) + if (Cmd_Argc() != 2 && Cmd_Argc() != 3) { - Con_TPrintf ("Usage: download \n"); + Con_TPrintf ("Usage: download \n"); return; } if (Cmd_IsInsecure()) //mark server specified downloads. { //don't let gamecode order us to download random junk - if (!CL_AllowArbitaryDownload(url)) + if (!CL_AllowArbitaryDownload(localname)) return; - CL_CheckOrEnqueDownloadFile(url, url, DLLF_REQUIRED|DLLF_VERBOSE); + CL_CheckOrEnqueDownloadFile(url, localname, DLLF_REQUIRED|DLLF_VERBOSE); return; } - CL_EnqueDownload(url, url, DLLF_USEREXPLICIT|DLLF_IGNOREFAILED|DLLF_REQUIRED|DLLF_OVERWRITE|DLLF_VERBOSE); + CL_EnqueDownload(url, localname, DLLF_USEREXPLICIT|DLLF_IGNOREFAILED|DLLF_REQUIRED|DLLF_OVERWRITE|DLLF_VERBOSE); } void CL_DownloadSize_f(void) diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index b44ee27be..e57c9950f 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -2512,7 +2512,7 @@ void Win7_TaskListInit(void) #define UPD_BUILDTYPE "rel" #else #define UPD_BUILDTYPE "test" - #define UPDATE_URL "http://triptohell.info/moodles/" + #define UPDATE_URL "https://triptohell.info/moodles/" #define UPDATE_URL_VERSION UPDATE_URL "version.txt" #ifdef _WIN64 #define UPDATE_URL_BUILD UPDATE_URL "win64/fte" EXETYPE "64.exe" diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index e0c28c505..328ec61d5 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -33,7 +33,7 @@ static struct PCCERT_CONTEXT (WINAPI *pCertCreateSelfSignCertificate) (HCRYPTPROV,PCERT_NAME_BLOB,DWORD,PCRYPT_KEY_PROV_INFO,PCRYPT_ALGORITHM_IDENTIFIER,PSYSTEMTIME,PSYSTEMTIME,PCERT_EXTENSIONS); BOOL (WINAPI *pCertStrToNameA) (DWORD,LPCSTR,DWORD,void *,BYTE *,DWORD *,LPCSTR *); } crypt; -static qboolean SSL_Init(void) +void SSL_Init(void) { dllfunction_t secur_functable[] = { @@ -66,7 +66,9 @@ static qboolean SSL_Init(void) secur.lib = Sys_LoadLibrary("secur32.dll", secur_functable); if (!crypt.lib) crypt.lib = Sys_LoadLibrary("crypt32.dll", crypt_functable); - +} +qboolean SSL_Inited(void) +{ return !!secur.lib && !!crypt.lib; } @@ -293,6 +295,58 @@ static void SSPI_Encode(sslfile_t *f) SSPI_TryFlushCryptOut(f); } +//these are known sites that use self-signed certificates, or are special enough that we don't trust corporate networks to hack in their own certificate authority for a proxy/mitm +static struct +{ + wchar_t *hostname; + unsigned int datasize; + qbyte *data; + //FIXME: include expiry information + //FIXME: add alternative when one is about to expire +} knowncerts[] = { + {L"triptohell.info", 933, "\x30\x82\x03\xa1\x30\x82\x02\x89\xa0\x03\x02\x01\x02\x02\x09\x00\x8b\xd0\x05\x63\x62\xd1\x6a\xe3\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x42\x44\x31\x0c\x30\x0a\x06\x03\x55\x04\x08\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x07\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0b\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x03\x0c\x03\x42\x61\x64\x31\x12\x30\x10\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x03\x42\x61\x64\x30\x1e\x17\x0d\x31\x34\x31\x32\x32\x34\x32\x32\x34\x32\x34\x37\x5a\x17\x0d\x32\x34\x31\x32\x32\x31\x32\x32\x34\x32\x34\x37\x5a\x30\x67\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x42\x44\x31\x0c\x30\x0a\x06\x03\x55\x04\x08\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x07\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x0b\x0c\x03\x42\x61\x64\x31\x0c\x30\x0a\x06\x03\x55\x04\x03\x0c\x03\x42\x61\x64\x31\x12\x30\x10\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x03\x42\x61\x64\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xaf\x10\x33\xfa\x39\xf5\xae\x2c\x91\x0e\x20\xe6\x3c\x5c\x7c\x1e\xeb\x16\x50\x2f\x05\x30\xfe\x67\xee\xa9\x00\x54\xd9\x4a\x86\xe6\xba\x80\xfb\x1a\x80\x08\x7e\x7b\x13\xe5\x1a\x18\xc9\xd4\x70\xbd\x5d\xc4\x38\xef\x64\xf1\x90\x2c\x53\x49\x93\x24\x36\x3e\x11\x59\x69\xa6\xdf\x37\xb2\x54\x82\x28\x3e\xdd\x30\x75\xa0\x18\xd8\xe1\xf5\x52\x73\x12\x5b\x37\x68\x1c\x59\xbd\x8c\x73\x66\x47\xbc\xcb\x9c\xfe\x38\x92\x8f\x74\xe9\xd1\x2f\x96\xd2\x5d\x6d\x11\x59\xb2\xdc\xbd\x8c\x37\x5b\x22\x76\x98\xe7\xbe\x08\xef\x1e\x99\xc4\xa9\x77\x2c\x9c\x0e\x08\x3c\x8e\xab\x97\x0c\x6a\xd7\x03\xab\xfd\x4a\x1e\x95\xb2\xc2\x9c\x3a\x16\x65\xd7\xaf\x45\x5f\x6e\xe7\xce\x51\xba\xa0\x60\x43\x0e\x07\xc5\x0b\x0a\x82\x05\x26\xc4\x92\x0a\x27\x5b\xfc\x57\x6c\xdf\xe2\x54\x8a\xef\x38\xf1\xf8\xc4\xf8\x51\x16\x27\x1f\x78\x89\x7c\x5b\xd7\x53\xcd\x9b\x54\x2a\xe6\x71\xee\xe4\x56\x2e\xa4\x09\x1a\x61\xf7\x0f\x97\x22\x94\xd7\xef\x21\x6c\xe6\x81\xfb\x54\x5f\x09\x92\xac\xd2\x7c\xab\xd5\xa9\x81\xf4\xc9\xb7\xd6\xbf\x68\xf8\x4f\xdc\xf3\x60\xa3\x3b\x29\x92\x9e\xdd\xa2\xa3\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x19\xed\xd0\x7b\x16\xaf\xb5\x0c\x9a\xe8\xd3\x46\x2e\x3c\x64\x29\xb6\xc1\x73\x5a\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x19\xed\xd0\x7b\x16\xaf\xb5\x0c\x9a\xe8\xd3\x46\x2e\x3c\x64\x29\xb6\xc1\x73\x5a\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x62\xa7\x26\xeb\xd4\x03\x29\x9c\x09\x33\x69\x7a\x9c\x65\x68\xec\x4c\xb9\x06\xeb\x1e\x51\x6f\x78\x20\xdc\xf6\x44\x5e\x06\x6e\x53\x87\x73\xe6\x14\x15\xb9\x17\x74\x67\xe0\x4e\x48\x38\xbc\x1c\xbd\xd0\xad\xd6\xbd\x8c\xf0\x3a\xe0\x13\x73\x19\xad\x8b\x79\x68\x67\x65\x9b\x7a\x4c\x81\xfb\xd9\x92\x77\x89\xb5\xb0\x53\xb0\xa5\xf7\x2d\x8e\x29\x60\x31\xd1\x9b\x2f\x63\x8a\x5f\x64\xc1\x61\xd5\xb7\xdf\x70\x3b\x2b\xf6\x1a\x96\xb9\xa7\x08\xca\x87\xa6\x8c\x60\xca\x6e\xd7\xee\xba\xef\x89\x0b\x93\xd5\xfd\xfc\x14\xba\xef\x27\xba\x90\x11\x90\xf7\x25\x70\xe7\x4e\xf4\x9c\x13\x27\xc1\xa7\x8e\xd9\x66\x43\x72\x20\x5b\xe1\x5c\x73\x74\xf5\x33\xf2\xa5\xf6\xe1\xd5\xac\xf3\x67\x5c\xe7\xd4\x0a\x8d\x91\x73\x03\x3e\x9d\xbc\x96\xc3\x0c\xdb\xd5\x77\x6e\x76\x44\x69\xaf\x24\x0f\x4f\x8b\x47\x36\x8b\xc3\xd6\x36\xdd\x26\x5a\x9c\xdd\x9c\x43\xee\x29\x43\xdd\x75\x2f\x19\x52\xfc\x1d\x24\x9c\x13\x29\x99\xa0\x6d\x7a\x95\xcc\xa0\x58\x86\xd8\xc5\xb9\xa3\xc2\x3d\x64\x1d\x85\x8a\xca\x53\x55\x8e\x9a\x6d\xc9\x91\x73\xf4\xe1\xe1\xa4\x9b\x76\xfc\x7f\x63\xc2\xb9\x23"}, + {NULL} +}; + +char *narrowen(char *out, size_t outlen, wchar_t *wide); +static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize) +{ + int i; + for (i = 0; knowncerts[i].hostname; i++) + { + if (!wcscmp(domain, knowncerts[i].hostname)) + { +#ifdef _DEBUG + if (!knowncerts[i].data) + { + int j; + Con_Printf("%ls cert %i bytes\n", domain, datasize); + + Con_Printf("\"", datasize); + for (j = 0; j < datasize; j++) + Con_Printf("\\x%02x", data[j]); + Con_Printf("\"\n", datasize); + continue; + } +#endif + if (knowncerts[i].datasize != datasize || memcmp(data, knowncerts[i].data, datasize)) + { + if (status != CERT_E_EXPIRED) + Con_Printf("%ls has an unexpected certificate\n", domain); + if (status == SEC_E_OK) + status = TRUST_E_FAIL; + } + else + { + if (status == CERT_E_UNTRUSTEDROOT) + status = SEC_E_OK; + break; + } + } + } + return status; +} + static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags) { HTTPSPolicyCallbackData polHttps; @@ -349,11 +403,11 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe } else { - if (PolicyStatus.dwError) + Status = VerifyKnownCertificates(PolicyStatus.dwError, pwszServerName, pServerCert->pbCertEncoded, pServerCert->cbCertEncoded); + if (Status) { char fmsg[512]; char *err; - Status = PolicyStatus.dwError; switch (Status) { case CERT_E_EXPIRED: err = "CERT_E_EXPIRED"; break; @@ -378,7 +432,7 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe case CERT_E_WRONG_USAGE: err = "CERT_E_WRONG_USAGE"; break; default: err = "(unknown)"; break; } - Con_Printf("Error verifying certificate for '%S': %s\n", pwszServerName, err); + Con_Printf("Error verifying certificate for '%ls': %s\n", pwszServerName, err); if (tls_ignorecertificateerrors->ival) { @@ -734,7 +788,7 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server) int err; unsigned int c; - if (!source || !SSL_Init()) + if (!source || !SSL_Inited()) { VFS_CLOSE(source); return NULL; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 3a76ec03c..6f1c47e6e 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -5510,6 +5510,7 @@ void NET_UPNPIGP_Callback(cvar_t *var, char *oldval) } cvar_t net_upnpigp = CVARCD("net_upnpigp", "0", NET_UPNPIGP_Callback, "If set, enables the use of the upnp-igd protocol to punch holes in your local NAT box."); +void SSL_Init(void); /* ==================== NET_Init @@ -5556,6 +5557,10 @@ void NET_Init (void) Cvar_Register (&net_upnpigp, "networking"); net_upnpigp.restriction = RESTRICT_MAX; +#if defined(HAVE_WINSSPI) + SSL_Init(); +#endif + Net_Master_Init(); } #ifndef SERVERONLY diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 356ea1e77..c23113e3d 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -279,7 +279,11 @@ It doesn't use persistant connections. struct http_dl_ctx_s { struct dl_download *dlctx; +#if 1 + vfsfile_t *sock; +#else SOCKET sock; //FIXME: support https. +#endif char *buffer; @@ -304,9 +308,15 @@ void HTTP_Cleanup(struct dl_download *dl) struct http_dl_ctx_s *con = dl->ctx; dl->ctx = NULL; +#if 1 + if (con->sock) + VFS_CLOSE(con->sock); + con->sock = NULL; +#else if (con->sock != INVALID_SOCKET) closesocket(con->sock); con->sock = INVALID_SOCKET; +#endif free(con->buffer); free(con); @@ -336,7 +346,15 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) switch(con->state) { case HC_REQUESTING: +#if 1 + ammount = VFS_WRITE(con->sock, con->buffer, con->bufferused); + if (!ammount) + return true; + if (ammount < 0) + return false; +#else ammount = send(con->sock, con->buffer, con->bufferused, 0); + if (!ammount) return false; @@ -346,6 +364,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) return false; return true; } +#endif con->bufferused -= ammount; memmove(con->buffer, con->buffer+ammount, con->bufferused); @@ -357,6 +376,13 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (con->bufferlen - con->bufferused < 1530) ExpandBuffer(con, 1530); +#if 1 + ammount = VFS_READ(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-15); + if (!ammount) + return true; + if (ammount < 0) + return false; +#else ammount = recv(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-15, 0); if (!ammount) return false; @@ -366,6 +392,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) return false; return true; } +#endif con->bufferused+=ammount; con->buffer[con->bufferused] = '\0'; @@ -556,6 +583,13 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (con->bufferlen - con->bufferused < 1530) ExpandBuffer(con, 1530); +#if 1 + ammount = VFS_READ(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-1); + if (ammount == 0) + return true; //no data yet + else if (ammount < 0) + ammount = 0; //error (EOF?) +#else ammount = recv(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-1, 0); if (ammount < 0) { @@ -563,6 +597,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) return false; return true; } +#endif con->bufferused+=ammount; @@ -643,7 +678,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) else { #if !defined(NPFTE) && defined(AVAIL_ZLIB) - if (con->gzip) + if (con->gzip && con->file) { VFS_SEEK(con->file, 0); dl->file = FS_DecompressGZip(con->file, dl->file); @@ -664,11 +699,12 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) void HTTPDL_Establish(struct dl_download *dl) { - unsigned long _true = true; - struct sockaddr_qstorage serveraddr; +// unsigned long _true = true; +// struct sockaddr_qstorage serveraddr; +// int addressfamily; +// int addresssize; struct http_dl_ctx_s *con; - int addressfamily; - int addresssize; + qboolean https = false; char server[128]; char uri[MAX_OSPATH]; @@ -677,7 +713,12 @@ void HTTPDL_Establish(struct dl_download *dl) if (!*url) url = dl->url; - if (!strnicmp(url, "http://", 7)) + if (!strnicmp(url, "https://", 8)) + { + url+=8; + https = true; + } + else if (!strnicmp(url, "http://", 7)) url+=7; slash = strchr(url, '/'); @@ -700,6 +741,19 @@ void HTTPDL_Establish(struct dl_download *dl) dl->status = DL_RESOLVING; +#if 1 + if (https) + { + //https uses port 443 instead of 80 by default + con->sock = FS_OpenTCP(server, 443); + //and with an extra ssl/tls layer between tcp and http. + con->sock = FS_OpenSSL(server, con->sock, false); + } + else + { + con->sock = FS_OpenTCP(server, 80); + } +#else if (!NET_StringToSockaddr(server, 80, &serveraddr, &addressfamily, &addresssize)) { dl->status = DL_FAILED; @@ -744,7 +798,7 @@ void HTTPDL_Establish(struct dl_download *dl) dl->status = DL_FAILED; return; } - +#endif if (dl->postdata) { ExpandBuffer(con, 1024 + strlen(uri) + strlen(server) + strlen(con->dlctx->postmimetype) + dl->postlen); @@ -1001,6 +1055,8 @@ qboolean DL_Decide(struct dl_download *dl) else*/ if (!strnicmp(url, "http://", 7)) dl->poll = HTTPDL_Poll; + else if (!strnicmp(url, "https://", 7)) + dl->poll = HTTPDL_Poll; else { dl->status = DL_FAILED;