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
This commit is contained in:
Spoike 2014-12-25 20:10:05 +00:00
parent c57dbc7ca4
commit 67bd64d35f
5 changed files with 153 additions and 24 deletions

View File

@ -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 <datafile>\n");
Con_TPrintf ("Usage: download <datafile> <localname>\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)

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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;