X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=crypto.c;h=a6ff74526798807c723a8013730add3d1ebcc576;hb=d79de33fe1787fbf73bee091ef5f3499d7f06396;hp=6abaaa220719ed20c96728530953c696bed78b67;hpb=6b1ade501453a2def2320663016d556b832e7391;p=xonotic%2Fdarkplaces.git diff --git a/crypto.c b/crypto.c index 6abaaa22..a6ff7452 100644 --- a/crypto.c +++ b/crypto.c @@ -6,17 +6,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 = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "crypto_developer", "0", "print extra info about crypto handshake"}; +cvar_t crypto_aeslevel = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; + +cvar_t crypto_servercpupercent = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "crypto_servercpupercent", "10", "allowed crypto CPU load in percent for server operation (0 = no limit, faster)"}; +cvar_t crypto_servercpumaxtime = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "crypto_servercpumaxtime", "0.01", "maximum allowed crypto CPU time per frame (0 = no limit)"}; +cvar_t crypto_servercpudebug = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "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)) @@ -513,7 +518,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) @@ -523,7 +528,7 @@ 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; @@ -1188,7 +1193,7 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch SV_UnlockThreadMutex(); } -static void Crypto_KeyGen_f(void) +static void Crypto_KeyGen_f(cmd_state_t *cmd) { int i; const char *p[1]; @@ -1206,14 +1211,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); @@ -1313,8 +1318,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"); @@ -1344,14 +1349,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) @@ -1374,7 +1379,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; @@ -1399,7 +1404,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; @@ -1410,12 +1415,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)); } } } @@ -1424,11 +1429,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(CMD_SHARED, "crypto_reload", Crypto_Reload_f, "reloads cryptographic keys"); + Cmd_AddCommand(CMD_SHARED, "crypto_keygen", Crypto_KeyGen_f, "generates and saves a cryptographic key"); + Cmd_AddCommand(CMD_SHARED, "crypto_keys", Crypto_Keys_f, "lists the loaded keys"); + Cmd_AddCommand(CMD_SHARED, "crypto_hostkeys", Crypto_HostKeys_f, "lists the cached host keys"); + Cmd_AddCommand(CMD_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); @@ -1722,12 +1728,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) @@ -1742,23 +1748,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 @@ -2031,7 +2037,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; @@ -2039,13 +2045,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; @@ -2075,7 +2081,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; } @@ -2185,6 +2191,10 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, int wantserver_aeslevel = 0; qboolean 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; @@ -2201,7 +2211,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 @@ -2237,7 +2247,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; @@ -2302,7 +2312,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: @@ -2313,7 +2323,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; } @@ -2373,7 +2383,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; } - return CRYPTO_DISCARD; } else @@ -2381,7 +2390,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; } } @@ -2389,6 +2398,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)); @@ -2402,11 +2416,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); @@ -2451,11 +2465,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 != 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)) { @@ -2492,7 +2506,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) { @@ -2533,11 +2547,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 {