Core: feature add 2FA (OTP) (#1054)
Add 2 factor authentification for improved security on your websites/apps. Taken from TrinityCore Closes #1049 on github.
This commit is contained in:
@@ -27,7 +27,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
|||||||
PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH);
|
PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH);
|
||||||
PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC);
|
PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
|
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
|
||||||
PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
|
PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
|
||||||
PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH);
|
PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH);
|
||||||
PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC);
|
PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH);
|
PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "RealmList.h"
|
#include "RealmList.h"
|
||||||
#include "AuthSocket.h"
|
#include "AuthSocket.h"
|
||||||
#include "AuthCodes.h"
|
#include "AuthCodes.h"
|
||||||
|
#include "TOTP.h"
|
||||||
#include "SHA1.h"
|
#include "SHA1.h"
|
||||||
#include "openssl/crypto.h"
|
#include "openssl/crypto.h"
|
||||||
|
|
||||||
@@ -532,6 +533,12 @@ bool AuthSocket::_HandleLogonChallenge()
|
|||||||
pkt.append(s.AsByteArray().get(), s.GetNumBytes()); // 32 bytes
|
pkt.append(s.AsByteArray().get(), s.GetNumBytes()); // 32 bytes
|
||||||
pkt.append(unk3.AsByteArray(16).get(), 16);
|
pkt.append(unk3.AsByteArray(16).get(), 16);
|
||||||
uint8 securityFlags = 0;
|
uint8 securityFlags = 0;
|
||||||
|
|
||||||
|
// Check if token is used
|
||||||
|
_tokenKey = fields[8].GetString();
|
||||||
|
if (!_tokenKey.empty())
|
||||||
|
securityFlags = 4;
|
||||||
|
|
||||||
pkt << uint8(securityFlags); // security flags (0x0...0x04)
|
pkt << uint8(securityFlags); // security flags (0x0...0x04)
|
||||||
|
|
||||||
if (securityFlags & 0x01) // PIN input
|
if (securityFlags & 0x01) // PIN input
|
||||||
@@ -704,6 +711,25 @@ bool AuthSocket::_HandleLogonProof()
|
|||||||
sha.UpdateBigNumbers(&A, &M, &K, NULL);
|
sha.UpdateBigNumbers(&A, &M, &K, NULL);
|
||||||
sha.Finalize();
|
sha.Finalize();
|
||||||
|
|
||||||
|
// Check auth token
|
||||||
|
if ((lp.securityFlags & 0x04) || !_tokenKey.empty())
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
|
if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
|
||||||
{
|
{
|
||||||
sAuthLogonProof_S proof;
|
sAuthLogonProof_S proof;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ private:
|
|||||||
eStatus _status;
|
eStatus _status;
|
||||||
|
|
||||||
std::string _login;
|
std::string _login;
|
||||||
|
std::string _tokenKey;
|
||||||
|
|
||||||
// Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ
|
// 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
|
// between enUS and enGB, which is important for the patch system
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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(NULL)/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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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