mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-17 02:50:29 +00:00
feat(Core/Authserver): TOTP rewrite (#5620)
This commit is contained in:
@@ -4,21 +4,23 @@
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <openssl/md5.h>
|
||||
|
||||
#include "AES.h"
|
||||
#include "Common.h"
|
||||
#include "CryptoGenerics.h"
|
||||
#include "CryptoRandom.h"
|
||||
#include "CryptoHash.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "ByteBuffer.h"
|
||||
#include "Configuration/Config.h"
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include "RealmList.h"
|
||||
#include "AuthSocket.h"
|
||||
#include "AuthCodes.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "TOTP.h"
|
||||
#include "openssl/crypto.h"
|
||||
#include <algorithm>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/md5.h>
|
||||
|
||||
#define ChunkSize 2048
|
||||
|
||||
@@ -371,6 +373,7 @@ bool AuthSocket::_HandleLogonChallenge()
|
||||
std::string const& ip_address = socket().getRemoteAddress();
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED);
|
||||
stmt->setString(0, ip_address);
|
||||
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
if (result)
|
||||
{
|
||||
@@ -438,6 +441,26 @@ bool AuthSocket::_HandleLogonChallenge()
|
||||
}
|
||||
}
|
||||
|
||||
uint8 securityFlags = 0;
|
||||
_totpSecret = fields[7].GetBinary();
|
||||
|
||||
// Check if a TOTP token is needed
|
||||
if (!_totpSecret || !_totpSecret.value().empty())
|
||||
{
|
||||
securityFlags = 4;
|
||||
|
||||
if (auto const& secret = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY))
|
||||
{
|
||||
bool success = acore::Crypto::AEDecrypt<acore::Crypto::AES>(*_totpSecret, *secret);
|
||||
if (!success)
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_DB_BUSY);
|
||||
LOG_ERROR("server.authserver", "[AuthChallenge] Account '%s' has invalid ciphertext for TOTP token key stored", _login.c_str());
|
||||
locked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
{
|
||||
//set expired bans to inactive
|
||||
@@ -446,18 +469,19 @@ bool AuthSocket::_HandleLogonChallenge()
|
||||
// If the account is banned, reject the logon attempt
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED);
|
||||
stmt->setUInt32(0, fields[0].GetUInt32());
|
||||
|
||||
PreparedQueryResult banresult = LoginDatabase.Query(stmt);
|
||||
if (banresult)
|
||||
{
|
||||
if ((*banresult)[0].GetUInt32() == (*banresult)[1].GetUInt32())
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_BANNED);
|
||||
LOG_DEBUG("network", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ());
|
||||
LOG_DEBUG("network", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt << uint8(WOW_FAIL_SUSPENDED);
|
||||
LOG_DEBUG("network", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ());
|
||||
LOG_DEBUG("network", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -481,12 +505,6 @@ bool AuthSocket::_HandleLogonChallenge()
|
||||
pkt.append(_srp6->N);
|
||||
pkt.append(_srp6->s);
|
||||
pkt.append(unk3.ToByteArray<16>());
|
||||
uint8 securityFlags = 0;
|
||||
|
||||
// Check if token is used
|
||||
_tokenKey = fields[7].GetString();
|
||||
if (!_tokenKey.empty())
|
||||
securityFlags = 4;
|
||||
|
||||
pkt << uint8(securityFlags); // security flags (0x0...0x04)
|
||||
|
||||
@@ -515,9 +533,9 @@ bool AuthSocket::_HandleLogonChallenge()
|
||||
for (int i = 0; i < 4; ++i)
|
||||
_localizationName[i] = ch->country[4 - i - 1];
|
||||
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
LOG_DEBUG("network", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str (), ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName) );
|
||||
#endif
|
||||
LOG_DEBUG("network", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)",
|
||||
socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str(), ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName));
|
||||
|
||||
///- All good, await client's proof
|
||||
_status = STATUS_LOGON_PROOF;
|
||||
}
|
||||
@@ -577,23 +595,24 @@ bool AuthSocket::_HandleLogonProof()
|
||||
acore::Crypto::SHA1::Digest M2 = acore::Crypto::SRP6::GetSessionVerifier(lp.A, lp.clientM, _sessionKey);
|
||||
|
||||
// Check auth token
|
||||
if ((lp.securityFlags & 0x04) || !_tokenKey.empty())
|
||||
bool tokenSuccess = false;
|
||||
bool sentToken = (lp.securityFlags & 0x04);
|
||||
|
||||
if (sentToken && _totpSecret)
|
||||
{
|
||||
uint8 size;
|
||||
socket().recv((char*)&size, 1);
|
||||
char* token = new char[size + 1];
|
||||
token[size] = '\0';
|
||||
socket().recv(token, size);
|
||||
unsigned int validToken = TOTP::GenerateToken(_tokenKey.c_str());
|
||||
unsigned int incomingToken = atoi(token);
|
||||
delete[] token;
|
||||
if (validToken != incomingToken)
|
||||
{
|
||||
char data[] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 };
|
||||
socket().send(data, sizeof(data));
|
||||
return false;
|
||||
}
|
||||
|
||||
tokenSuccess = acore::Crypto::TOTP::ValidateToken(*_totpSecret, incomingToken);
|
||||
memset(_totpSecret->data(), 0, _totpSecret->size());
|
||||
}
|
||||
else if (!sentToken && !_totpSecret)
|
||||
tokenSuccess = true;
|
||||
|
||||
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
|
||||
{
|
||||
@@ -616,6 +635,12 @@ bool AuthSocket::_HandleLogonProof()
|
||||
socket().send((char*)&proof, sizeof(proof));
|
||||
}
|
||||
|
||||
if (!tokenSuccess)
|
||||
{
|
||||
char data[4] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 };
|
||||
socket().send(data, sizeof(data));
|
||||
}
|
||||
|
||||
///- Set _status to authed!
|
||||
_status = STATUS_AUTHED;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "CryptoHash.h"
|
||||
#include "Optional.h"
|
||||
#include "RealmSocket.h"
|
||||
#include "SRP6.h"
|
||||
|
||||
@@ -65,7 +66,7 @@ private:
|
||||
eStatus _status;
|
||||
|
||||
std::string _login;
|
||||
std::string _tokenKey;
|
||||
Optional<std::vector<uint8>> _totpSecret;
|
||||
|
||||
// Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ
|
||||
// between enUS and enGB, which is important for the patch system
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "TOTP.h"
|
||||
#include <cstring>
|
||||
|
||||
int base32_decode(const char* encoded, char* result, int bufSize)
|
||||
{
|
||||
// Base32 implementation
|
||||
// Copyright 2010 Google Inc.
|
||||
// Author: Markus Gutschke
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
int buffer = 0;
|
||||
int bitsLeft = 0;
|
||||
int count = 0;
|
||||
for (const char* ptr = encoded; count < bufSize && *ptr; ++ptr)
|
||||
{
|
||||
char ch = *ptr;
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-')
|
||||
continue;
|
||||
buffer <<= 5;
|
||||
// Deal with commonly mistyped characters
|
||||
if (ch == '0')
|
||||
ch = 'O';
|
||||
else if (ch == '1')
|
||||
ch = 'L';
|
||||
else if (ch == '8')
|
||||
ch = 'B';
|
||||
// Look up one base32 digit
|
||||
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
|
||||
ch = (ch & 0x1F) - 1;
|
||||
else if (ch >= '2' && ch <= '7')
|
||||
ch -= '2' - 26;
|
||||
else
|
||||
return -1;
|
||||
buffer |= ch;
|
||||
bitsLeft += 5;
|
||||
if (bitsLeft >= 8)
|
||||
{
|
||||
result[count++] = buffer >> (bitsLeft - 8);
|
||||
bitsLeft -= 8;
|
||||
}
|
||||
}
|
||||
if (count < bufSize)
|
||||
result[count] = '\000';
|
||||
return count;
|
||||
}
|
||||
|
||||
#define HMAC_RES_SIZE 20
|
||||
|
||||
namespace TOTP
|
||||
{
|
||||
unsigned int GenerateToken(const char* b32key)
|
||||
{
|
||||
size_t keySize = strlen(b32key);
|
||||
int bufsize = (keySize + 7) / 8 * 5;
|
||||
char* encoded = new char[bufsize];
|
||||
memset(encoded, 0, bufsize);
|
||||
unsigned int hmacResSize = HMAC_RES_SIZE;
|
||||
unsigned char hmacRes[HMAC_RES_SIZE];
|
||||
unsigned long timestamp = time(nullptr) / 30;
|
||||
unsigned char challenge[8];
|
||||
for (int i = 8; i--; timestamp >>= 8)
|
||||
challenge[i] = timestamp;
|
||||
base32_decode(b32key, encoded, bufsize);
|
||||
HMAC(EVP_sha1(), encoded, bufsize, challenge, 8, hmacRes, &hmacResSize);
|
||||
unsigned int offset = hmacRes[19] & 0xF;
|
||||
unsigned int truncHash = (hmacRes[offset] << 24) | (hmacRes[offset + 1] << 16 ) | (hmacRes[offset + 2] << 8) | (hmacRes[offset + 3]);
|
||||
truncHash &= 0x7FFFFFFF;
|
||||
delete[] encoded;
|
||||
return truncHash % 1000000;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _TOTP_H
|
||||
#define _TOTP_H
|
||||
#include "openssl/hmac.h"
|
||||
#include "openssl/evp.h"
|
||||
namespace TOTP
|
||||
{
|
||||
unsigned int GenerateToken(const char* b32key);
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user