1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-07-12 02:34:59 +03:00

OpenVPN: Add packet scrambling/obfuscation feature

This allows an OpenVPN client to bypass a firewall which is aware of the protocol and is able to block it.
The XOR mask set on the server has to be the same on the client, otherwise it will not be able to connect with certain obfuscation modes.
A special OpenVPN client built with the "XOR patch" is required in order to use this function, because it has never been merged in the official OpenVPN repository.

Two parameters are added to the server configuration: "OpenVPNObfuscationMethod" and "OpenVPNObfuscationMask".
Their value can be retrieved with "OpenVpnObfuscationGet" and set with "OpenVpnObfuscationEnable" in the VPN Command Line Management Utility.
This commit is contained in:
Davide Beatrici
2018-11-12 22:32:37 +01:00
parent 25c99a7e04
commit 6a45921f41
14 changed files with 566 additions and 123 deletions

View File

@ -306,9 +306,150 @@ UINT OvsDecrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *dest, UCHAR *src, UINT
return 0;
}
// XOR the bytes with the specified string
void OvsDataXorMask(void *data, const UINT data_size, const char *mask, const UINT mask_size)
{
UINT i;
UCHAR *buf;
// Validate arguments
if (data == NULL || data_size == 0 || mask == NULL || mask_size == 0)
{
return;
}
for (i = 0, buf = data; i < data_size; i++, buf++)
{
*buf = *buf ^ mask[i % mask_size];
}
}
// XOR each byte with its position within the buffer
void OvsDataXorPtrPos(void *data, const UINT size)
{
UINT i;
UCHAR *buf;
// Validate arguments
if (data == NULL || size == 0)
{
return;
}
for (i = 0, buf = data; i < size; i++, buf++)
{
*buf = *buf ^ i + 1;
}
}
// Reverse bytes order if they're more than 2, keeping the first byte unchanged
void OvsDataReverse(void *data, const UINT size)
{
UINT i;
UCHAR tmp;
UCHAR *buf_start, *buf_end;
// Validate arguments
if (data == NULL || size < 3)
{
return;
}
for (i = 0, buf_start = (UCHAR *)data + 1, buf_end = (UCHAR *)data + (size - 1); i < (size - 1 ) / 2; i++, buf_start++, buf_end--)
{
tmp = *buf_start;
*buf_start = *buf_end;
*buf_end = tmp;
}
}
// Detects the method used to obfuscate the packet
UINT OvsDetectObfuscation(void *data, UINT size, char *xormask)
{
UINT ret;
void *tmp;
OPENVPN_PACKET *parsed_packet;
// Validate arguments
if (data == NULL || size == 0)
{
return INFINITE;
}
ret = INFINITE;
tmp = NULL;
// OPENVPN_SCRAMBLE_MODE_DISABLED
parsed_packet = OvsParsePacket(data, size);
if (parsed_packet != NULL)
{
ret = OPENVPN_SCRAMBLE_MODE_DISABLED;
goto final;
}
// OPENVPN_SCRAMBLE_MODE_XORMASK
tmp = Clone(data, size);
OvsDataXorMask(tmp, size, xormask, StrLen(xormask));
parsed_packet = OvsParsePacket(tmp, size);
if (parsed_packet != NULL)
{
ret = OPENVPN_SCRAMBLE_MODE_XORMASK;
goto final;
}
Free(tmp);
// OPENVPN_SCRAMBLE_MODE_XORPTRPOS
tmp = Clone(data, size);
OvsDataXorPtrPos(tmp, size);
parsed_packet = OvsParsePacket(tmp, size);
if (parsed_packet != NULL)
{
ret = OPENVPN_SCRAMBLE_MODE_XORPTRPOS;
goto final;
}
Free(tmp);
// OPENVPN_SCRAMBLE_MODE_REVERSE
tmp = Clone(data, size);
OvsDataReverse(tmp, size);
parsed_packet = OvsParsePacket(tmp, size);
if (parsed_packet != NULL)
{
ret = OPENVPN_SCRAMBLE_MODE_REVERSE;
goto final;
}
Free(tmp);
// OPENVPN_SCRAMBLE_MODE_OBFUSCATE
tmp = Clone(data, size);
OvsDataXorMask(tmp, size, xormask, StrLen(xormask));
OvsDataXorPtrPos(tmp, size);
OvsDataReverse(tmp, size);
OvsDataXorPtrPos(tmp, size);
parsed_packet = OvsParsePacket(tmp, size);
if (parsed_packet != NULL)
{
ret = OPENVPN_SCRAMBLE_MODE_OBFUSCATE;
goto final;
}
final:
OvsFreePacket(parsed_packet);
Free(tmp);
return ret;
}
// Process the received packet
void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
{
OPENVPN_CHANNEL *c;
OPENVPN_SESSION *se;
OPENVPN_PACKET *recv_packet;
// Validate arguments
@ -317,7 +458,6 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
return;
}
// Search for the session
se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, protocol);
if (se == NULL)
@ -325,142 +465,179 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
return;
}
// Parse the packet
recv_packet = OvsParsePacket(p->Data, p->Size);
if (recv_packet != NULL)
// Detect obfuscation mode and save it for the next packets in the same session
if (se->ObfuscationMode == INFINITE)
{
OPENVPN_CHANNEL *c = NULL;
if (recv_packet->OpCode != OPENVPN_P_DATA_V1 && recv_packet->MySessionId != 0)
se->ObfuscationMode = OvsDetectObfuscation(p->Data, p->Size, s->Cedar->OpenVPNObfuscationMask);
if (se->ObfuscationMode != INFINITE)
{
Debug("RECV PACKET: %u %I64u\n", recv_packet->KeyId, recv_packet->MySessionId);
}
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
{
Debug(" PKT %u %u\n", recv_packet->OpCode, recv_packet->KeyId);
}
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
{
// Control packet
if (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2 ||
recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
{
// Connection request packet
if (se->Channels[recv_packet->KeyId] != NULL)
{
// Release when there is a channel data already
OvsFreeChannel(se->Channels[recv_packet->KeyId]);
se->Channels[recv_packet->KeyId] = NULL;
}
// Create a new channel
c = OvsNewChannel(se, recv_packet->KeyId);
if (se->ClientSessionId == 0)
{
se->ClientSessionId = recv_packet->MySessionId;
}
se->Channels[recv_packet->KeyId] = c;
Debug("OpenVPN New Channel :%u\n", recv_packet->KeyId);
OvsLog(s, se, c, "LO_NEW_CHANNEL");
}
/* else if (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
{
// Response to soft reset request packet
OPENVPN_PACKET *p;
p = OvsNewControlPacket(OPENVPN_P_CONTROL_SOFT_RESET_V1, recv_packet->KeyId, se->ServerSessionId,
0, NULL, 0, 0, 0, NULL);
OvsSendPacketNow(s, se, p);
OvsFreePacket(p);
}*/
else
{
// Packet other than the connection request
if (se->Channels[recv_packet->KeyId] != NULL)
{
c = se->Channels[recv_packet->KeyId];
}
}
if (c != NULL)
{
// Delete the send packet list by looking the packet ID in the ACK list of arrived packet
OvsDeleteFromSendingControlPacketList(c, recv_packet->NumAck, recv_packet->AckPacketId);
if (recv_packet->OpCode != OPENVPN_P_ACK_V1)
{
// Add the Packet ID of arrived packet to the list
InsertIntDistinct(c->AckReplyList, recv_packet->PacketId);
Debug("Recv Packet ID (c=%u): %u\n", c->KeyId, recv_packet->PacketId);
if ((recv_packet->PacketId > c->MaxRecvPacketId)
|| (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2)
|| (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1))
{
c->MaxRecvPacketId = recv_packet->PacketId;
// Process the received control packet
OvsProcessRecvControlPacket(s, se, c, recv_packet);
}
}
}
Debug("OvsProceccRecvPacket(): detected packet obfuscation/scrambling mode: %u\n", se->ObfuscationMode);
}
else
{
// Data packet
Debug("OvsProceccRecvPacket(): failed to detect packet obfuscation/scrambling mode!\n");
return;
}
}
// Handle scrambled packet
switch (se->ObfuscationMode)
{
case OPENVPN_SCRAMBLE_MODE_DISABLED:
break;
case OPENVPN_SCRAMBLE_MODE_XORMASK:
OvsDataXorMask(p->Data, p->Size, s->Cedar->OpenVPNObfuscationMask, StrLen(s->Cedar->OpenVPNObfuscationMask));
break;
case OPENVPN_SCRAMBLE_MODE_XORPTRPOS:
OvsDataXorPtrPos(p->Data, p->Size);
break;
case OPENVPN_SCRAMBLE_MODE_REVERSE:
OvsDataReverse(p->Data, p->Size);
break;
case OPENVPN_SCRAMBLE_MODE_OBFUSCATE:
OvsDataXorMask(p->Data, p->Size, s->Cedar->OpenVPNObfuscationMask, StrLen(s->Cedar->OpenVPNObfuscationMask));
OvsDataXorPtrPos(p->Data, p->Size);
OvsDataReverse(p->Data, p->Size);
OvsDataXorPtrPos(p->Data, p->Size);
}
// Parse the packet
recv_packet = OvsParsePacket(p->Data, p->Size);
if (recv_packet == NULL)
{
Debug("OvsProceccRecvPacket(): OvsParsePacket() returned NULL!\n");
return;
}
if (recv_packet->OpCode != OPENVPN_P_DATA_V1 && recv_packet->MySessionId != 0)
{
Debug("RECV PACKET: %u %I64u\n", recv_packet->KeyId, recv_packet->MySessionId);
}
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
{
Debug(" PKT %u %u\n", recv_packet->OpCode, recv_packet->KeyId);
}
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
{
// Control packet
if (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2 ||
recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
{
// Connection request packet
if (se->Channels[recv_packet->KeyId] != NULL)
{
OPENVPN_CHANNEL *c = se->Channels[recv_packet->KeyId];
if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
// Release when there is a channel data already
OvsFreeChannel(se->Channels[recv_packet->KeyId]);
se->Channels[recv_packet->KeyId] = NULL;
}
// Create a new channel
c = OvsNewChannel(se, recv_packet->KeyId);
if (se->ClientSessionId == 0)
{
se->ClientSessionId = recv_packet->MySessionId;
}
se->Channels[recv_packet->KeyId] = c;
Debug("OpenVPN New Channel :%u\n", recv_packet->KeyId);
OvsLog(s, se, c, "LO_NEW_CHANNEL");
}
/* else if (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
{
// Response to soft reset request packet
OPENVPN_PACKET *p;
p = OvsNewControlPacket(OPENVPN_P_CONTROL_SOFT_RESET_V1, recv_packet->KeyId, se->ServerSessionId,
0, NULL, 0, 0, 0, NULL);
OvsSendPacketNow(s, se, p);
OvsFreePacket(p);
}*/
else
{
// Packet other than the connection request
if (se->Channels[recv_packet->KeyId] != NULL)
{
c = se->Channels[recv_packet->KeyId];
}
}
if (c != NULL)
{
// Delete the send packet list by looking the packet ID in the ACK list of arrived packet
OvsDeleteFromSendingControlPacketList(c, recv_packet->NumAck, recv_packet->AckPacketId);
if (recv_packet->OpCode != OPENVPN_P_ACK_V1)
{
// Add the Packet ID of arrived packet to the list
InsertIntDistinct(c->AckReplyList, recv_packet->PacketId);
Debug("Recv Packet ID (c=%u): %u\n", c->KeyId, recv_packet->PacketId);
if ((recv_packet->PacketId > c->MaxRecvPacketId)
|| (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2)
|| (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1))
{
UINT size;
UCHAR *data = s->TmpBuf;
if (c->CipherDecrypt->IsAeadCipher)
c->MaxRecvPacketId = recv_packet->PacketId;
// Process the received control packet
OvsProcessRecvControlPacket(s, se, c, recv_packet);
}
}
}
}
else
{
// Data packet
if (se->Channels[recv_packet->KeyId] != NULL)
{
OPENVPN_CHANNEL *c = se->Channels[recv_packet->KeyId];
if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
{
UINT size;
UCHAR *data = s->TmpBuf;
if (c->CipherDecrypt->IsAeadCipher)
{
// Update variable part (packet ID) of IV
Copy(c->IvRecv, recv_packet->Data, sizeof(recv_packet->PacketId));
// Decrypt
size = OvsDecrypt(c->CipherDecrypt, NULL, c->IvRecv, data, recv_packet->Data + sizeof(UINT), recv_packet->DataSize - sizeof(UINT));
}
else
{
// Decrypt
size = OvsDecrypt(c->CipherDecrypt, c->MdRecv, c->IvRecv, data, recv_packet->Data, recv_packet->DataSize);
// Seek buffer after the packet ID
data += sizeof(UINT);
size -= sizeof(UINT);
}
// Update of last communication time
se->LastCommTick = s->Now;
if (size < sizeof(ping_signature) || Cmp(data, ping_signature, sizeof(ping_signature)) != 0)
{
// Receive a packet!
if (se->Ipc != NULL)
{
// Update variable part (packet ID) of IV
Copy(c->IvRecv, recv_packet->Data, sizeof(recv_packet->PacketId));
// Decrypt
size = OvsDecrypt(c->CipherDecrypt, NULL, c->IvRecv, data, recv_packet->Data + sizeof(UINT), recv_packet->DataSize - sizeof(UINT));
}
else
{
// Decrypt
size = OvsDecrypt(c->CipherDecrypt, c->MdRecv, c->IvRecv, data, recv_packet->Data, recv_packet->DataSize);
// Seek buffer after the packet ID
data += sizeof(UINT);
size -= sizeof(UINT);
}
// Update of last communication time
se->LastCommTick = s->Now;
if (size < sizeof(ping_signature) || Cmp(data, ping_signature, sizeof(ping_signature)) != 0)
{
// Receive a packet!
if (se->Ipc != NULL)
switch (se->Mode)
{
switch (se->Mode)
{
case OPENVPN_MODE_L2: // Send an Ethernet packet to a session
IPCSendL2(se->Ipc, data, size);
break;
case OPENVPN_MODE_L3: // Send an IPv4 packet to a session
IPCSendIPv4(se->Ipc, data, size);
break;
}
case OPENVPN_MODE_L2: // Send an Ethernet packet to a session
IPCSendL2(se->Ipc, data, size);
break;
case OPENVPN_MODE_L3: // Send an IPv4 packet to a session
IPCSendIPv4(se->Ipc, data, size);
break;
}
}
}
}
}
OvsFreePacket(recv_packet);
}
OvsFreePacket(recv_packet);
}
// Remove a packet which the opponent has received from the transmission list
@ -1725,7 +1902,6 @@ OPENVPN_PACKET *OvsParsePacket(UCHAR *data, UINT size)
return ret;
LABEL_ERROR:
Debug("OvsParsePacket Error.\n");
OvsFreePacket(ret);
return NULL;
}
@ -1830,6 +2006,8 @@ OPENVPN_SESSION *OvsNewSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_por
Copy(&se->ServerIp, server_ip, sizeof(IP));
se->ServerPort = server_port;
se->ObfuscationMode = s->Cedar->OpenVPNObfuscation ? INFINITE : OPENVPN_SCRAMBLE_MODE_DISABLED;
se->LastCommTick = s->Now;
se->Protocol = protocol;
@ -2508,6 +2686,27 @@ void OvsSendPacketRawNow(OPENVPN_SERVER *s, OPENVPN_SESSION *se, void *data, UIN
return;
}
// Scramble the packet
switch (se->ObfuscationMode)
{
case OPENVPN_SCRAMBLE_MODE_DISABLED:
break;
case OPENVPN_SCRAMBLE_MODE_XORMASK:
OvsDataXorMask(data, size, s->Cedar->OpenVPNObfuscationMask, StrLen(s->Cedar->OpenVPNObfuscationMask));
break;
case OPENVPN_SCRAMBLE_MODE_XORPTRPOS:
OvsDataXorPtrPos(data, size);
break;
case OPENVPN_SCRAMBLE_MODE_REVERSE:
OvsDataReverse(data, size);
break;
case OPENVPN_SCRAMBLE_MODE_OBFUSCATE:
OvsDataXorPtrPos(data, size);
OvsDataReverse(data, size);
OvsDataXorPtrPos(data, size);
OvsDataXorMask(data, size, s->Cedar->OpenVPNObfuscationMask, StrLen(s->Cedar->OpenVPNObfuscationMask));
}
u = NewUdpPacket(&se->ServerIp, se->ServerPort, &se->ClientIp, se->ClientPort,
data, size);