X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=crypto.c;h=21b70954fc9045e163faf138591ece7bc0828051;hp=8f7deba70afd34117a06dd1d3464b7d314e494d5;hb=0303750a7b40951618dca290c172a7afa60e9aa8;hpb=cf5839b5517e744166950ecb7907ac4e24ea8506 diff --git a/crypto.c b/crypto.c index 8f7deba7..21b70954 100644 --- a/crypto.c +++ b/crypto.c @@ -1,3 +1,24 @@ +/* +Copyright (C) 2010-2015 Rudolf Polzer (divVerent) +Copyright (C) 2010-2020 Ashley Rose Hale (LadyHavoc) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + #include "quakedef.h" #include "crypto.h" #include "common.h" @@ -6,17 +27,22 @@ #include "hmac.h" #include "libcurl.h" -cvar_t crypto_developer = {CVAR_SAVE, "crypto_developer", "0", "print extra info about crypto handshake"}; -cvar_t crypto_servercpupercent = {CVAR_SAVE, "crypto_servercpupercent", "10", "allowed crypto CPU load in percent for server operation (0 = no limit, faster)"}; -cvar_t crypto_servercpumaxtime = {CVAR_SAVE, "crypto_servercpumaxtime", "0.01", "maximum allowed crypto CPU time per frame (0 = no limit)"}; -cvar_t crypto_servercpudebug = {CVAR_SAVE, "crypto_servercpudebug", "0", "print statistics about time usage by crypto"}; +cvar_t crypto_developer = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_developer", "0", "print extra info about crypto handshake"}; +cvar_t crypto_aeslevel = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; + +cvar_t crypto_servercpupercent = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_servercpupercent", "10", "allowed crypto CPU load in percent for server operation (0 = no limit, faster)"}; +cvar_t crypto_servercpumaxtime = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_servercpumaxtime", "0.01", "maximum allowed crypto CPU time per frame (0 = no limit)"}; +cvar_t crypto_servercpudebug = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_servercpudebug", "0", "print statistics about time usage by crypto"}; static double crypto_servercpu_accumulator = 0; static double crypto_servercpu_lastrealtime = 0; -cvar_t crypto_aeslevel = {CVAR_SAVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; + +extern cvar_t net_sourceaddresscheck; + int crypto_keyfp_recommended_length; static const char *crypto_idstring = NULL; static char crypto_idstring_buf[512]; + #define PROTOCOL_D0_BLIND_ID FOURCC_D0PK #define PROTOCOL_VLEN (('v' << 0) | ('l' << 8) | ('e' << 16) | ('n' << 24)) @@ -153,7 +179,7 @@ static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, co // d0_blind_id interface #define D0_EXPORT -#ifdef __GNUC__ +#if defined (__GNUC__) || (__clang__) || (__TINYC__) #define D0_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else #define D0_WARN_UNUSED_RESULT @@ -254,7 +280,7 @@ static dllfunction_t d0_blind_id_funcs[] = // end of d0_blind_id interface static dllhandle_t d0_blind_id_dll = NULL; -static qboolean Crypto_OpenLibrary (void) +static qbool Crypto_OpenLibrary (void) { const char* dllnames [] = { @@ -274,12 +300,12 @@ static qboolean Crypto_OpenLibrary (void) return true; // Load the DLL - return Sys_LoadLibrary (dllnames, &d0_blind_id_dll, d0_blind_id_funcs); + return Sys_LoadDependency (dllnames, &d0_blind_id_dll, d0_blind_id_funcs); } static void Crypto_CloseLibrary (void) { - Sys_UnloadLibrary (&d0_blind_id_dll); + Sys_FreeLibrary (&d0_blind_id_dll); } #endif @@ -323,7 +349,7 @@ static dllfunction_t d0_rijndael_funcs[] = // end of d0_blind_id interface static dllhandle_t d0_rijndael_dll = NULL; -static qboolean Crypto_Rijndael_OpenLibrary (void) +static qbool Crypto_Rijndael_OpenLibrary (void) { const char* dllnames [] = { @@ -343,12 +369,12 @@ static qboolean Crypto_Rijndael_OpenLibrary (void) return true; // Load the DLL - return Sys_LoadLibrary (dllnames, &d0_rijndael_dll, d0_rijndael_funcs); + return Sys_LoadDependency (dllnames, &d0_rijndael_dll, d0_rijndael_funcs); } static void Crypto_Rijndael_CloseLibrary (void) { - Sys_UnloadLibrary (&d0_rijndael_dll); + Sys_FreeLibrary (&d0_rijndael_dll); } #endif @@ -359,7 +385,7 @@ void sha256(unsigned char *out, const unsigned char *in, int n) qd0_blind_id_util_sha256((char *) out, (const char *) in, n); } -static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax, qboolean inuserdir) +static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax, qbool inuserdir) { char vabuf[1024]; qfile_t *f = NULL; @@ -377,7 +403,7 @@ static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax, qboolean return (size_t) n; } -static qboolean PutWithNul(char **data, size_t *len, const char *str) +static qbool PutWithNul(char **data, size_t *len, const char *str) { // invariant: data points to insertion point size_t l = strlen(str); @@ -440,7 +466,7 @@ static d0_blind_id_t *Crypto_ReadPublicKey(char *buf, size_t len) } // d0si reading -static qboolean Crypto_AddPrivateKey(d0_blind_id_t *pk, char *buf, size_t len) +static qbool Crypto_AddPrivateKey(d0_blind_id_t *pk, char *buf, size_t len) { const char *p[1]; size_t l[1]; @@ -455,8 +481,8 @@ static qboolean Crypto_AddPrivateKey(d0_blind_id_t *pk, char *buf, size_t len) #define MAX_PUBKEYS 16 static d0_blind_id_t *pubkeys[MAX_PUBKEYS]; static char pubkeys_fp64[MAX_PUBKEYS][FP64_SIZE+1]; -static qboolean pubkeys_havepriv[MAX_PUBKEYS]; -static qboolean pubkeys_havesig[MAX_PUBKEYS]; +static qbool pubkeys_havepriv[MAX_PUBKEYS]; +static qbool pubkeys_havesig[MAX_PUBKEYS]; static char pubkeys_priv_fp64[MAX_PUBKEYS][FP64_SIZE+1]; static char challenge_append[1400]; static size_t challenge_append_length; @@ -488,7 +514,8 @@ typedef struct int next_step; char challenge[2048]; char wantserver_idfp[FP64_SIZE+1]; - qboolean wantserver_aes; + qbool wantserver_aes; + qbool wantserver_issigned; int cdata_id; } crypto_data_t; @@ -498,7 +525,7 @@ crypto_data_t; #define MAKE_CDATA if(!crypto->data) crypto->data = Z_Malloc(sizeof(crypto_data_t)) #define CLEAR_CDATA if(crypto->data) { if(CDATA->id) qd0_blind_id_free(CDATA->id); Z_Free(crypto->data); } crypto->data = NULL -static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qboolean allow_create) +static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qbool allow_create) { crypto_t *crypto; int i, best; @@ -512,7 +539,7 @@ static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qboolean if(i < MAX_CRYPTOCONNECTS && (allow_create || cryptoconnects[i].crypto.data)) { crypto = &cryptoconnects[i].crypto; - cryptoconnects[i].lasttime = realtime; + cryptoconnects[i].lasttime = host.realtime; return crypto; } if(!allow_create) @@ -522,13 +549,13 @@ static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qboolean if(cryptoconnects[i].lasttime < cryptoconnects[best].lasttime) best = i; crypto = &cryptoconnects[best].crypto; - cryptoconnects[best].lasttime = realtime; + cryptoconnects[best].lasttime = host.realtime; memcpy(&cryptoconnects[best].address, peeraddress, sizeof(cryptoconnects[best].address)); CLEAR_CDATA; return crypto; } -qboolean Crypto_FinishInstance(crypto_t *out, crypto_t *crypto) +qbool Crypto_FinishInstance(crypto_t *out, crypto_t *crypto) { // no check needed here (returned pointers are only used in prefilled fields) if(!crypto || !crypto->authenticated) @@ -556,6 +583,7 @@ typedef struct crypto_storedhostkey_s int keyid; char idfp[FP64_SIZE+1]; int aeslevel; + qbool issigned; } crypto_storedhostkey_t; static crypto_storedhostkey_t *crypto_storedhostkey_hashtable[CRYPTO_HOSTKEY_HASHSIZE]; @@ -582,12 +610,12 @@ static void Crypto_ClearHostKeys(void) } } -static qboolean Crypto_ClearHostKey(lhnetaddress_t *peeraddress) +static qbool Crypto_ClearHostKey(lhnetaddress_t *peeraddress) { char buf[128]; int hashindex; crypto_storedhostkey_t **hkp; - qboolean found = false; + qbool found = false; LHNETADDRESS_ToString(peeraddress, buf, sizeof(buf), 1); hashindex = CRC_Block((const unsigned char *) buf, strlen(buf)) % CRYPTO_HOSTKEY_HASHSIZE; @@ -604,7 +632,7 @@ static qboolean Crypto_ClearHostKey(lhnetaddress_t *peeraddress) return found; } -static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystring, qboolean complain) +static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystring, qbool complain) { char buf[128]; int hashindex; @@ -612,6 +640,7 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri int keyid; char idfp[FP64_SIZE+1]; int aeslevel; + qbool issigned; if(!d0_blind_id_dll) return; @@ -626,10 +655,12 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri ++keystring; keyid = -1; + issigned = false; while(*keystring && keyid < 0) { // id@key const char *idstart, *idend, *keystart, *keyend; + qbool thisissigned = true; ++keystring; // skip the space idstart = keystring; while(*keystring && *keystring != ' ' && *keystring != '@') @@ -643,14 +674,23 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri ++keystring; keyend = keystring; + if (keystart[0] == '~') + { + thisissigned = false; + ++keystart; + } + if(idend - idstart == FP64_SIZE && keyend - keystart == FP64_SIZE) { - for(keyid = MAX_PUBKEYS - 1; keyid >= 0; --keyid) - if(pubkeys[keyid]) - if(!memcmp(pubkeys_fp64[keyid], keystart, FP64_SIZE)) + int thiskeyid; + for(thiskeyid = MAX_PUBKEYS - 1; thiskeyid >= 0; --thiskeyid) + if(pubkeys[thiskeyid]) + if(!memcmp(pubkeys_fp64[thiskeyid], keystart, FP64_SIZE)) { memcpy(idfp, idstart, FP64_SIZE); idfp[FP64_SIZE] = 0; + keyid = thiskeyid; + issigned = thisissigned; break; } // If this failed, keyid will be -1. @@ -672,8 +712,11 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri Con_Printf("Server %s tried to change the host key to a value not in the host cache. Connecting to it will fail. To accept the new host key, do crypto_hostkey_clear %s\n", buf, buf); if(hk->aeslevel > aeslevel) Con_Printf("Server %s tried to reduce encryption status, not accepted. Connecting to it will fail. To accept, do crypto_hostkey_clear %s\n", buf, buf); + if(hk->issigned > issigned) + Con_Printf("Server %s tried to reduce signature status, not accepted. Connecting to it will fail. To accept, do crypto_hostkey_clear %s\n", buf, buf); } hk->aeslevel = max(aeslevel, hk->aeslevel); + hk->issigned = issigned; return; } @@ -684,10 +727,11 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri memcpy(hk->idfp, idfp, FP64_SIZE+1); hk->next = crypto_storedhostkey_hashtable[hashindex]; hk->aeslevel = aeslevel; + hk->issigned = issigned; crypto_storedhostkey_hashtable[hashindex] = hk; } -qboolean Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, int *aeslevel) +qbool Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, int *aeslevel, qbool *issigned) { char buf[128]; int hashindex; @@ -711,10 +755,12 @@ qboolean Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *k strlcpy(idfp, hk->idfp, idfplen); if(aeslevel) *aeslevel = hk->aeslevel; + if(issigned) + *issigned = hk->issigned; return true; } -int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, qboolean *issigned) // return value: -1 if more to come, +1 if valid, 0 if end of list +int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, qbool *issigned) // return value: -1 if more to come, +1 if valid, 0 if end of list { if(keyid < 0 || keyid >= MAX_PUBKEYS) return 0; @@ -765,7 +811,7 @@ static void Crypto_BuildChallengeAppend(void) challenge_append_length = p - challenge_append; } -static qboolean Crypto_SavePubKeyTextFile(int i) +static qbool Crypto_SavePubKeyTextFile(int i) { qfile_t *f; char vabuf[1024]; @@ -793,6 +839,19 @@ static qboolean Crypto_SavePubKeyTextFile(int i) return true; } +static void Crypto_BuildIdString(void) +{ + int i; + char vabuf[1024]; + + crypto_idstring = NULL; + dpsnprintf(crypto_idstring_buf, sizeof(crypto_idstring_buf), "%d", d0_rijndael_dll ? crypto_aeslevel.integer : 0); + for (i = 0; i < MAX_PUBKEYS; ++i) + if (pubkeys[i]) + strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s%s", pubkeys_priv_fp64[i], pubkeys_havesig[i] ? "" : "~", pubkeys_fp64[i]), sizeof(crypto_idstring_buf)); + crypto_idstring = crypto_idstring_buf; +} + void Crypto_LoadKeys(void) { char buf[8192]; @@ -814,8 +873,6 @@ void Crypto_LoadKeys(void) // PUBLIC KEYS to accept (including modulus) // PRIVATE KEY of user - crypto_idstring = NULL; - dpsnprintf(crypto_idstring_buf, sizeof(crypto_idstring_buf), "%d", d0_rijndael_dll ? crypto_aeslevel.integer : 0); for(i = 0; i < MAX_PUBKEYS; ++i) { memset(pubkeys_fp64[i], 0, sizeof(pubkeys_fp64[i])); @@ -845,12 +902,10 @@ void Crypto_LoadKeys(void) if(qd0_blind_id_verify_private_id(pubkeys[i]) && qd0_blind_id_verify_public_id(pubkeys[i], &status)) { pubkeys_havepriv[i] = true; - strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[i], pubkeys_fp64[i]), sizeof(crypto_idstring_buf)); + pubkeys_havesig[i] = status; // verify the key we just got (just in case) - if(status) - pubkeys_havesig[i] = true; - else + if(!status) Con_Printf("NOTE: this ID has not yet been signed!\n"); Crypto_SavePubKeyTextFile(i); @@ -879,9 +934,9 @@ void Crypto_LoadKeys(void) } } } - crypto_idstring = crypto_idstring_buf; keygen_i = -1; + Crypto_BuildIdString(); Crypto_BuildChallengeAppend(); // find a good prefix length for all the keys we know (yes, algorithm is not perfect yet, may yield too long prefix length) @@ -1039,7 +1094,7 @@ void Crypto_Init(void) } // end -qboolean Crypto_Available(void) +qbool Crypto_Available(void) { if(!d0_blind_id_dll) return false; @@ -1068,17 +1123,17 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch return; } - if(keygen_i >= MAX_PUBKEYS || !pubkeys[keygen_i]) + if(keygen_i < 0) { - Con_Printf("overflow of keygen_i\n"); - keygen_i = -1; + Con_Printf("Unexpected response from keygen server:\n"); + Com_HexDumpToConsole(buffer, (int)length_received); SV_UnlockThreadMutex(); return; } - if(keygen_i < 0) + if(keygen_i >= MAX_PUBKEYS || !pubkeys[keygen_i]) { - Con_Printf("Unexpected response from keygen server:\n"); - Com_HexDumpToConsole(buffer, (int)length_received); + Con_Printf("overflow of keygen_i\n"); + keygen_i = -1; SV_UnlockThreadMutex(); return; } @@ -1153,11 +1208,13 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch Con_Printf("Saved to key_%d.d0si%s\n", keygen_i, sessionid.string); + Crypto_BuildIdString(); + keygen_i = -1; SV_UnlockThreadMutex(); } -static void Crypto_KeyGen_f(void) +static void Crypto_KeyGen_f(cmd_state_t *cmd) { int i; const char *p[1]; @@ -1175,14 +1232,14 @@ static void Crypto_KeyGen_f(void) Con_Print("libd0_blind_id DLL not found, this command is inactive.\n"); return; } - if(Cmd_Argc() != 3) + if(Cmd_Argc(cmd) != 3) { - Con_Printf("usage:\n%s id url\n", Cmd_Argv(0)); + Con_Printf("usage:\n%s id url\n", Cmd_Argv(cmd, 0)); return; } SV_LockThreadMutex(); Crypto_LoadKeys(); - i = atoi(Cmd_Argv(1)); + i = atoi(Cmd_Argv(cmd, 1)); if(!pubkeys[i]) { Con_Printf("there is no public key %d\n", i); @@ -1282,8 +1339,8 @@ static void Crypto_KeyGen_f(void) SV_UnlockThreadMutex(); return; } - buf2pos = strlen(Cmd_Argv(2)); - memcpy(buf2, Cmd_Argv(2), buf2pos); + buf2pos = strlen(Cmd_Argv(cmd, 2)); + memcpy(buf2, Cmd_Argv(cmd, 2), buf2pos); if(!(buf2l = Crypto_UnParsePack(buf2 + buf2pos, sizeof(buf2) - buf2pos - 1, FOURCC_D0IQ, p, l, 1))) { Con_Printf("Crypto_UnParsePack failed\n"); @@ -1313,14 +1370,14 @@ static void Crypto_KeyGen_f(void) // end // console commands -static void Crypto_Reload_f(void) +static void Crypto_Reload_f(cmd_state_t *cmd) { Crypto_ClearHostKeys(); Crypto_UnloadKeys(); Crypto_LoadKeys(); } -static void Crypto_Keys_f(void) +static void Crypto_Keys_f(cmd_state_t *cmd) { int i; if(!d0_blind_id_dll) @@ -1343,7 +1400,7 @@ static void Crypto_Keys_f(void) } } -static void Crypto_HostKeys_f(void) +static void Crypto_HostKeys_f(cmd_state_t *cmd) { int i; crypto_storedhostkey_t *hk; @@ -1368,7 +1425,7 @@ static void Crypto_HostKeys_f(void) } } -static void Crypto_HostKey_Clear_f(void) +static void Crypto_HostKey_Clear_f(cmd_state_t *cmd) { lhnetaddress_t addr; int i; @@ -1379,12 +1436,12 @@ static void Crypto_HostKey_Clear_f(void) return; } - for(i = 1; i < Cmd_Argc(); ++i) + for(i = 1; i < Cmd_Argc(cmd); ++i) { - LHNETADDRESS_FromString(&addr, Cmd_Argv(i), 26000); + LHNETADDRESS_FromString(&addr, Cmd_Argv(cmd, i), 26000); if(Crypto_ClearHostKey(&addr)) { - Con_Printf("cleared host key for %s\n", Cmd_Argv(i)); + Con_Printf("cleared host key for %s\n", Cmd_Argv(cmd, i)); } } } @@ -1393,11 +1450,12 @@ void Crypto_Init_Commands(void) { if(d0_blind_id_dll) { - Cmd_AddCommand("crypto_reload", Crypto_Reload_f, "reloads cryptographic keys"); - Cmd_AddCommand("crypto_keygen", Crypto_KeyGen_f, "generates and saves a cryptographic key"); - Cmd_AddCommand("crypto_keys", Crypto_Keys_f, "lists the loaded keys"); - Cmd_AddCommand("crypto_hostkeys", Crypto_HostKeys_f, "lists the cached host keys"); - Cmd_AddCommand("crypto_hostkey_clear", Crypto_HostKey_Clear_f, "clears a cached host key"); + Cmd_AddCommand(CF_SHARED, "crypto_reload", Crypto_Reload_f, "reloads cryptographic keys"); + Cmd_AddCommand(CF_SHARED, "crypto_keygen", Crypto_KeyGen_f, "generates and saves a cryptographic key"); + Cmd_AddCommand(CF_SHARED, "crypto_keys", Crypto_Keys_f, "lists the loaded keys"); + Cmd_AddCommand(CF_SHARED, "crypto_hostkeys", Crypto_HostKeys_f, "lists the cached host keys"); + Cmd_AddCommand(CF_SHARED, "crypto_hostkey_clear", Crypto_HostKey_Clear_f, "clears a cached host key"); + Cvar_RegisterVariable(&crypto_developer); if(d0_rijndael_dll) Cvar_RegisterVariable(&crypto_aeslevel); @@ -1635,7 +1693,7 @@ const char *Crypto_GetInfoResponseDataString(void) } // network protocol -qboolean Crypto_ServerAppendToChallenge(const char *data_in, size_t len_in, char *data_out, size_t *len_out, size_t maxlen_out) +qbool Crypto_ServerAppendToChallenge(const char *data_in, size_t len_in, char *data_out, size_t *len_out, size_t maxlen_out) { // cheap op, all is precomputed if(!d0_blind_id_dll) @@ -1691,12 +1749,12 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, return CRYPTO_NOMATCH; // will be later accepted if encryption was set up // validate the challenge for (i = 0;i < MAX_CHALLENGES;i++) - if(challenge[i].time > 0) - if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s)) + if(challenges[i].time > 0) + if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s)) break; // if the challenge is not recognized, drop the packet if (i == MAX_CHALLENGES) // challenge mismatch is silent - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing challenge in connect"); crypto = Crypto_ServerFindInstance(peeraddress, false); if(!crypto || !crypto->authenticated) @@ -1711,23 +1769,23 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, id = (cnt ? atoi(cnt) : -1); cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)); if(!cnt) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing cnt in d0pk"); GetUntilNul(&data_in, &len_in); if(!data_in) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing appended data in d0pk"); if(!strcmp(cnt, "0")) { int i; if (!(s = InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue)))) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing challenge in d0pk\\0"); // validate the challenge for (i = 0;i < MAX_CHALLENGES;i++) - if(challenge[i].time > 0) - if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s)) + if(challenges[i].time > 0) + if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s)) break; // if the challenge is not recognized, drop the packet - if (i == MAX_CHALLENGES) // challenge mismatch is silent - return CRYPTO_DISCARD; // pre-challenge: rather be silent + if (i == MAX_CHALLENGES) + return Crypto_SoftServerError(data_out, len_out, "invalid challenge in d0pk\\0"); if (!(s = InfoString_GetValue(string + 4, "aeslevel", infostringvalue, sizeof(infostringvalue)))) aeslevel = 0; // not supported @@ -1802,6 +1860,7 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, // I am the server, and my key is ok... so let's set server_keyfp and server_idfp strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp)); strlcpy(crypto->server_idfp, pubkeys_priv_fp64[CDATA->s], sizeof(crypto->server_idfp)); + crypto->server_issigned = pubkeys_havesig[CDATA->s]; if(!CDATA->id) CDATA->id = qd0_blind_id_new(); @@ -1942,10 +2001,9 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, CLEAR_CDATA; return Crypto_ServerError(data_out, len_out, "d0_blind_id_authenticate_with_private_id_verify failed (authentication error)", "Authentication error"); } - if(status) - strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp)); - else - crypto->client_keyfp[0] = 0; + strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp)); + crypto->client_issigned = status; + memset(crypto->client_idfp, 0, sizeof(crypto->client_idfp)); fpbuflen = FP64_SIZE; if(!qd0_blind_id_fingerprint64_public_id(CDATA->id, crypto->client_idfp, &fpbuflen)) @@ -1983,8 +2041,8 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, double t = 0; static double complain_time = 0; const char *cnt; - qboolean do_time = false; - qboolean do_reject = false; + qbool do_time = false; + qbool do_reject = false; char infostringvalue[MAX_INPUTLINE]; if(crypto_servercpupercent.value > 0 || crypto_servercpumaxtime.value > 0) if(len_in > 5 && !memcmp(data_in, "d0pk\\", 5)) @@ -2000,7 +2058,7 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, // check if we may perform crypto... if(crypto_servercpupercent.value > 0) { - crypto_servercpu_accumulator += (realtime - crypto_servercpu_lastrealtime) * crypto_servercpupercent.value * 0.01; + crypto_servercpu_accumulator += (host.realtime - crypto_servercpu_lastrealtime) * crypto_servercpupercent.value * 0.01; if(crypto_servercpumaxtime.value) if(crypto_servercpu_accumulator > crypto_servercpumaxtime.value) crypto_servercpu_accumulator = crypto_servercpumaxtime.value; @@ -2008,13 +2066,13 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, else { if(crypto_servercpumaxtime.value > 0) - if(realtime != crypto_servercpu_lastrealtime) + if(host.realtime != crypto_servercpu_lastrealtime) crypto_servercpu_accumulator = crypto_servercpumaxtime.value; } - crypto_servercpu_lastrealtime = realtime; + crypto_servercpu_lastrealtime = host.realtime; if(do_reject && crypto_servercpu_accumulator < 0) { - if(realtime > complain_time + 5) + if(host.realtime > complain_time + 5) Con_Printf("crypto: cannot perform requested crypto operations; denial service attack or crypto_servercpupercent/crypto_servercpumaxtime are too low\n"); *len_out = 0; return CRYPTO_DISCARD; @@ -2044,7 +2102,7 @@ static int Crypto_ClientError(char *data_out, size_t *len_out, const char *msg) static int Crypto_SoftClientError(char *data_out, size_t *len_out, const char *msg) { *len_out = 0; - Con_Printf("%s\n", msg); + Con_DPrintf("%s\n", msg); return CRYPTO_DISCARD; } @@ -2068,7 +2126,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if (len_in == 6 && !memcmp(string, "accept", 6) && cls.connect_trying && d0_rijndael_dll) { int wantserverid = -1; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL); + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL, NULL); if(!crypto || !crypto->authenticated) // we ALSO get here if we are using an encrypted connection, so let's rule this out { if(wantserverid >= 0) @@ -2081,7 +2139,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, else if (len_in >= 1 && string[0] == 'j' && cls.connect_trying && d0_rijndael_dll) { int wantserverid = -1; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL); + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL, NULL); //if(!crypto || !crypto->authenticated) { if(wantserverid >= 0) @@ -2096,14 +2154,14 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, int wantserverid = -1; // these three are harmless - if(string[4] == CCREP_SERVER_INFO) + if((unsigned char) string[4] == CCREP_SERVER_INFO) return CRYPTO_NOMATCH; - if(string[4] == CCREP_PLAYER_INFO) + if((unsigned char) string[4] == CCREP_PLAYER_INFO) return CRYPTO_NOMATCH; - if(string[4] == CCREP_RULE_INFO) + if((unsigned char) string[4] == CCREP_RULE_INFO) return CRYPTO_NOMATCH; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL); + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL, NULL); //if(!crypto || !crypto->authenticated) { if(wantserverid >= 0) @@ -2149,14 +2207,19 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, const char *p; int i; int clientid = -1, serverid = -1, wantserverid = -1; - qboolean server_can_auth = true; + qbool server_can_auth = true; char wantserver_idfp[FP64_SIZE+1]; int wantserver_aeslevel = 0; + qbool wantserver_issigned = false; + + // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server. + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + return Crypto_SoftClientError(data_out, len_out, "challenge message from wrong server"); // if we have a stored host key for the server, assume serverid to already be selected! // (the loop will refuse to overwrite this one then) wantserver_idfp[0] = 0; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, wantserver_idfp, sizeof(wantserver_idfp), &wantserver_aeslevel); + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, wantserver_idfp, sizeof(wantserver_idfp), &wantserver_aeslevel, &wantserver_issigned); // requirement: wantserver_idfp is a full ID if wantserverid set // if we leave, we have to consider the connection @@ -2169,7 +2232,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, GetUntilNul(&data_in, &len_in); if(!data_in) return (wantserverid >= 0) ? Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though a host key is present") : - (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; // FTEQW extension protocol @@ -2205,7 +2268,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(!vlen_blind_id_ptr) return (wantserverid >= 0) ? Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though authentication is required") : - (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; data_in = vlen_blind_id_ptr; @@ -2262,6 +2325,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, crypto->server_keyfp[0] = 0; crypto->server_idfp[0] = 0; memcpy(CDATA->wantserver_idfp, wantserver_idfp, sizeof(crypto->server_idfp)); + CDATA->wantserver_issigned = wantserver_issigned; if(CDATA->wantserver_idfp[0]) // if we know a host key, honor its encryption setting switch(bound(0, d0_rijndael_dll ? crypto_aeslevel.integer : 0, 3)) @@ -2269,7 +2333,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, default: // dummy, never happens, but to make gcc happy... case 0: if(wantserver_aeslevel >= 3) - return Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL); + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)"); CDATA->wantserver_aes = false; break; case 1: @@ -2280,7 +2344,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, break; case 3: if(wantserver_aeslevel <= 0) - return Crypto_ServerError(data_out, len_out, "This server requires encryption to be supported (crypto_aeslevel >= 1, and d0_rijndael library must be present)", NULL); + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be supported (crypto_aeslevel >= 1, and d0_rijndael library must be present)"); CDATA->wantserver_aes = true; break; } @@ -2296,6 +2360,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, // I am the client, and my key is ok... so let's set client_keyfp and client_idfp strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp)); strlcpy(crypto->client_idfp, pubkeys_priv_fp64[CDATA->c], sizeof(crypto->client_idfp)); + crypto->client_issigned = pubkeys_havesig[CDATA->c]; } if(serverid >= 0) @@ -2315,7 +2380,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, CDATA->next_step = 1; *len_out = data_out_p - data_out; } - else if(clientid >= 0) + else // if(clientid >= 0) // guaranteed by condition one level outside { // skip over server auth, perform client auth only if(!CDATA->id) @@ -2339,9 +2404,6 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, data_out_p += *len_out; *len_out = data_out_p - data_out; } - else - *len_out = data_out_p - data_out; - return CRYPTO_DISCARD; } else @@ -2349,7 +2411,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(wantserver_idfp[0]) // if we know a host key, honor its encryption setting if(wantserver_aeslevel >= 3) return Crypto_ClientError(data_out, len_out, "Server insists on encryption, but neither can authenticate to the other"); - return (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + return (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; } } @@ -2357,6 +2419,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, { const char *cnt; int id; + + // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server. + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + return Crypto_SoftClientError(data_out, len_out, "d0pk\\ message from wrong server"); + cnt = InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue)); id = (cnt ? atoi(cnt) : -1); cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)); @@ -2370,11 +2437,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, { if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 1) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); - cls.connect_nextsendtime = max(cls.connect_nextsendtime, realtime + 1); // prevent "hammering" + cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering" if((s = InfoString_GetValue(string + 4, "aes", infostringvalue, sizeof(infostringvalue)))) aes = atoi(s); @@ -2419,21 +2486,26 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 3) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); - cls.connect_nextsendtime = max(cls.connect_nextsendtime, realtime + 1); // prevent "hammering" + cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering" if(!qd0_blind_id_authenticate_with_private_id_verify(CDATA->id, data_in, len_in, msgbuf, &msgbuflen, &status)) { CLEAR_CDATA; return Crypto_ClientError(data_out, len_out, "d0_blind_id_authenticate_with_private_id_verify failed (server authentication error)"); } - if(status) - strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp)); - else - crypto->server_keyfp[0] = 0; + + strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp)); + if (!status && CDATA->wantserver_issigned) + { + CLEAR_CDATA; + return Crypto_ClientError(data_out, len_out, "Stored host key requires a valid signature, but server did not provide any"); + } + crypto->server_issigned = status; + memset(crypto->server_idfp, 0, sizeof(crypto->server_idfp)); fpbuflen = FP64_SIZE; if(!qd0_blind_id_fingerprint64_public_id(CDATA->id, crypto->server_idfp, &fpbuflen)) @@ -2455,7 +2527,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, } // cache the server key - Crypto_StoreHostKey(&cls.connect_address, va(vabuf, sizeof(vabuf), "%d %s@%s", crypto->use_aes ? 1 : 0, crypto->server_idfp, pubkeys_fp64[CDATA->s]), false); + Crypto_StoreHostKey(&cls.connect_address, va(vabuf, sizeof(vabuf), "%d %s@%s%s", crypto->use_aes ? 1 : 0, crypto->server_idfp, crypto->server_issigned ? "" : "~", pubkeys_fp64[CDATA->s]), false); if(CDATA->c >= 0) { @@ -2496,11 +2568,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 5) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); - cls.connect_nextsendtime = max(cls.connect_nextsendtime, realtime + 1); // prevent "hammering" + cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering" if(CDATA->s < 0) // only if server didn't auth {