Added security measures against packet replay (mostly only useful in enncrypted mode)

Added checks to prevent sub-message dropping
This commit is contained in:
Gabriel Tofvesson 2017-09-29 13:18:09 +02:00
parent c46cc97913
commit e14794529e
2 changed files with 62 additions and 22 deletions

View File

@ -1,4 +1,5 @@
#include "Net.h"
#include "Support.h"
#include <stdlib.h>
#include <stdio.h>
@ -7,21 +8,6 @@
#include <ws2tcpip.h>
namespace IO {
bool connected(SOCKET sock)
{
char buf;
int err = recv(sock, &buf, 1, MSG_PEEK);
if (err == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
return false;
}
}
return true;
}
bool cryptoLevelsAreCompatible(CryptoLevel l1, CryptoLevel l2) {
return !(((l1 == CryptoLevel::None) && (l2 == CryptoLevel::Force)) || ((l2 == CryptoLevel::None) && (l1 == CryptoLevel::Force)));
}
@ -34,8 +20,20 @@ namespace IO {
return c;
}
void flushPrecedingPings(std::vector<char>* sparse) {
while (sparse->size() >= sizeof(ulong_64b)) {
ulong_64b size = 0;
char* c = readSparse(sparse, sizeof(ulong_64b), false);
memcpy(&size, c, sizeof(ulong_64b));
delete[] c;
if (size != FLAG_PING) return;
else delete[] readSparse(sparse, sizeof(ulong_64b)); // If this block is a ping packet, remove it
}
}
bool hasFullMessage(std::vector<char> *sparse) {
if (sparse->size() < sizeof(ulong_64b)) return false;
flushPrecedingPings(sparse);
ulong_64b size = 0;
char* c = readSparse(sparse, sizeof(ulong_64b), false);
memcpy(&size, c, sizeof(ulong_64b));
@ -54,6 +52,7 @@ namespace IO {
canWrite = true;
evt = nullptr;
char cryptoPref = static_cast<char>(preferEncrypted);
commTime = time(nullptr);
if (send(_socket, &cryptoPref, 1, 0) == SOCKET_ERROR) throw new _exception(); // Cannot establish connection :(
if (!noThread) listener = std::thread([](NetClient& cli) { while (cli._open) { cli.update(); Sleep(25); } }, std::ref(*this)); // Setup separate thread for reading new data
}
@ -130,7 +129,22 @@ namespace IO {
shutdown(_socket, SD_SEND);
canWrite = false;
}
bool NetClient::ping() {
int i;
char* c = new char[sizeof(ulong_64b)];
ulong_64b pingValue = FLAG_PING;
memcpy(c, (const char*)&pingValue, sizeof(ulong_64b));
for (ulong_64b wIdx = 0; wIdx < sizeof(ulong_64b); ++wIdx) {
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) return false;
else if (i == 0) --wIdx;
}
commTime = time(nullptr);
}
size_t NetClient::getBOPCount() { return firstMessage ? outPacketBuf->size() : 0; }
bool NetClient::_write(char* message, ulong_64b size) {
if (size==FLAG_PING) throw new _exception(); // Max value is reserved for ping packet
int i;
char* c = new char[sizeof(ulong_64b)];
memcpy(c, &size, sizeof(ulong_64b));
@ -142,6 +156,7 @@ namespace IO {
if ((i = send(_socket, message + wIdx, 1, 0)) == SOCKET_ERROR) return false;
else if (i == 0) --wIdx;
}
commTime = time(nullptr);
return true;
}
bool NetClient::write(void* message, ulong_64b size) {
@ -153,9 +168,15 @@ namespace IO {
return true;
}
if (!canWrite) return false;
char* msg = encrypted ? Crypto::full_auto_encrypt(message, size, pK, &size) : (char*)message;
_write(msg, size + encrypted);
char* bMsg = new char[size+1];
bMsg[0] = remotePUID;
++remotePUID;
memcpy(bMsg + 1, message, size);
++size;
char* msg = encrypted ? Crypto::full_auto_encrypt(bMsg, size, pK, &size) : (char*)bMsg;
_write(msg, size);
if (encrypted) delete[] msg;
delete[] bMsg;
return true;
}
bool NetClient::write(char* message) { return write(message, strlen(message)+1); } // Send together with the null-terminator
@ -185,11 +206,6 @@ namespace IO {
}
bool NetClient::isEncrypted() { return encrypted; }
void NetClient::update() {
/*if (!connected(_socket)) { // Check this later...
_open = false;
close();
return;
}*/
int iResult = 0, rdErr;
unsigned long rCount;
rdErr = ioctlsocket(_socket, FIONREAD, &rCount);
@ -200,14 +216,30 @@ namespace IO {
for (int i = 0; i < iResult; ++i)
if (sparse->size() < BUF_2_MAX)
sparse->push_back(rBuf[i]); // Drop anything over the absolute max
commTime = time(nullptr);
if(!firstMessage) flushPrecedingPings(sparse);
}
while (!firstMessage && hasFullMessage(sparse)) {
Packet p;
char* size = readSparse(sparse, sizeof(ulong_64b));
memcpy(&p.size, size, sizeof(ulong_64b));
delete[] size;
p.message = readSparse(sparse, p.size);
if (encrypted) p.message = Crypto::full_auto_decrypt(p.message, keys.privKey, &p.size);
p.packetUID = p.message[0];
if (p.packetUID != expectedNextPUID) continue; // Detect packet replay/mismatch
else ++expectedNextPUID;
--p.size;
char* c = new char[p.size];
memcpy(c, p.message + 1, p.size);
delete[] p.message;
p.message = c;
if (evt == nullptr) packets->push_back(p);
else evt(this, p); // Notify event handler of a new packet
}
@ -258,6 +290,7 @@ namespace IO {
_open = false;
close();
}
if ((time(nullptr) - commTime) > 1) if (!ping()) { _open = false; close(); }
}
bool NetClient::isOpen() { return _open; }

View File

@ -14,6 +14,9 @@
#define WIN32_LEAN_AND_MEAN
// Ping flag tells the recieving host to drop the current ulong_64b, as it is sent to check if the connection is still alive
#define FLAG_PING (ulong_64b)-1
#include "Crypto.h"
#include "ArchAbstract.h"
@ -29,6 +32,7 @@ namespace IO {
struct Packet {
ulong_64b size;
char packetUID;
char* message;
};
@ -48,6 +52,8 @@ namespace IO {
bool fm_neg_hasLevel = false;
bool fm_neg_hasSize = false;
bool startNegotiate = false;
char expectedNextPUID = 0;
char remotePUID = 0;
std::vector<char>* sparse;
std::vector<Packet>* outPacketBuf;
Crypto::RSA::KeyData keys; // Client's keysets (if using encryption)
@ -59,6 +65,7 @@ namespace IO {
bool _write(char*, ulong_64b); // Internal write function. Doesn't do any of the fancy auto encryption: just raw write...
bool writeBufferedPackets(); // Flushes and deletes buffer
void update(); // Read incoming data and store in buffers
bool ping(); // Check if connection is alive by pinging remote host
protected:
std::thread listener; // Incoming data listener (optional)
SOCKET _socket; // Underlying socket used for communication