mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-25 10:59:53 +03:00
2aaf9012a0
Also, the default timeout value is set to 30000 (milliseconds) instead of 10000. The change is made because it was reported that some routers failed to connect in time.
3057 lines
71 KiB
C
3057 lines
71 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Cedar Communication Module
|
|
|
|
|
|
// Proto_OpenVPN.c
|
|
// OpenVPN protocol stack
|
|
|
|
#include "Proto_OpenVPN.h"
|
|
|
|
#include "Cedar.h"
|
|
#include "Connection.h"
|
|
#include "IPC.h"
|
|
#include "Logging.h"
|
|
#include "Proto_EtherIP.h"
|
|
#include "Proto_PPP.h"
|
|
#include "Server.h"
|
|
|
|
#include "Mayaqua/Internat.h"
|
|
#include "Mayaqua/Memory.h"
|
|
#include "Mayaqua/Object.h"
|
|
#include "Mayaqua/Str.h"
|
|
#include "Mayaqua/Table.h"
|
|
#include "Mayaqua/Tick64.h"
|
|
|
|
// Ping signature of the OpenVPN protocol
|
|
static UCHAR ping_signature[] =
|
|
{
|
|
0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb,
|
|
0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48
|
|
};
|
|
|
|
const PROTO_IMPL *OvsGetProtoImpl()
|
|
{
|
|
static const PROTO_IMPL impl =
|
|
{
|
|
OvsName,
|
|
OvsOptions,
|
|
NULL,
|
|
OvsInit,
|
|
OvsFree,
|
|
OvsIsPacketForMe,
|
|
OvsProcessData,
|
|
OvsProcessDatagrams
|
|
};
|
|
|
|
return &impl;
|
|
}
|
|
|
|
const char *OvsName()
|
|
{
|
|
return "OpenVPN";
|
|
}
|
|
|
|
const PROTO_OPTION *OvsOptions()
|
|
{
|
|
static const PROTO_OPTION options[] =
|
|
{
|
|
{ .Name = "DefaultClientOption", .Type = PROTO_OPTION_STRING, .String = "dev-type tun,link-mtu 1500,tun-mtu 1500,cipher AES-128-CBC,auth SHA1,keysize 128,key-method 2,tls-client" },
|
|
{ .Name = "Obfuscation", .Type = PROTO_OPTION_BOOL, .Bool = false },
|
|
{ .Name = "ObfuscationMask", .Type = PROTO_OPTION_STRING, .String = "" },
|
|
{ .Name = "PingSendInterval", .Type = PROTO_OPTION_UINT32, .UInt32 = 3000 },
|
|
{ .Name = "PushDummyIPv4AddressOnL2Mode", .Type = PROTO_OPTION_BOOL, .Bool = true },
|
|
{ .Name = "Timeout", .Type = PROTO_OPTION_UINT32, .UInt32 = 30000 },
|
|
{ .Name = NULL, .Type = PROTO_OPTION_UNKNOWN }
|
|
};
|
|
|
|
return options;
|
|
}
|
|
|
|
bool OvsInit(void **param, const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname)
|
|
{
|
|
if (param == NULL || options == NULL || cedar == NULL || im == NULL || se == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Debug("OvsInit(): cipher: %s, hostname: %s\n", cipher, hostname);
|
|
|
|
*param = NewOpenVpnServer(options, cedar, im, se);
|
|
|
|
return true;
|
|
}
|
|
|
|
void OvsFree(void *param)
|
|
{
|
|
FreeOpenVpnServer(param);
|
|
}
|
|
|
|
// Check whether it's an OpenVPN packet
|
|
bool OvsIsPacketForMe(const PROTO_MODE mode, const void *data, const UINT size)
|
|
{
|
|
if (data == NULL || size < 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (mode == PROTO_MODE_TCP)
|
|
{
|
|
const UCHAR *raw = data;
|
|
if (raw[0] == 0x00 && raw[1] == 0x0E)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (mode == PROTO_MODE_UDP)
|
|
{
|
|
OPENVPN_PACKET *packet = OvsParsePacket(data, size);
|
|
if (packet == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OvsFreePacket(packet);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OvsProcessData(void *param, TCP_RAW_DATA *in, FIFO *out)
|
|
{
|
|
bool ret = true;
|
|
UINT i;
|
|
OPENVPN_SERVER *server = param;
|
|
UCHAR buf[OPENVPN_TCP_MAX_PACKET_SIZE];
|
|
|
|
if (server == NULL || in == NULL || out == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Separate to a list of datagrams by interpreting the data received from the TCP socket
|
|
while (true)
|
|
{
|
|
UDPPACKET *packet;
|
|
USHORT payload_size, packet_size;
|
|
FIFO *fifo = in->Data;
|
|
const UINT fifo_size = FifoSize(fifo);
|
|
|
|
if (fifo_size < sizeof(USHORT))
|
|
{
|
|
// Non-arrival
|
|
break;
|
|
}
|
|
|
|
// The beginning of a packet contains the data size
|
|
payload_size = READ_USHORT(FifoPtr(fifo));
|
|
packet_size = payload_size + sizeof(USHORT);
|
|
|
|
if (payload_size == 0 || packet_size > sizeof(buf))
|
|
{
|
|
ret = false;
|
|
Debug("OvsProcessData(): Invalid payload size: %u bytes\n", payload_size);
|
|
break;
|
|
}
|
|
|
|
if (fifo_size < packet_size)
|
|
{
|
|
// Non-arrival
|
|
break;
|
|
}
|
|
|
|
if (ReadFifo(fifo, buf, packet_size) != packet_size)
|
|
{
|
|
ret = false;
|
|
Debug("OvsProcessData(): ReadFifo() failed to read the packet\n");
|
|
break;
|
|
}
|
|
|
|
// Insert packet into the list
|
|
packet = NewUdpPacket(&in->SrcIP, in->SrcPort, &in->DstIP, in->DstPort, Clone(buf + sizeof(USHORT), payload_size), payload_size);
|
|
Add(server->RecvPacketList, packet);
|
|
}
|
|
|
|
// Process the list of received datagrams
|
|
OvsRecvPacket(server, server->RecvPacketList, OPENVPN_PROTOCOL_TCP);
|
|
|
|
// Release the received packet list
|
|
for (i = 0; i < LIST_NUM(server->RecvPacketList); ++i)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(server->RecvPacketList, i);
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
DeleteAll(server->RecvPacketList);
|
|
|
|
// Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server
|
|
for (i = 0; i < LIST_NUM(server->SendPacketList); ++i)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(server->SendPacketList, i);
|
|
|
|
// Store the size in the TCP send queue first
|
|
USHORT us = Endian16((USHORT)p->Size);
|
|
|
|
WriteFifo(out, &us, sizeof(USHORT));
|
|
|
|
// Write the data body
|
|
WriteFifo(out, p->Data, p->Size);
|
|
|
|
// Packet release
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
DeleteAll(server->SendPacketList);
|
|
|
|
if (server->Giveup <= server->Now)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < LIST_NUM(server->SessionList); ++i)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(server->SessionList, i);
|
|
|
|
if (se->Established)
|
|
{
|
|
return ret && server->DisconnectCount < 1;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
server->SupressSendPacket = FifoSize(out) > MAX_BUFFERING_PACKET_SIZE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool OvsProcessDatagrams(void *param, LIST *in, LIST *out)
|
|
{
|
|
UINT i;
|
|
LIST *to_send;
|
|
OPENVPN_SERVER *server = param;
|
|
|
|
if (server == NULL || in == NULL || out == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OvsRecvPacket(server, in, OPENVPN_PROTOCOL_UDP);
|
|
|
|
to_send = server->SendPacketList;
|
|
|
|
for (i = 0; i < LIST_NUM(to_send); ++i)
|
|
{
|
|
Add(out, LIST_DATA(to_send, i));
|
|
}
|
|
|
|
DeleteAll(server->SendPacketList);
|
|
|
|
if (server->Giveup <= server->Now)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < LIST_NUM(server->SessionList); ++i)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(server->SessionList, i);
|
|
|
|
if (se->Established)
|
|
{
|
|
return server->DisconnectCount < 1;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Write the OpenVPN log
|
|
void OvsLog(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, char *name, ...)
|
|
{
|
|
wchar_t prefix[MAX_SIZE * 2];
|
|
wchar_t buf2[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (s == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (se == NULL)
|
|
{
|
|
UniStrCpy(prefix, sizeof(prefix), _UU("LO_PREFIX_RAW"));
|
|
}
|
|
else
|
|
{
|
|
if (c == NULL)
|
|
{
|
|
UniFormat(prefix, sizeof(prefix), _UU("LO_PREFIX_SESSION"),
|
|
se->Id, &se->ClientIp, se->ClientPort, &se->ServerIp, se->ServerPort);
|
|
}
|
|
else
|
|
{
|
|
UniFormat(prefix, sizeof(prefix), _UU("LO_PREFIX_CHANNEL"),
|
|
se->Id, &se->ClientIp, se->ClientPort, &se->ServerIp, se->ServerPort,
|
|
c->KeyId);
|
|
}
|
|
}
|
|
va_start(args, name);
|
|
UniFormatArgs(buf2, sizeof(buf2), _UU(name), args);
|
|
va_end(args);
|
|
|
|
UniStrCat(prefix, sizeof(prefix), buf2);
|
|
|
|
WriteServerLog(s->Cedar, prefix);
|
|
}
|
|
|
|
// Encrypt the data
|
|
UINT OvsEncrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *tag, UCHAR *dest, UCHAR *src, UINT src_size, UCHAR *aad, UINT aad_size)
|
|
{
|
|
// Validate arguments
|
|
if (cipher == NULL || (cipher->IsAeadCipher == false && md == NULL))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (cipher->IsAeadCipher)
|
|
{
|
|
// Encrypt in AEAD mode (no HMAC)
|
|
UINT dest_size = CipherProcessAead(cipher, iv, tag, 16, dest, src, src_size, aad, aad_size);
|
|
if (dest_size == 0)
|
|
{
|
|
Debug("OvsEncrypt(): CipherProcessAead() failed!\n");
|
|
return 0;
|
|
}
|
|
|
|
return dest_size;
|
|
}
|
|
else
|
|
{
|
|
// Encrypt in non-AEAD mode (with HMAC)
|
|
UINT ret;
|
|
UINT dest_size = CipherProcess(cipher, iv, dest + md->Size + cipher->IvSize, src, src_size);
|
|
if (dest_size == 0)
|
|
{
|
|
Debug("OvsEncrypt(): CipherProcess() failed!\n");
|
|
return 0;
|
|
}
|
|
|
|
// Copy the IV
|
|
Copy(dest + md->Size, iv, cipher->IvSize);
|
|
dest_size += cipher->IvSize;
|
|
|
|
// Calculate the HMAC
|
|
ret = MdProcess(md, dest, dest + md->Size, dest_size);
|
|
if (ret == 0)
|
|
{
|
|
Debug("OvsEncrypt(): MdProcess() failed!\n");
|
|
return 0;
|
|
}
|
|
|
|
return dest_size + ret;
|
|
}
|
|
}
|
|
|
|
// Decrypt the data
|
|
UINT OvsDecrypt(CIPHER *cipher, MD *md, UCHAR *iv, UCHAR *dest, UCHAR *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (cipher == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (cipher->IsAeadCipher)
|
|
{
|
|
UCHAR *tag = src;
|
|
|
|
if (iv == NULL || size <= OPENVPN_TAG_SIZE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
src += OPENVPN_TAG_SIZE;
|
|
size -= OPENVPN_TAG_SIZE;
|
|
|
|
// Payload
|
|
if (size >= 1 && (cipher->BlockSize == 0 || (size % cipher->BlockSize) == 0))
|
|
{
|
|
// Decryption
|
|
UINT ret = CipherProcessAead(cipher, iv, tag, OPENVPN_TAG_SIZE, dest, src, size, iv, sizeof(UINT));
|
|
if (ret == 0)
|
|
{
|
|
Debug("OvsDecrypt(): CipherProcessAead() failed!\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UCHAR *hmac;
|
|
UCHAR hmac_test[128];
|
|
|
|
if (md == NULL || iv == NULL || size < (md->Size + cipher->IvSize + sizeof(UINT)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// HMAC
|
|
hmac = src;
|
|
src += md->Size;
|
|
size -= md->Size;
|
|
|
|
if (MdProcess(md, hmac_test, src, size) == 0)
|
|
{
|
|
Debug("OvsDecrypt(): MdProcess() failed!\n");
|
|
return 0;
|
|
}
|
|
|
|
if (Cmp(hmac_test, hmac, md->Size) != 0)
|
|
{
|
|
Debug("OvsDecrypt(): HMAC verification failed!\n");
|
|
return 0;
|
|
}
|
|
|
|
// IV
|
|
Copy(iv, src, cipher->IvSize);
|
|
src += cipher->IvSize;
|
|
size -= cipher->IvSize;
|
|
|
|
// Payload
|
|
if (size >= 1 && (cipher->BlockSize == 0 || (size % cipher->BlockSize) == 0))
|
|
{
|
|
// Decryption
|
|
UINT ret = CipherProcess(cipher, iv, dest, src, size);
|
|
if (ret == 0)
|
|
{
|
|
Debug("OvsDecrypt(): CipherProcess() failed!\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
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
|
|
if (s == NULL || p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Search for the session
|
|
se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, protocol);
|
|
if (se == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Detect obfuscation mode and save it for the next packets in the same session
|
|
if (se->ObfuscationMode == INFINITE)
|
|
{
|
|
se->ObfuscationMode = OvsDetectObfuscation(p->Data, p->Size, s->ObfuscationMask);
|
|
if (se->ObfuscationMode != INFINITE)
|
|
{
|
|
Debug("OvsProceccRecvPacket(): detected packet obfuscation/scrambling mode: %u\n", se->ObfuscationMode);
|
|
}
|
|
else
|
|
{
|
|
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->ObfuscationMask, StrLen(s->ObfuscationMask));
|
|
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->ObfuscationMask, StrLen(s->ObfuscationMask));
|
|
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;
|
|
}
|
|
|
|
c = se->Channels[recv_packet->KeyId];
|
|
|
|
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
|
|
{
|
|
// Control packet
|
|
Debug("OvsProceccRecvPacket(): Received control packet. PacketId: %u, OpCode: %u, KeyId: %u, MySessionId: %I64u\n",
|
|
recv_packet->PacketId, recv_packet->OpCode, recv_packet->KeyId, recv_packet->MySessionId);
|
|
|
|
if (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2 ||
|
|
recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
|
|
{
|
|
// Connection request packet
|
|
if (c != NULL && c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
|
|
{
|
|
// If there's already an established data channel, release it
|
|
OvsFreeChannel(se->Channels[recv_packet->KeyId]);
|
|
c = se->Channels[recv_packet->KeyId] = NULL;
|
|
Debug("OvsProceccRecvPacket(): Released established data channel: %u\n", recv_packet->KeyId);
|
|
}
|
|
|
|
if (c == 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("OvsProceccRecvPacket(): Created a 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);
|
|
}
|
|
*/
|
|
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);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Data packet
|
|
if (c != NULL && 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);
|
|
if (size > sizeof(UINT))
|
|
{
|
|
// 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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
// Remove a packet which the opponent has received from the transmission list
|
|
void OvsDeleteFromSendingControlPacketList(OPENVPN_CHANNEL *c, UINT num_acks, UINT *acks)
|
|
{
|
|
LIST *o;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (c == NULL || num_acks == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
o = NewListFast(NULL);
|
|
for (i = 0; i < num_acks; i++)
|
|
{
|
|
UINT ack = acks[i];
|
|
UINT j;
|
|
|
|
for (j = 0; j < LIST_NUM(c->SendControlPacketList); j++)
|
|
{
|
|
OPENVPN_CONTROL_PACKET *p = LIST_DATA(c->SendControlPacketList, j);
|
|
|
|
if (p->PacketId == ack)
|
|
{
|
|
AddDistinct(o, p);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < LIST_NUM(o); i++)
|
|
{
|
|
OPENVPN_CONTROL_PACKET *p = LIST_DATA(o, i);
|
|
|
|
Delete(c->SendControlPacketList, p);
|
|
|
|
OvsFreeControlPacket(p);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
// Process the received control packet
|
|
void OvsProcessRecvControlPacket(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, OPENVPN_PACKET *p)
|
|
{
|
|
FIFO *recv_fifo = NULL;
|
|
FIFO *send_fifo = NULL;
|
|
// Validate arguments
|
|
if (s == NULL || se == NULL || c == NULL || p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->OpCode == OPENVPN_P_CONTROL_V1)
|
|
{
|
|
Debug("SSL (c=%u): %u\n", c->KeyId, p->DataSize);
|
|
|
|
if (c->SslPipe == NULL)
|
|
{
|
|
// Create an SSL pipe
|
|
Lock(s->Cedar->lock);
|
|
{
|
|
if (s->Dh->Size != s->Cedar->DhParamBits)
|
|
{
|
|
DhFree(s->Dh);
|
|
s->Dh = DhNewFromBits(s->Cedar->DhParamBits);
|
|
}
|
|
|
|
c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, true, &c->ClientCert);
|
|
}
|
|
Unlock(s->Cedar->lock);
|
|
|
|
Debug("SSL Pipe Created (c=%u).\n", c->KeyId);
|
|
}
|
|
|
|
if (c->SslPipe->IsDisconnected == false)
|
|
{
|
|
// Pour the physically received data into SSL pipe
|
|
if (FifoSize(c->SslPipe->RawIn->SendFifo) < OPENVPN_MAX_SSL_RECV_BUF_SIZE)
|
|
{
|
|
Debug("SSL_Write: %u\n", p->DataSize);
|
|
WriteFifo(c->SslPipe->RawIn->SendFifo, p->Data, p->DataSize);
|
|
}
|
|
SyncSslPipe(c->SslPipe);
|
|
}
|
|
}
|
|
|
|
if (c->SslPipe != NULL && c->SslPipe->IsDisconnected == false)
|
|
{
|
|
recv_fifo = c->SslPipe->SslInOut->RecvFifo;
|
|
send_fifo = c->SslPipe->SslInOut->SendFifo;
|
|
}
|
|
|
|
Debug("SIZE: recv_fifo = %u, send_fifo = %u\n", FifoSize(recv_fifo), FifoSize(send_fifo));
|
|
|
|
switch (c->Status)
|
|
{
|
|
case OPENVPN_CHANNEL_STATUS_INIT:
|
|
switch (p->OpCode)
|
|
{
|
|
case OPENVPN_P_CONTROL_SOFT_RESET_V1:
|
|
// Key update (soft reset)
|
|
if (se->Established)
|
|
{
|
|
if (c->IsInitiatorServer == false)
|
|
{
|
|
OvsSendControlPacket(c, OPENVPN_P_CONTROL_SOFT_RESET_V1, NULL, 0);
|
|
}
|
|
|
|
c->Status = OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_KEY;
|
|
c->IsRekeyChannel = true;
|
|
}
|
|
break;
|
|
|
|
case OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2:
|
|
// New connection (hard reset)
|
|
OvsSendControlPacketEx(c, OPENVPN_P_CONTROL_HARD_RESET_SERVER_V2, NULL, 0, true);
|
|
|
|
c->Status = OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_KEY;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_KEY:
|
|
if (FifoSize(recv_fifo) >= 1)
|
|
{
|
|
OPENVPN_KEY_METHOD_2 data;
|
|
UCHAR *ptr = FifoPtr(recv_fifo);
|
|
|
|
// Parse OPENVPN_KEY_METHOD_2
|
|
UINT read_size = OvsParseKeyMethod2(&data, ptr, FifoSize(recv_fifo), true);
|
|
if (read_size != 0)
|
|
{
|
|
BUF *b;
|
|
|
|
// Success in parsing key information
|
|
ReadFifo(recv_fifo, NULL, read_size);
|
|
|
|
// Set session parameters
|
|
OvsSetupSessionParameters(s, se, c, &data);
|
|
|
|
// Build OPENVPN_KEY_METHOD_2 to respond
|
|
b = OvsBuildKeyMethod2(&c->ServerKey);
|
|
|
|
// Transmission of the response data
|
|
if (b != NULL)
|
|
{
|
|
WriteFifo(send_fifo, b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
}
|
|
|
|
// State transition
|
|
c->Status = OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_PUSH_REQUEST;
|
|
if (c->IsRekeyChannel)
|
|
{
|
|
c->Status = OPENVPN_CHANNEL_STATUS_ESTABLISHED;
|
|
c->EstablishedTick = s->Now;
|
|
Debug("OpenVPN Channel %u Established (re-key).\n", c->KeyId);
|
|
OvsLog(s, se, c, "LO_CHANNEL_ESTABLISHED_NEWKEY");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPENVPN_CHANNEL_STATUS_TLS_WAIT_CLIENT_PUSH_REQUEST:
|
|
if (FifoSize(recv_fifo) >= 1)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
UINT read_size = OvsPeekStringFromFifo(recv_fifo, tmp, sizeof(tmp));
|
|
|
|
if (read_size >= 1)
|
|
{
|
|
Debug("Client->Server (c=%u): %s\n", c->KeyId, tmp);
|
|
|
|
ReadFifo(recv_fifo, NULL, read_size);
|
|
|
|
if (StartWith(tmp, "PUSH_REQUEST"))
|
|
{
|
|
// Since connection requested, start VPN connection
|
|
// When the IPC VPN connection has not been started yet, start it
|
|
OvsBeginIPCAsyncConnectionIfEmpty(s, se, c);
|
|
|
|
// State transition
|
|
c->Status = OPENVPN_CHANNEL_STATUS_TLS_VPN_CONNECTING;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPENVPN_CHANNEL_STATUS_TLS_VPN_CONNECTING:
|
|
case OPENVPN_CHANNEL_STATUS_ESTABLISHED:
|
|
if (FifoSize(recv_fifo) >= 1)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
UINT read_size = OvsPeekStringFromFifo(recv_fifo, tmp, sizeof(tmp));
|
|
|
|
if (read_size >= 1)
|
|
{
|
|
Debug("Client->Server (c=%u): %s\n", c->KeyId, tmp);
|
|
|
|
ReadFifo(recv_fifo, NULL, read_size);
|
|
|
|
if (StartWith(tmp, "PUSH_REQUEST"))
|
|
{
|
|
WriteFifo(send_fifo, se->PushReplyStr, StrLen(se->PushReplyStr));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Calculate the proper MSS
|
|
UINT OvsCalcTcpMss(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c)
|
|
{
|
|
UINT ret = MTU_FOR_PPPOE;
|
|
// Validate arguments
|
|
if (s == NULL || se == NULL || c == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (c->MdSend == NULL || c->CipherEncrypt == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (se->Protocol == OPENVPN_PROTOCOL_TCP)
|
|
{
|
|
// Calculation is not required for TCP mode
|
|
return 0;
|
|
}
|
|
|
|
// IPv4 / IPv6
|
|
if (IsIP4(&se->ClientIp))
|
|
{
|
|
ret -= 20;
|
|
}
|
|
else
|
|
{
|
|
ret -= 40;
|
|
}
|
|
|
|
// UDP
|
|
ret -= 8;
|
|
|
|
// opcode
|
|
ret -= 1;
|
|
|
|
// HMAC
|
|
ret -= c->MdSend->Size;
|
|
|
|
// IV
|
|
ret -= c->CipherEncrypt->IvSize;
|
|
|
|
// Packet ID
|
|
ret -= 4;
|
|
|
|
if (c->CipherEncrypt->IsNullCipher == false)
|
|
{
|
|
// block
|
|
ret -= c->CipherEncrypt->BlockSize;
|
|
}
|
|
|
|
if (se->Mode == OPENVPN_MODE_L2)
|
|
{
|
|
// Inner Ethernet Header
|
|
ret -= 14;
|
|
}
|
|
|
|
// Inner IPv4
|
|
ret -= 20;
|
|
|
|
// Inner TCP
|
|
ret -= 20;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// When the IPC VPN connection has not been started yet, start it
|
|
void OvsBeginIPCAsyncConnectionIfEmpty(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c)
|
|
{
|
|
// Validate arguments
|
|
if (s == NULL || se == NULL || c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsIPCConnected(se->Ipc) == false)
|
|
{
|
|
FreeIPC(se->Ipc);
|
|
|
|
se->Ipc = NULL;
|
|
}
|
|
|
|
if (se->IpcAsync == NULL)
|
|
{
|
|
LIST *pi;
|
|
IPC_PARAM p;
|
|
ETHERIP_ID id;
|
|
|
|
Zero(&p, sizeof(p));
|
|
Zero(&id, sizeof(id));
|
|
|
|
// Parse the user name
|
|
PPPParseUsername(s->Cedar, c->ClientKey.Username, &id);
|
|
|
|
|
|
// Build IPC connection parameters
|
|
StrCpy(p.ClientName, sizeof(p.ClientName), OPENVPN_IPC_CLIENT_NAME);
|
|
StrCpy(p.Postfix, sizeof(p.Postfix), (se->Mode == OPENVPN_MODE_L3 ? OPENVPN_IPC_POSTFIX_L3 : OPENVPN_IPC_POSTFIX_L2));
|
|
|
|
StrCpy(p.UserName, sizeof(p.UserName), id.UserName);
|
|
StrCpy(p.HubName, sizeof(p.HubName), id.HubName);
|
|
StrCpy(p.Password, sizeof(p.Password), c->ClientKey.Password);
|
|
|
|
Copy(&p.ClientIp, &se->ClientIp, sizeof(IP));
|
|
p.ClientPort = se->ClientPort;
|
|
|
|
Copy(&p.ServerIp, &se->ServerIp, sizeof(IP));
|
|
p.ServerPort = se->ServerPort;
|
|
|
|
if (c->CipherEncrypt->IsNullCipher == false)
|
|
{
|
|
StrCpy(p.CryptName, sizeof(p.CryptName), c->CipherEncrypt->Name);
|
|
}
|
|
|
|
// OpenVPN sends the default gateway's MAC address,
|
|
// if the option --push-peer-info is enabled.
|
|
// It also sends all of the client's environment
|
|
// variables whose names start with "UV_".
|
|
pi = NewEntryList(c->ClientKey.PeerInfo, "\n", "=\t");
|
|
|
|
// Check presence of custom hostname
|
|
if (EntryListHasKey(pi, "UV_HOSTNAME"))
|
|
{
|
|
StrCpy(p.ClientHostname, sizeof(p.ClientHostname), EntryListStrValue(pi, "UV_HOSTNAME"));
|
|
}
|
|
else // Use the default gateway's MAC address
|
|
{
|
|
StrCpy(p.ClientHostname, sizeof(p.ClientHostname), EntryListStrValue(pi, "IV_HWADDR"));
|
|
}
|
|
|
|
FreeEntryList(pi);
|
|
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
// L3 Mode
|
|
p.IsL3Mode = true;
|
|
}
|
|
else
|
|
{
|
|
// L2 Mode
|
|
p.BridgeMode = true;
|
|
}
|
|
|
|
if (IsEmptyStr(c->ClientKey.Username) || IsEmptyStr(c->ClientKey.Password))
|
|
{
|
|
// OpenVPN X.509 certificate authentication will be used only when no username / password is specified
|
|
if (c->ClientCert.X != NULL)
|
|
{
|
|
p.ClientCertificate = c->ClientCert.X;
|
|
}
|
|
}
|
|
|
|
p.Layer = (se->Mode == OPENVPN_MODE_L2) ? IPC_LAYER_2 : IPC_LAYER_3;
|
|
|
|
// Calculate the MSS
|
|
p.Mss = OvsCalcTcpMss(s, se, c);
|
|
Debug("MSS=%u\n", p.Mss);
|
|
|
|
// Start an IPC connection
|
|
se->IpcAsync = NewIPCAsync(s->Cedar, &p, s->SockEvent);
|
|
}
|
|
}
|
|
|
|
// Peek a NULL-terminated string from the FIFO
|
|
UINT OvsPeekStringFromFifo(FIFO *f, char *str, UINT str_size)
|
|
{
|
|
UINT i;
|
|
bool ok = false;
|
|
// Validate arguments
|
|
if (f == NULL || str == NULL || str_size == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
StrCpy(str, str_size, "");
|
|
|
|
for (i = 0; i < MIN(str_size, FifoSize(f)); i++)
|
|
{
|
|
char c = *(((char *)FifoPtr(f)) + i);
|
|
|
|
if (c != 0)
|
|
{
|
|
str[i] = c;
|
|
}
|
|
else
|
|
{
|
|
str[i] = 0;
|
|
i++;
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok == false)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// Set session parameters
|
|
void OvsSetupSessionParameters(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, OPENVPN_KEY_METHOD_2 *data)
|
|
{
|
|
LIST *o;
|
|
BUF *b;
|
|
char opt_str[MAX_SIZE];
|
|
char *cipher_name, *md_name;
|
|
// Validate arguments
|
|
if (s == NULL || se == NULL || c == NULL || data == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(&c->ClientKey, data, sizeof(OPENVPN_KEY_METHOD_2));
|
|
|
|
// Parse the parameter string
|
|
Debug("Parsing Option Str: %s\n", data->OptionString);
|
|
|
|
OvsLog(s, se, c, "LO_OPTION_STR_RECV", data->OptionString);
|
|
|
|
if (c->ClientCert.X != NULL)
|
|
{
|
|
if (c->ClientCert.X->subject_name != NULL)
|
|
{
|
|
OvsLog(s, se, c, "LO_CLIENT_CERT", c->ClientCert.X->subject_name->CommonName);
|
|
}
|
|
else
|
|
{
|
|
OvsLog(s, se, c, "LO_CLIENT_CERT", "(unknown CN)");
|
|
}
|
|
}
|
|
else if (!c->ClientCert.PreverifyErr)
|
|
{
|
|
OvsLog(s, se, c, "LO_CLIENT_NO_CERT");
|
|
}
|
|
else
|
|
{
|
|
OvsLog(s, se, c, "LO_CLIENT_UNVERIFIED_CERT", c->ClientCert.PreverifyErrMessage);
|
|
}
|
|
|
|
Zero(opt_str, sizeof(opt_str));
|
|
StrCpy(opt_str, sizeof(opt_str), data->OptionString);
|
|
if (s->Cedar != NULL && (IsEmptyStr(opt_str) || StartWith(opt_str, "V0 UNDEF") || InStr(opt_str, ",") == false))
|
|
{
|
|
StrCpy(opt_str, sizeof(opt_str), s->DefaultClientOption);
|
|
}
|
|
|
|
o = NewEntryList(opt_str, ",", " \t");
|
|
|
|
if (se->Mode == OPENVPN_MODE_UNKNOWN)
|
|
{
|
|
UINT mtu;
|
|
// Layer
|
|
if (StrCmpi(EntryListStrValue(o, "dev-type"), "tun") == 0)
|
|
{
|
|
// L3
|
|
se->Mode = OPENVPN_MODE_L3;
|
|
}
|
|
else
|
|
{
|
|
// L2
|
|
se->Mode = OPENVPN_MODE_L2;
|
|
}
|
|
|
|
// Link MTU
|
|
mtu = EntryListIntValue(o, "link-mtu");
|
|
if (mtu == 0)
|
|
{
|
|
mtu = OPENVPN_MTU_LINK;
|
|
}
|
|
se->LinkMtu = mtu;
|
|
|
|
// Tun MTU
|
|
mtu = EntryListIntValue(o, "tun-mtu");
|
|
if (mtu == 0)
|
|
{
|
|
mtu = OPENVPN_MTU_TUN;
|
|
}
|
|
se->TunMtu = mtu;
|
|
}
|
|
|
|
// Protocol
|
|
if (se->Protocol == OPENVPN_PROTOCOL_TCP)
|
|
{
|
|
// TCP
|
|
if (IsIP6(&se->ClientIp) == false)
|
|
{
|
|
StrCpy(c->Proto, sizeof(c->Proto), "TCPv4_SERVER");
|
|
}
|
|
else
|
|
{
|
|
StrCpy(c->Proto, sizeof(c->Proto), "TCPv6_SERVER");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// UDP
|
|
if (IsIP6(&se->ClientIp) == false)
|
|
{
|
|
StrCpy(c->Proto, sizeof(c->Proto), "UDPv4");
|
|
}
|
|
else
|
|
{
|
|
StrCpy(c->Proto, sizeof(c->Proto), "UDPv6");
|
|
}
|
|
}
|
|
|
|
// Encryption algorithm
|
|
cipher_name = EntryListStrValue(o, "cipher");
|
|
|
|
// Hash algorithm
|
|
md_name = EntryListStrValue(o, "auth");
|
|
|
|
// Random number generation
|
|
Rand(c->ServerKey.Random1, sizeof(c->ServerKey.Random1));
|
|
Rand(c->ServerKey.Random2, sizeof(c->ServerKey.Random2));
|
|
|
|
// Generate the Master Secret
|
|
b = NewBuf();
|
|
WriteBuf(b, OPENVPN_PREMASTER_LABEL, StrLen(OPENVPN_PREMASTER_LABEL));
|
|
WriteBuf(b, c->ClientKey.Random1, sizeof(c->ClientKey.Random1));
|
|
WriteBuf(b, c->ServerKey.Random1, sizeof(c->ServerKey.Random1));
|
|
Enc_tls1_PRF(b->Buf, b->Size,
|
|
c->ClientKey.PreMasterSecret, sizeof(c->ClientKey.PreMasterSecret),
|
|
c->MasterSecret, sizeof(c->MasterSecret));
|
|
FreeBuf(b);
|
|
|
|
// Generate an Expansion Key
|
|
b = NewBuf();
|
|
WriteBuf(b, OPENVPN_EXPANSION_LABEL, StrLen(OPENVPN_EXPANSION_LABEL));
|
|
WriteBuf(b, c->ClientKey.Random2, sizeof(c->ClientKey.Random2));
|
|
WriteBuf(b, c->ServerKey.Random2, sizeof(c->ServerKey.Random2));
|
|
WriteBufInt64(b, se->ClientSessionId);
|
|
WriteBufInt64(b, se->ServerSessionId);
|
|
Enc_tls1_PRF(b->Buf, b->Size, c->MasterSecret, sizeof(c->MasterSecret),
|
|
c->ExpansionKey, sizeof(c->ExpansionKey));
|
|
FreeBuf(b);
|
|
|
|
// Set up the encryption algorithm
|
|
c->CipherEncrypt = OvsGetCipher(cipher_name);
|
|
c->CipherDecrypt = OvsGetCipher(cipher_name);
|
|
SetCipherKey(c->CipherDecrypt, c->ExpansionKey + 0, false);
|
|
SetCipherKey(c->CipherEncrypt, c->ExpansionKey + 128, true);
|
|
|
|
if (c->CipherDecrypt->IsAeadCipher)
|
|
{
|
|
// In AEAD mode the IV is composed by the packet ID and a part of the HMAC key
|
|
Copy(c->IvRecv + sizeof(c->LastDataPacketId), c->ExpansionKey + 64, c->CipherDecrypt->IvSize - sizeof(c->LastDataPacketId));
|
|
Copy(c->IvSend + sizeof(c->LastDataPacketId), c->ExpansionKey + 192, c->CipherEncrypt->IvSize - sizeof(c->LastDataPacketId));
|
|
}
|
|
else
|
|
{
|
|
// Set up the hash algorithm
|
|
c->MdSend = OvsGetMd(md_name);
|
|
c->MdRecv = OvsGetMd(md_name);
|
|
SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size);
|
|
SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size);
|
|
}
|
|
|
|
// We pass the cipher name sent from the OpenVPN client, unless it's a different cipher, to prevent a message such as:
|
|
// WARNING: 'cipher' is used inconsistently, local='cipher AES-128-GCM', remote='cipher aes-128-gcm'
|
|
// It happens because OpenVPN uses "strcmp()" to compare the local and remote parameters:
|
|
// https://github.com/OpenVPN/openvpn/blob/a6fd48ba36ede465b0905a95568c3ec0d425ca71/src/openvpn/options.c#L3819-L3831
|
|
if (StrCmpi(cipher_name, c->CipherEncrypt->Name) != 0)
|
|
{
|
|
cipher_name = c->CipherEncrypt->Name;
|
|
}
|
|
|
|
// Generate the response option string
|
|
Format(c->ServerKey.OptionString, sizeof(c->ServerKey.OptionString),
|
|
"V4,dev-type %s,link-mtu %u,tun-mtu %u,proto %s,"
|
|
"cipher %s,auth %s,keysize %u,key-method 2,tls-server",
|
|
(se->Mode == OPENVPN_MODE_L2 ? "tap" : "tun"),
|
|
se->LinkMtu,
|
|
se->TunMtu,
|
|
c->Proto,
|
|
cipher_name, md_name, c->CipherEncrypt->KeySize * 8);
|
|
|
|
FreeEntryList(o);
|
|
|
|
Debug("OvsSetupSessionParameters(): Built OptionString: %s\n", c->ServerKey.OptionString);
|
|
OvsLog(s, se, c, "LO_OPTION_STR_SEND", c->ServerKey.OptionString);
|
|
}
|
|
|
|
// Get the encryption algorithm
|
|
CIPHER *OvsGetCipher(char *name)
|
|
{
|
|
CIPHER *c = NULL;
|
|
|
|
// OpenVPN sends the cipher name in uppercase, even if it's not standard,
|
|
// thus we have to convert it to lowercase for EVP_get_cipherbyname().
|
|
char lowercase_name[MAX_SIZE];
|
|
StrCpy(lowercase_name, sizeof(lowercase_name), name);
|
|
StrLower(lowercase_name);
|
|
|
|
if (IsEmptyStr(lowercase_name) == false)
|
|
{
|
|
c = NewCipher(lowercase_name);
|
|
}
|
|
|
|
if (c == NULL)
|
|
{
|
|
c = NewCipher(OPENVPN_DEFAULT_CIPHER);
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
// Get the hash algorithm
|
|
MD *OvsGetMd(char *name)
|
|
{
|
|
MD *m = NULL;
|
|
|
|
if (IsEmptyStr(name) == false)
|
|
{
|
|
m = NewMd(name);
|
|
}
|
|
|
|
if (m == NULL)
|
|
{
|
|
m = NewMd(OPENVPN_DEFAULT_MD);
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
// Build the data from KEY_METHOD2
|
|
BUF *OvsBuildKeyMethod2(OPENVPN_KEY_METHOD_2 *d)
|
|
{
|
|
BUF *b;
|
|
UCHAR uc;
|
|
// Validate arguments
|
|
if (d == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
// Reserved
|
|
WriteBufInt(b, 0);
|
|
|
|
// Method
|
|
uc = 2;
|
|
WriteBuf(b, &uc, sizeof(UCHAR));
|
|
|
|
// Random1
|
|
WriteBuf(b, d->Random1, sizeof(d->Random1));
|
|
|
|
// Random2
|
|
WriteBuf(b, d->Random2, sizeof(d->Random2));
|
|
|
|
// Option String
|
|
OvsWriteStringToBuf(b, d->OptionString, sizeof(d->OptionString));
|
|
|
|
// Username
|
|
OvsWriteStringToBuf(b, d->Username, sizeof(d->Username));
|
|
|
|
// Password
|
|
OvsWriteStringToBuf(b, d->Password, sizeof(d->Password));
|
|
|
|
// PeerInfo
|
|
OvsWriteStringToBuf(b, d->PeerInfo, sizeof(d->PeerInfo));
|
|
|
|
return b;
|
|
}
|
|
|
|
// Append a string to buf
|
|
void OvsWriteStringToBuf(BUF *b, char *str, UINT max_size)
|
|
{
|
|
USHORT us;
|
|
UINT i;
|
|
char *tmp;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (str == NULL)
|
|
{
|
|
str = "";
|
|
}
|
|
|
|
if (StrLen(str) == 0)
|
|
{
|
|
us = 0;
|
|
WriteBuf(b, &us, sizeof(USHORT));
|
|
return;
|
|
}
|
|
|
|
i = StrSize(str);
|
|
i = MIN(i, max_size);
|
|
us = Endian16((USHORT)i);
|
|
WriteBuf(b, &us, sizeof(USHORT));
|
|
|
|
tmp = Malloc(i);
|
|
Copy(tmp, str, i);
|
|
tmp[i - 1] = 0;
|
|
WriteBuf(b, tmp, i);
|
|
|
|
Free(tmp);
|
|
}
|
|
|
|
// Parse the KEY_METHOD2
|
|
UINT OvsParseKeyMethod2(OPENVPN_KEY_METHOD_2 *ret, UCHAR *data, UINT size, bool client_mode)
|
|
{
|
|
BUF *b;
|
|
UINT read_size = 0;
|
|
UINT ui;
|
|
UCHAR uc;
|
|
// Validate arguments
|
|
Zero(ret, sizeof(OPENVPN_KEY_METHOD_2));
|
|
if (ret == NULL || data == NULL || size == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, data, size);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
// Reserved
|
|
if (ReadBuf(b, &ui, sizeof(UINT)) == sizeof(UINT))
|
|
{
|
|
// Method
|
|
if (ReadBuf(b, &uc, sizeof(UCHAR)) == sizeof(UCHAR) && uc == 2)
|
|
{
|
|
// Pre Master Secret
|
|
if (client_mode == false || ReadBuf(b, ret->PreMasterSecret, sizeof(ret->PreMasterSecret)) == sizeof(ret->PreMasterSecret))
|
|
{
|
|
// Random1
|
|
if (ReadBuf(b, ret->Random1, sizeof(ret->Random1)) == sizeof(ret->Random1))
|
|
{
|
|
// Random2
|
|
if (ReadBuf(b, ret->Random2, sizeof(ret->Random2)) == sizeof(ret->Random2))
|
|
{
|
|
// String
|
|
if (OvsReadStringFromBuf(b, ret->OptionString, sizeof(ret->OptionString)) &&
|
|
OvsReadStringFromBuf(b, ret->Username, sizeof(ret->Username)) &&
|
|
OvsReadStringFromBuf(b, ret->Password, sizeof(ret->Password)))
|
|
{
|
|
if (!OvsReadStringFromBuf(b, ret->PeerInfo, sizeof(ret->PeerInfo)))
|
|
{
|
|
Zero(ret->PeerInfo, sizeof(ret->PeerInfo));
|
|
}
|
|
read_size = b->Current;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeBuf(b);
|
|
|
|
return read_size;
|
|
}
|
|
|
|
// Read a string from BUF
|
|
bool OvsReadStringFromBuf(BUF *b, char *str, UINT str_size)
|
|
{
|
|
USHORT us;
|
|
// Validate arguments
|
|
if (b == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ReadBuf(b, &us, sizeof(USHORT)) != sizeof(USHORT))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
us = Endian16(us);
|
|
|
|
if (us == 0)
|
|
{
|
|
StrCpy(str, str_size, "");
|
|
return true;
|
|
}
|
|
|
|
if (us > str_size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ReadBuf(b, str, us) != us)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (str[us - 1] != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Transmission of control packet (Automatic segmentation with the maximum size)
|
|
void OvsSendControlPacketWithAutoSplit(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT data_size)
|
|
{
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (c == NULL || (data_size != 0 && data == NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, data, data_size);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
while (true)
|
|
{
|
|
UCHAR tmp[OPENVPN_CONTROL_PACKET_MAX_DATASIZE];
|
|
UINT size = ReadBuf(b, tmp, sizeof(tmp));
|
|
|
|
if (size == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
OvsSendControlPacket(c, opcode, tmp, size);
|
|
//Debug(" *** CNT SEND %u\n", size);
|
|
}
|
|
|
|
FreeBuf(b);
|
|
}
|
|
|
|
// Send the control packet
|
|
void OvsSendControlPacket(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT data_size)
|
|
{
|
|
OvsSendControlPacketEx(c, opcode, data, data_size, false);
|
|
}
|
|
void OvsSendControlPacketEx(OPENVPN_CHANNEL *c, UCHAR opcode, UCHAR *data, UINT data_size, bool no_resend)
|
|
{
|
|
OPENVPN_CONTROL_PACKET *p;
|
|
// Validate arguments
|
|
if (c == NULL || (data_size != 0 && data == NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
p = ZeroMalloc(sizeof(OPENVPN_CONTROL_PACKET));
|
|
|
|
p->NoResend = no_resend;
|
|
|
|
p->OpCode = opcode;
|
|
p->PacketId = c->NextSendPacketId++;
|
|
|
|
if (data != NULL)
|
|
{
|
|
p->Data = Clone(data, data_size);
|
|
p->DataSize = data_size;
|
|
}
|
|
|
|
p->NextSendTime = 0;
|
|
|
|
Add(c->SendControlPacketList, p);
|
|
}
|
|
|
|
// Release the control packet being transmitted
|
|
void OvsFreeControlPacket(OPENVPN_CONTROL_PACKET *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->Data != NULL)
|
|
{
|
|
Free(p->Data);
|
|
}
|
|
|
|
Free(p);
|
|
}
|
|
|
|
// Get a list of packet ID to be responded
|
|
UINT OvsGetAckReplyList(OPENVPN_CHANNEL *c, UINT *ret)
|
|
{
|
|
UINT i;
|
|
LIST *o = NULL;
|
|
UINT num;
|
|
// Validate arguments
|
|
if (c == NULL || ret == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
num = MIN(LIST_NUM(c->AckReplyList), OPENVPN_MAX_NUMACK);
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
UINT *v = LIST_DATA(c->AckReplyList, i);
|
|
|
|
if (o == NULL)
|
|
{
|
|
o = NewListFast(NULL);
|
|
}
|
|
|
|
Add(o, v);
|
|
|
|
ret[i] = *v;
|
|
}
|
|
|
|
for (i = 0; i < LIST_NUM(o); i++)
|
|
{
|
|
UINT *v = LIST_DATA(o, i);
|
|
|
|
Delete(c->AckReplyList, v);
|
|
|
|
Free(v);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
|
|
return num;
|
|
}
|
|
|
|
// Release the channel
|
|
void OvsFreeChannel(OPENVPN_CHANNEL *c)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->SslPipe != NULL)
|
|
{
|
|
FreeSslPipe(c->SslPipe);
|
|
}
|
|
|
|
ReleaseIntList(c->AckReplyList);
|
|
|
|
for (i = 0; i < LIST_NUM(c->SendControlPacketList); i++)
|
|
{
|
|
OPENVPN_CONTROL_PACKET *p = LIST_DATA(c->SendControlPacketList, i);
|
|
|
|
OvsFreeControlPacket(p);
|
|
}
|
|
|
|
ReleaseList(c->SendControlPacketList);
|
|
|
|
FreeCipher(c->CipherDecrypt);
|
|
FreeCipher(c->CipherEncrypt);
|
|
|
|
FreeMd(c->MdRecv);
|
|
FreeMd(c->MdSend);
|
|
|
|
if (c->ClientCert.X != NULL)
|
|
{
|
|
FreeX(c->ClientCert.X);
|
|
}
|
|
|
|
Free(c);
|
|
}
|
|
|
|
// Create a new channel
|
|
OPENVPN_CHANNEL *OvsNewChannel(OPENVPN_SESSION *se, UCHAR key_id)
|
|
{
|
|
OPENVPN_CHANNEL *c;
|
|
// Validate arguments
|
|
if (se == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
c = ZeroMalloc(sizeof(OPENVPN_CHANNEL));
|
|
|
|
c->Session = se;
|
|
c->Server = se->Server;
|
|
|
|
c->Status = OPENVPN_CHANNEL_STATUS_INIT;
|
|
|
|
c->AckReplyList = NewIntList(true);
|
|
|
|
c->SendControlPacketList = NewListFast(NULL);
|
|
|
|
c->KeyId = key_id;
|
|
|
|
Rand(c->IvSend, sizeof(c->IvSend));
|
|
Rand(c->IvRecv, sizeof(c->IvRecv));
|
|
|
|
//c->NextRekey = se->Server->Now + (UINT64)5000;
|
|
|
|
se->LastCreatedChannelIndex = key_id;
|
|
|
|
return c;
|
|
}
|
|
|
|
// Create a new server-side channel ID
|
|
UINT64 OvsNewServerSessionId(OPENVPN_SERVER *s)
|
|
{
|
|
// Validate arguments
|
|
if (s == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
UINT64 id = Rand64();
|
|
UINT i;
|
|
bool exists = false;
|
|
|
|
if (id == 0 || id == (UINT64)(0xFFFFFFFFFFFFFFFFULL))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < LIST_NUM(s->SessionList); i++)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
|
|
if (se->ServerSessionId == id)
|
|
{
|
|
exists = true;
|
|
}
|
|
}
|
|
|
|
if (exists == false)
|
|
{
|
|
return id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build and submit the OpenVPN data packet
|
|
void OvsSendDataPacket(OPENVPN_CHANNEL *c, UCHAR key_id, UINT data_packet_id, void *data, UINT data_size)
|
|
{
|
|
const UCHAR op = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07);
|
|
UCHAR *dest_data;
|
|
UINT dest_size;
|
|
// Validate arguments
|
|
if (c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// [ xxx ] = unprotected
|
|
// [ - xxx - ] = authenticated
|
|
// [ * xxx * ] = encrypted and authenticated
|
|
|
|
if (c->CipherEncrypt->IsAeadCipher)
|
|
{
|
|
// [ opcode ] [ - packet ID - ] [ TAG ] [ * packet payload * ]
|
|
UCHAR tag[16];
|
|
|
|
// Update variable part (packet ID) of IV
|
|
WRITE_UINT(c->IvSend, data_packet_id);
|
|
|
|
// Prepare a buffer to store the results
|
|
dest_data = Malloc(sizeof(op) + sizeof(data_packet_id) + sizeof(tag) + data_size + 256);
|
|
|
|
// Set data size to the maximum known
|
|
dest_size = sizeof(op) + sizeof(data_packet_id) + sizeof(tag);
|
|
|
|
// Write opcode
|
|
dest_data[0] = op;
|
|
|
|
// Write packet ID
|
|
WRITE_UINT(dest_data + sizeof(op), data_packet_id);
|
|
|
|
// Write encrypted payload
|
|
dest_size += OvsEncrypt(c->CipherEncrypt, NULL, c->IvSend, tag, dest_data + dest_size, data, data_size, c->IvSend, sizeof(data_packet_id));
|
|
|
|
// Write authentication tag
|
|
Copy(dest_data + sizeof(op) + sizeof(data_packet_id), tag, sizeof(tag));
|
|
}
|
|
else
|
|
{
|
|
// [ opcode ] [ HMAC ] [ - IV - ] [ * packet ID * ] [ * packet payload * ]
|
|
UINT encrypted_size = sizeof(data_packet_id) + data_size;
|
|
UCHAR *encrypted_data = ZeroMalloc(encrypted_size);
|
|
WRITE_UINT(encrypted_data, data_packet_id);
|
|
Copy(encrypted_data + sizeof(data_packet_id), data, data_size);
|
|
|
|
// Prepare a buffer to store the results
|
|
dest_data = Malloc(sizeof(op) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256);
|
|
|
|
// Set data size to the maximum known
|
|
dest_size = sizeof(op);
|
|
|
|
// Write opcode
|
|
dest_data[0] = op;
|
|
|
|
// Write IV, encrypted packet ID and payload
|
|
dest_size += OvsEncrypt(c->CipherEncrypt, c->MdSend, c->IvSend, NULL, dest_data + sizeof(op), encrypted_data, encrypted_size, NULL, 0);
|
|
|
|
Free(encrypted_data);
|
|
|
|
// Update the IV
|
|
Copy(c->IvSend, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize);
|
|
}
|
|
|
|
OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size);
|
|
}
|
|
|
|
// Build an OpenVPN control packet
|
|
BUF *OvsBuildPacket(OPENVPN_PACKET *p)
|
|
{
|
|
BUF *b;
|
|
UCHAR uc;
|
|
UINT num_ack;
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
// OpCode + KeyID
|
|
uc = ((p->OpCode << 3) & 0xF8) | (p->KeyId & 0x07);
|
|
WriteBufChar(b, uc);
|
|
|
|
if (p->OpCode == OPENVPN_P_DATA_V1)
|
|
{
|
|
// Data Packet
|
|
WriteBuf(b, p->Data, p->DataSize);
|
|
SeekBuf(b, 0, 0);
|
|
return b;
|
|
}
|
|
|
|
// Sender Channel ID
|
|
WriteBufInt64(b, p->MySessionId);
|
|
|
|
// NumAck
|
|
num_ack = MIN(p->NumAck, OPENVPN_MAX_NUMACK);
|
|
WriteBufChar(b, (UCHAR)num_ack);
|
|
|
|
if (p->NumAck >= 1)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0; i < num_ack; i++)
|
|
{
|
|
WriteBufInt(b, (UCHAR)p->AckPacketId[i]);
|
|
}
|
|
|
|
// Received Channel ID
|
|
WriteBufInt64(b, p->YourSessionId);
|
|
}
|
|
|
|
if (p->OpCode != OPENVPN_P_ACK_V1)
|
|
{
|
|
// Packet ID
|
|
WriteBufInt(b, p->PacketId);
|
|
|
|
// Payload
|
|
if (p->DataSize >= 1 && p->Data != NULL)
|
|
{
|
|
WriteBuf(b, p->Data, p->DataSize);
|
|
}
|
|
}
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Parse the OpenVPN packet
|
|
OPENVPN_PACKET *OvsParsePacket(UCHAR *data, UINT size)
|
|
{
|
|
UCHAR uc;
|
|
OPENVPN_PACKET *ret = NULL;
|
|
// Validate arguments
|
|
if (data == NULL || size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(OPENVPN_PACKET));
|
|
|
|
uc = *((UCHAR *)data);
|
|
data++;
|
|
size--;
|
|
|
|
ret->OpCode = ((uc & 0xF8) >> 3) & 0x1F;
|
|
ret->KeyId = uc & 0x07;
|
|
|
|
if (ret->OpCode == OPENVPN_P_DATA_V1)
|
|
{
|
|
// Data packet
|
|
ret->DataSize = size;
|
|
ret->Data = Clone(data, size);
|
|
return ret;
|
|
}
|
|
|
|
// Sender Channel ID
|
|
if (size < sizeof(UINT64))
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
ret->MySessionId = READ_UINT64(data);
|
|
data += sizeof(UINT64);
|
|
size -= sizeof(UINT64);
|
|
|
|
// ACK
|
|
if (size < 1)
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
uc = *((UCHAR *)data);
|
|
data++;
|
|
size--;
|
|
|
|
ret->NumAck = uc;
|
|
|
|
if (ret->NumAck > 4)
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
if (ret->NumAck >= 1)
|
|
{
|
|
UINT i;
|
|
|
|
if (size < (sizeof(UINT) * (UINT)ret->NumAck + sizeof(UINT64)))
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
for (i = 0; i < ret->NumAck; i++)
|
|
{
|
|
UINT ui;
|
|
|
|
ui = READ_UINT(data);
|
|
|
|
ret->AckPacketId[i] = ui;
|
|
|
|
data += sizeof(UINT);
|
|
size -= sizeof(UINT);
|
|
}
|
|
|
|
ret->YourSessionId = READ_UINT64(data);
|
|
data += sizeof(UINT64);
|
|
size -= sizeof(UINT64);
|
|
}
|
|
|
|
if (ret->OpCode != OPENVPN_P_ACK_V1)
|
|
{
|
|
// Read the Packet ID Because in the case of other than ACK
|
|
if (size < sizeof(UINT))
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
|
|
ret->PacketId = READ_UINT(data);
|
|
data += sizeof(UINT);
|
|
size -= sizeof(UINT);
|
|
|
|
// Payload
|
|
ret->DataSize = size;
|
|
if (size >= 1)
|
|
{
|
|
ret->Data = Clone(data, size);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
|
|
LABEL_ERROR:
|
|
OvsFreePacket(ret);
|
|
return NULL;
|
|
}
|
|
|
|
// Release the OpenVPN packet
|
|
void OvsFreePacket(OPENVPN_PACKET *p)
|
|
{
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p->Data != NULL)
|
|
{
|
|
Free(p->Data);
|
|
}
|
|
|
|
Free(p);
|
|
}
|
|
|
|
// If the session does not exist, create a session
|
|
OPENVPN_SESSION *OvsFindOrCreateSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol)
|
|
{
|
|
OPENVPN_SESSION *se;
|
|
// Validate arguments
|
|
if (s == NULL || server_ip == NULL || server_port == 0 || client_ip == NULL || client_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
se = OvsSearchSession(s, server_ip, server_port, client_ip, client_port, protocol);
|
|
if (se == NULL)
|
|
{
|
|
se = OvsNewSession(s, server_ip, server_port, client_ip, client_port, protocol);
|
|
|
|
if (se != NULL)
|
|
{
|
|
Insert(s->SessionList, se);
|
|
}
|
|
}
|
|
|
|
return se;
|
|
}
|
|
|
|
// Get the number of sessions currently connected from the IP address of the client
|
|
UINT OvsGetNumSessionByClientIp(OPENVPN_SERVER *s, IP *ip)
|
|
{
|
|
UINT i;
|
|
UINT ret = 0;
|
|
// Validate arguments
|
|
if (s == NULL || ip == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < LIST_NUM(s->SessionList); i++)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
|
|
|
|
if (CmpIpAddr(&se->ClientIp, ip) == 0)
|
|
{
|
|
ret++;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Create a new session
|
|
OPENVPN_SESSION *OvsNewSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol)
|
|
{
|
|
OPENVPN_SESSION *se;
|
|
char server_ip_str[MAX_SIZE];
|
|
char client_ip_str[MAX_SIZE];
|
|
// Validate arguments
|
|
if (s == NULL || server_ip == NULL || server_port == 0 || client_ip == NULL || client_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
if (OvsGetNumSessionByClientIp(s, client_ip) > OPENVPN_QUOTA_MAX_NUM_SESSIONS_PER_IP)
|
|
{
|
|
// Number of sessions from the same IP address too many
|
|
return NULL;
|
|
}
|
|
|
|
if (LIST_NUM(s->SessionList) > OPENVPN_QUOTA_MAX_NUM_SESSIONS)
|
|
{
|
|
// Too many OpenVPN sessions
|
|
return NULL;
|
|
}
|
|
|
|
se = ZeroMalloc(sizeof(OPENVPN_SESSION));
|
|
|
|
se->Server = s;
|
|
|
|
Copy(&se->ClientIp, client_ip, sizeof(IP));
|
|
se->ClientPort = client_port;
|
|
|
|
Copy(&se->ServerIp, server_ip, sizeof(IP));
|
|
se->ServerPort = server_port;
|
|
|
|
se->ObfuscationMode = s->Obfuscation ? INFINITE : OPENVPN_SCRAMBLE_MODE_DISABLED;
|
|
|
|
se->LastCommTick = s->Now;
|
|
|
|
se->Protocol = protocol;
|
|
|
|
se->ServerSessionId = OvsNewServerSessionId(se->Server);
|
|
|
|
se->CreatedTick = s->Now;
|
|
|
|
se->Id = s->NextSessionId;
|
|
s->NextSessionId++;
|
|
|
|
IPToStr(server_ip_str, sizeof(server_ip_str), server_ip);
|
|
IPToStr(client_ip_str, sizeof(client_ip_str), client_ip);
|
|
Debug("OpenVPN New Session: %s:%u -> %s:%u Proto=%u\n", server_ip_str, server_port,
|
|
client_ip_str, client_port, protocol);
|
|
|
|
return se;
|
|
}
|
|
|
|
// Release the session
|
|
void OvsFreeSession(OPENVPN_SESSION *se)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (se == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If there is IP addresses which is got from a DHCP server in the session, release it
|
|
if (se->Ipc != NULL)
|
|
{
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
if (se->IpcAsync != NULL)
|
|
{
|
|
IP dhcp_ip;
|
|
|
|
UINTToIP(&dhcp_ip, se->IpcAsync->L3ClientAddressOption.ServerAddress);
|
|
|
|
IPCDhcpFreeIP(se->Ipc, &dhcp_ip);
|
|
IPC_PROTO_SET_STATUS(se->Ipc, IPv6State, IPC_PROTO_STATUS_CLOSED);
|
|
IPCProcessL3EventsIPv4Only(se->Ipc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Release the channel
|
|
for (i = 0; i < OPENVPN_NUM_CHANNELS; i++)
|
|
{
|
|
OPENVPN_CHANNEL *c = se->Channels[i];
|
|
|
|
if (c != NULL)
|
|
{
|
|
OvsFreeChannel(c);
|
|
}
|
|
}
|
|
|
|
// Release the IPC
|
|
if (se->Ipc != NULL)
|
|
{
|
|
FreeIPC(se->Ipc);
|
|
}
|
|
|
|
if (se->IpcAsync != NULL)
|
|
{
|
|
FreeIPCAsync(se->IpcAsync);
|
|
}
|
|
|
|
Free(se);
|
|
}
|
|
|
|
// Search the session from the endpoint information
|
|
OPENVPN_SESSION *OvsSearchSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol)
|
|
{
|
|
OPENVPN_SESSION *se;
|
|
OPENVPN_SESSION t;
|
|
// Validate arguments
|
|
if (s == NULL || server_ip == NULL || server_port == 0 || client_ip == NULL || client_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Copy(&t.ClientIp, client_ip, sizeof(IP));
|
|
t.ClientPort = client_port;
|
|
Copy(&t.ServerIp, server_ip, sizeof(IP));
|
|
t.ServerPort = server_port;
|
|
t.Protocol = protocol;
|
|
|
|
se = Search(s->SessionList, &t);
|
|
|
|
return se;
|
|
}
|
|
|
|
// Receive packets in the OpenVPN server
|
|
void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol)
|
|
{
|
|
UINT i, j;
|
|
LIST *delete_session_list = NULL;
|
|
// Validate arguments
|
|
if (s == NULL || recv_packet_list == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
s->Now = Tick64();
|
|
|
|
// Process for all sessions
|
|
for (i = 0; i < LIST_NUM(s->SessionList); i++)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
|
|
|
|
if (se->Ipc != NULL)
|
|
{
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
// Flush the ARP table of the IPC
|
|
IPCFlushArpTableEx(se->Ipc, s->Now);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process received packets
|
|
for (i = 0; i < LIST_NUM(recv_packet_list); i++)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(recv_packet_list, i);
|
|
|
|
OvsProceccRecvPacket(s, p, protocol);
|
|
}
|
|
|
|
// Treat for all sessions and all channels
|
|
for (i = 0; i < LIST_NUM(s->SessionList); i++)
|
|
{
|
|
OPENVPN_CHANNEL *latest_channel = NULL;
|
|
UINT64 max_tick = 0;
|
|
OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
|
|
bool is_disconnected = false;
|
|
|
|
if (se->Ipc != NULL)
|
|
{
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
IPCProcessL3EventsIPv4Only(se->Ipc);
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < OPENVPN_NUM_CHANNELS; j++)
|
|
{
|
|
OPENVPN_CHANNEL *c = se->Channels[j];
|
|
|
|
if (c != NULL)
|
|
{
|
|
if (c->RekeyInitiated == false && ((c->NextRekey <= s->Now && c->NextRekey != 0) || (c->LastDataPacketId >= OPENVPN_MAX_PACKET_ID_FOR_TRIGGER_REKEY)))
|
|
{
|
|
OPENVPN_CHANNEL *c2;
|
|
// Send a soft reset by creating a new channel
|
|
UINT next_channel_id = se->LastCreatedChannelIndex + 1;
|
|
if (next_channel_id >= OPENVPN_NUM_CHANNELS)
|
|
{
|
|
next_channel_id = 1;
|
|
}
|
|
if (se->Channels[next_channel_id] != NULL)
|
|
{
|
|
// Release when there is a channel data already
|
|
OvsFreeChannel(se->Channels[next_channel_id]);
|
|
se->Channels[next_channel_id] = NULL;
|
|
}
|
|
|
|
// Create a new channel
|
|
c2 = OvsNewChannel(se, (UCHAR)next_channel_id);
|
|
c2->IsInitiatorServer = true;
|
|
se->Channels[next_channel_id] = c2;
|
|
Debug("OpenVPN New Channel for Re-Keying :%u\n", next_channel_id);
|
|
OvsLog(s, se, c, "LO_INITIATE_REKEY");
|
|
|
|
// Send a soft reset
|
|
OvsSendControlPacket(c2, OPENVPN_P_CONTROL_SOFT_RESET_V1, NULL, 0);
|
|
|
|
c->RekeyInitiated = true;
|
|
}
|
|
}
|
|
|
|
if (c != NULL)
|
|
{
|
|
switch (c->Status)
|
|
{
|
|
case OPENVPN_CHANNEL_STATUS_TLS_VPN_CONNECTING:
|
|
// Check whether the connection process completed if there is a channel running a VPN connection process
|
|
if (se->IpcAsync != NULL)
|
|
{
|
|
if (se->IpcAsync->Done)
|
|
{
|
|
if (se->IpcAsync->Ipc != NULL)
|
|
{
|
|
char option_str[4096];
|
|
char l3_options[MAX_SIZE];
|
|
|
|
// Successful in VPN connection
|
|
Debug("OpenVPN Channel %u Established (new key).\n", j);
|
|
OvsLog(s, se, c, "LO_CHANNEL_ESTABLISHED");
|
|
|
|
// Return the PUSH_REPLY
|
|
Format(option_str, sizeof(option_str),
|
|
"PUSH_REPLY,ping %u,ping-restart %u",
|
|
s->PingSendInterval / 1000,
|
|
s->Timeout / 1000);
|
|
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
// Add such as the IP address that was acquired from the DHCP server
|
|
// if the L3 mode to the option character string
|
|
DHCP_OPTION_LIST *cao = &se->IpcAsync->L3ClientAddressOption;
|
|
char ip_client[64];
|
|
char ip_subnet_mask[64];
|
|
char ip_dns1[64];
|
|
char ip_dns2[64];
|
|
char ip_wins1[64];
|
|
char ip_wins2[64];
|
|
char ip_defgw[64];
|
|
|
|
ClearStr(ip_dns1, sizeof(ip_dns1));
|
|
ClearStr(ip_dns2, sizeof(ip_dns2));
|
|
ClearStr(ip_wins1, sizeof(ip_wins1));
|
|
ClearStr(ip_wins2, sizeof(ip_wins2));
|
|
ClearStr(ip_defgw, sizeof(ip_defgw));
|
|
|
|
IPToStr32(ip_client, sizeof(ip_client),
|
|
cao->ClientAddress);
|
|
|
|
IPToStr32(ip_subnet_mask, sizeof(ip_subnet_mask),
|
|
cao->SubnetMask);
|
|
|
|
Format(l3_options, sizeof(l3_options),
|
|
",topology subnet");
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
Format(l3_options, sizeof(l3_options),
|
|
",ifconfig %s %s",
|
|
ip_client,
|
|
ip_subnet_mask);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
// Domain name
|
|
if (IsEmptyStr(cao->DomainName) == false)
|
|
{
|
|
Format(l3_options, sizeof(l3_options),
|
|
",dhcp-option DOMAIN %s", cao->DomainName);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
}
|
|
|
|
// DNS server address 1
|
|
if (cao->DnsServer != 0)
|
|
{
|
|
char ip_str[64];
|
|
IPToStr32(ip_str, sizeof(ip_str), cao->DnsServer);
|
|
Format(l3_options, sizeof(l3_options),
|
|
",dhcp-option DNS %s", ip_str);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
StrCpy(ip_dns1, sizeof(ip_dns1), ip_str);
|
|
}
|
|
|
|
// DNS server address 2
|
|
if (cao->DnsServer2 != 0)
|
|
{
|
|
char ip_str[64];
|
|
IPToStr32(ip_str, sizeof(ip_str), cao->DnsServer2);
|
|
Format(l3_options, sizeof(l3_options),
|
|
",dhcp-option DNS %s", ip_str);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
StrCpy(ip_dns2, sizeof(ip_dns2), ip_str);
|
|
}
|
|
|
|
// WINS address 1
|
|
if (cao->WinsServer != 0)
|
|
{
|
|
char ip_str[64];
|
|
IPToStr32(ip_str, sizeof(ip_str), cao->WinsServer);
|
|
Format(l3_options, sizeof(l3_options),
|
|
",dhcp-option WINS %s", ip_str);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
StrCpy(ip_wins1, sizeof(ip_wins1), ip_str);
|
|
}
|
|
|
|
// WINS address 2
|
|
if (cao->WinsServer2 != 0)
|
|
{
|
|
char ip_str[64];
|
|
IPToStr32(ip_str, sizeof(ip_str), cao->WinsServer2);
|
|
Format(l3_options, sizeof(l3_options),
|
|
",dhcp-option WINS %s", ip_str);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
StrCpy(ip_wins2, sizeof(ip_wins2), ip_str);
|
|
}
|
|
|
|
// Default gateway
|
|
if (cao->Gateway != 0)
|
|
{
|
|
char ip_str[64];
|
|
IPToStr32(ip_str, sizeof(ip_str), cao->Gateway);
|
|
Format(l3_options, sizeof(l3_options),
|
|
",route-gateway %s,redirect-gateway def1", ip_str);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
StrCpy(ip_defgw, sizeof(ip_defgw), ip_str);
|
|
}
|
|
else
|
|
{
|
|
#if 0 // Currently disabled
|
|
// If the default gateway is not specified, add the static routing table
|
|
// entry for the local IP subnet
|
|
IP local_network;
|
|
IP client_ip;
|
|
IP subnet_mask;
|
|
|
|
UINTToIP(&client_ip, cao->ClientAddress);
|
|
UINTToIP(&subnet_mask, cao->SubnetMask);
|
|
|
|
Zero(&local_network, sizeof(IP));
|
|
IPAnd4(&local_network, &client_ip, &subnet_mask);
|
|
|
|
Format(l3_options, sizeof(l3_options),
|
|
",route %r %r vpn_gateway",
|
|
&local_network,
|
|
&cao->SubnetMask);
|
|
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
#endif
|
|
}
|
|
|
|
// Classless routing table
|
|
if (cao->ClasslessRoute.NumExistingRoutes >= 1)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < MAX_DHCP_CLASSLESS_ROUTE_ENTRIES; i++)
|
|
{
|
|
DHCP_CLASSLESS_ROUTE *r = &cao->ClasslessRoute.Entries[i];
|
|
|
|
if (r->Exists)
|
|
{
|
|
Format(l3_options, sizeof(l3_options),
|
|
",route %r %r vpn_gateway",
|
|
&r->Network, &r->SubnetMask);
|
|
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
}
|
|
}
|
|
}
|
|
|
|
OvsLog(s, se, c, "LP_SET_IPV4_PARAM",
|
|
ip_client, ip_subnet_mask, ip_defgw, ip_dns1, ip_dns2, ip_wins1, ip_wins2);
|
|
}
|
|
else
|
|
{
|
|
// OpenVPN L2 mode. To fix the bug of OpenVPN 2.4.6 and particular version of kernel mode TAP driver
|
|
// on Linux, the TAP device must be up after the OpenVPN client is connected.
|
|
// However there is no direct push instruction to do so to OpenVPN client.
|
|
// Therefore we push the dummy IPv4 address (RFC7600) to the OpenVPN client.
|
|
if (s->PushDummyIPv4AddressOnL2Mode)
|
|
{
|
|
StrCat(option_str, sizeof(option_str), ",ifconfig 192.0.0.8 255.255.255.240");
|
|
}
|
|
}
|
|
|
|
// From https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage:
|
|
//
|
|
// --block-outside-dns
|
|
// Block DNS servers on other network adapters to prevent DNS leaks.
|
|
// This option prevents any application from accessing TCP or UDP port 53 except one inside the tunnel.
|
|
// It uses Windows Filtering Platform (WFP) and works on Windows Vista or later.
|
|
// This option is considered unknown on non-Windows platforms and unsupported on Windows XP, resulting in fatal error.
|
|
// You may want to use --setenv opt or --ignore-unknown-option (not suitable for Windows XP) to ignore said error.
|
|
// Note that pushing unknown options from server does not trigger fatal errors.
|
|
StrCat(option_str, sizeof(option_str), ",block-outside-dns");
|
|
|
|
WriteFifo(c->SslPipe->SslInOut->SendFifo, option_str, StrSize(option_str));
|
|
|
|
Debug("Push Str: %s\n", option_str);
|
|
OvsLog(s, se, c, "LO_PUSH_REPLY", option_str);
|
|
|
|
StrCpy(se->PushReplyStr, sizeof(se->PushReplyStr), option_str);
|
|
|
|
se->Ipc = se->IpcAsync->Ipc;
|
|
se->IpcAsync->Ipc = NULL;
|
|
|
|
s->SessionEstablishedCount++;
|
|
|
|
// Set a Sock Event of IPC to Sock Event of the UDP Listener
|
|
IPCSetSockEventWhenRecvL2Packet(se->Ipc, s->SockEvent);
|
|
|
|
// State transition
|
|
c->Status = OPENVPN_CHANNEL_STATUS_ESTABLISHED;
|
|
c->EstablishedTick = s->Now;
|
|
se->Established = true;
|
|
se->LastCommTick = Tick64();
|
|
}
|
|
else
|
|
{
|
|
char *str;
|
|
|
|
if (se->IpcAsync->DhcpAllocFailed)
|
|
{
|
|
OvsLog(s, se, c, "LP_DHCP_REQUEST_NG");
|
|
}
|
|
|
|
// Failed to connect VPN
|
|
Debug("OpenVPN Channel %u Failed.\n", j);
|
|
OvsLog(s, se, c, "LO_CHANNEL_FAILED");
|
|
|
|
// Return the AUTH_FAILED
|
|
str = "AUTH_FAILED";
|
|
WriteFifo(c->SslPipe->SslInOut->SendFifo, str, StrSize(str));
|
|
|
|
s->SessionEstablishedCount++;
|
|
|
|
// State transition
|
|
c->Status = OPENVPN_CHANNEL_STATUS_DISCONNECTED;
|
|
|
|
FreeIPCAsync(se->IpcAsync);
|
|
se->IpcAsync = NULL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPENVPN_CHANNEL_STATUS_ESTABLISHED:
|
|
// Monitor the IPC whether not disconnected when there is a VPN connection completed channel
|
|
if (IsIPCConnected(se->Ipc) == false)
|
|
{
|
|
// Send the RESTART since IPC is disconnected
|
|
char *str = "RESTART";
|
|
Debug("OpenVPN Channel %u Disconnected by HUB.\n", j);
|
|
|
|
OvsLog(s, se, c, "LO_CHANNEL_DISCONNECTED_BY_HUB");
|
|
|
|
WriteFifo(c->SslPipe->SslInOut->SendFifo, str, StrSize(str));
|
|
|
|
// State transition
|
|
c->Status = OPENVPN_CHANNEL_STATUS_DISCONNECTED;
|
|
|
|
// Set the session to disconnected state
|
|
se->Established = false;
|
|
se->LastCommTick = s->Now;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (c != NULL)
|
|
{
|
|
// If there is a packet to be transmitted physically in SSL, send it
|
|
if (c->SslPipe != NULL && SyncSslPipe(c->SslPipe))
|
|
{
|
|
if (FifoSize(c->SslPipe->RawOut->RecvFifo) >= 1)
|
|
{
|
|
Debug("RawOut Fifo Size (c=%u): %u\n", c->KeyId, FifoSize(c->SslPipe->RawOut->RecvFifo));
|
|
|
|
OvsSendControlPacketWithAutoSplit(c, OPENVPN_P_CONTROL_V1,
|
|
FifoPtr(c->SslPipe->RawOut->RecvFifo),
|
|
FifoSize(c->SslPipe->RawOut->RecvFifo));
|
|
|
|
ReadFifo(c->SslPipe->RawOut->RecvFifo, NULL, FifoSize(c->SslPipe->RawOut->RecvFifo));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c != NULL)
|
|
{
|
|
UINT num;
|
|
UINT acks[OPENVPN_MAX_NUMACK];
|
|
UINT k;
|
|
|
|
// Packet transmission
|
|
for (k = 0; k < LIST_NUM(c->SendControlPacketList); k++)
|
|
{
|
|
OPENVPN_CONTROL_PACKET *cp = LIST_DATA(c->SendControlPacketList, k);
|
|
|
|
if (cp->NextSendTime <= s->Now)
|
|
{
|
|
if (cp->NoResend == false || cp->NumSent == 0) // To address the UDP reflection amplification attack: https://github.com/SoftEtherVPN/SoftEtherVPN/issues/1001
|
|
{
|
|
OPENVPN_PACKET *p;
|
|
|
|
cp->NumSent++;
|
|
|
|
num = OvsGetAckReplyList(c, acks);
|
|
|
|
p = OvsNewControlPacket(cp->OpCode, j, se->ServerSessionId, num, acks,
|
|
se->ClientSessionId, cp->PacketId, cp->DataSize, cp->Data);
|
|
|
|
OvsSendPacketNow(s, se, p);
|
|
|
|
OvsFreePacket(p);
|
|
|
|
cp->NextSendTime = s->Now + (UINT64)OPENVPN_CONTROL_PACKET_RESEND_INTERVAL;
|
|
|
|
AddInterrupt(s->Interrupt, cp->NextSendTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the response with an ACK-only packet is required, respond such that
|
|
num = OvsGetAckReplyList(c, acks);
|
|
|
|
if (num >= 1)
|
|
{
|
|
OPENVPN_PACKET *p = OvsNewControlPacket(OPENVPN_P_ACK_V1, j, se->ServerSessionId,
|
|
num, acks, se->ClientSessionId, 0, 0, NULL);
|
|
|
|
OvsSendPacketNow(s, se, p);
|
|
|
|
OvsFreePacket(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (se->Ipc != NULL)
|
|
{
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
if (se->IpcAsync != NULL)
|
|
{
|
|
// Update DHCP address
|
|
if (se->IpcAsync->L3NextDhcpRenewTick <= s->Now)
|
|
{
|
|
IP ip;
|
|
|
|
se->IpcAsync->L3NextDhcpRenewTick = s->Now + se->IpcAsync->L3DhcpRenewInterval;
|
|
|
|
UINTToIP(&ip, se->IpcAsync->L3ClientAddressOption.ServerAddress);
|
|
|
|
IPCDhcpRenewIP(se->Ipc, &ip);
|
|
}
|
|
}
|
|
|
|
IPCProcessL3EventsIPv4Only(se->Ipc);
|
|
}
|
|
|
|
IPCProcessInterrupts(se->Ipc);
|
|
}
|
|
|
|
// Choose the latest channel in all established channels
|
|
for (j = 0; j < OPENVPN_NUM_CHANNELS; j++)
|
|
{
|
|
OPENVPN_CHANNEL *c = se->Channels[j];
|
|
|
|
if (c != NULL)
|
|
{
|
|
if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
|
|
{
|
|
if (max_tick <= c->EstablishedTick)
|
|
{
|
|
max_tick = c->EstablishedTick;
|
|
latest_channel = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (se->Established == false)
|
|
{
|
|
latest_channel = NULL;
|
|
}
|
|
|
|
// Send the data using the latest channel (when there is no transmission channel, suck out the queue simply)
|
|
if (se->Mode == OPENVPN_MODE_L2)
|
|
{
|
|
// Get an Ethernet frame from IPC
|
|
while (true)
|
|
{
|
|
BLOCK *b = IPCRecvL2(se->Ipc);
|
|
if (b == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (latest_channel != NULL && s->SupressSendPacket == false)
|
|
{
|
|
OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId, b->Buf, b->Size);
|
|
}
|
|
|
|
FreeBlock(b);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get an IPv4 packet from IPC
|
|
while (true)
|
|
{
|
|
BLOCK *b = IPCRecvIPv4(se->Ipc);
|
|
if (b == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (latest_channel != NULL && s->SupressSendPacket == false)
|
|
{
|
|
OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId, b->Buf, b->Size);
|
|
}
|
|
|
|
FreeBlock(b);
|
|
}
|
|
}
|
|
|
|
// Send a Ping
|
|
if (latest_channel != NULL)
|
|
{
|
|
if ((se->NextPingSendTick == 0) || (se->NextPingSendTick <= s->Now))
|
|
{
|
|
se->NextPingSendTick = s->Now + s->PingSendInterval;
|
|
|
|
OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId,
|
|
ping_signature, sizeof(ping_signature));
|
|
|
|
AddInterrupt(s->Interrupt, se->NextPingSendTick);
|
|
}
|
|
}
|
|
|
|
if ((se->Established == false) && (s->Now >= (se->CreatedTick + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT)))
|
|
{
|
|
is_disconnected = true;
|
|
}
|
|
|
|
if (se->Established && (s->Now >= (se->LastCommTick + s->Timeout)))
|
|
{
|
|
is_disconnected = true;
|
|
}
|
|
|
|
if (is_disconnected)
|
|
{
|
|
if (delete_session_list == NULL)
|
|
{
|
|
delete_session_list = NewListFast(NULL);
|
|
}
|
|
|
|
Add(delete_session_list, se);
|
|
}
|
|
}
|
|
|
|
if (delete_session_list != NULL)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0; i < LIST_NUM(delete_session_list); i++)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(delete_session_list, i);
|
|
|
|
Debug("Deleting Session %p\n", se);
|
|
|
|
OvsFreeSession(se);
|
|
|
|
s->DisconnectCount++;
|
|
|
|
Delete(s->SessionList, se);
|
|
}
|
|
|
|
ReleaseList(delete_session_list);
|
|
}
|
|
}
|
|
|
|
// Send the packet now
|
|
void OvsSendPacketNow(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_PACKET *p)
|
|
{
|
|
BUF *b;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (s == NULL || se == NULL || p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Debug("Sending Opcode=%u ", p->OpCode);
|
|
if (p->NumAck >= 1)
|
|
{
|
|
Debug("Sending ACK Packet IDs (c=%u): ", p->KeyId);
|
|
for (i = 0; i < p->NumAck; i++)
|
|
{
|
|
Debug("%u ", p->AckPacketId[i]);
|
|
}
|
|
}
|
|
Debug("\n");
|
|
|
|
b = OvsBuildPacket(p);
|
|
|
|
OvsSendPacketRawNow(s, se, b->Buf, b->Size);
|
|
|
|
Free(b);
|
|
}
|
|
void OvsSendPacketRawNow(OPENVPN_SERVER *s, OPENVPN_SESSION *se, void *data, UINT size)
|
|
{
|
|
UDPPACKET *u;
|
|
|
|
// Validate arguments
|
|
if (s == NULL || se == NULL || data == NULL || size == 0)
|
|
{
|
|
Free(data);
|
|
return;
|
|
}
|
|
|
|
// Scramble the packet
|
|
switch (se->ObfuscationMode)
|
|
{
|
|
case OPENVPN_SCRAMBLE_MODE_DISABLED:
|
|
break;
|
|
case OPENVPN_SCRAMBLE_MODE_XORMASK:
|
|
OvsDataXorMask(data, size, s->ObfuscationMask, StrLen(s->ObfuscationMask));
|
|
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->ObfuscationMask, StrLen(s->ObfuscationMask));
|
|
}
|
|
|
|
u = NewUdpPacket(&se->ServerIp, se->ServerPort, &se->ClientIp, se->ClientPort,
|
|
data, size);
|
|
|
|
Add(s->SendPacketList, u);
|
|
}
|
|
// Create a new OpenVPN control packet
|
|
OPENVPN_PACKET *OvsNewControlPacket(UCHAR opcode, UCHAR key_id, UINT64 my_channel_id, UINT num_ack,
|
|
UINT *ack_packet_ids, UINT64 your_channel_id, UINT packet_id,
|
|
UINT data_size, UCHAR *data)
|
|
{
|
|
OPENVPN_PACKET *p = ZeroMalloc(sizeof(OPENVPN_PACKET));
|
|
UINT i;
|
|
|
|
p->OpCode = opcode;
|
|
p->KeyId = key_id;
|
|
p->MySessionId = my_channel_id;
|
|
p->NumAck = num_ack;
|
|
|
|
for (i = 0; i < MIN(num_ack, OPENVPN_MAX_NUMACK); i++)
|
|
{
|
|
p->AckPacketId[i] = ack_packet_ids[i];
|
|
}
|
|
|
|
p->YourSessionId = your_channel_id;
|
|
p->PacketId = packet_id;
|
|
|
|
if (data_size != 0 && data != NULL)
|
|
{
|
|
p->Data = Clone(data, data_size);
|
|
p->DataSize = data_size;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
// Comparison function of the entries in the session list
|
|
int OvsCompareSessionList(void *p1, void *p2)
|
|
{
|
|
OPENVPN_SESSION *s1, *s2;
|
|
int i;
|
|
// Validate arguments
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
s1 = *(OPENVPN_SESSION **)p1;
|
|
s2 = *(OPENVPN_SESSION **)p2;
|
|
if (s1 == NULL || s2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
i = Cmp(&s1->Protocol, &s2->Protocol, sizeof(s1->Protocol));
|
|
if (i != 0)
|
|
{
|
|
return i;
|
|
}
|
|
|
|
i = CmpIpAddr(&s1->ClientIp, &s2->ClientIp);
|
|
if (i != 0)
|
|
{
|
|
return i;
|
|
}
|
|
|
|
i = COMPARE_RET(s1->ClientPort, s2->ClientPort);
|
|
if (i != 0)
|
|
{
|
|
return i;
|
|
}
|
|
|
|
i = CmpIpAddr(&s1->ServerIp, &s2->ServerIp);
|
|
if (i != 0)
|
|
{
|
|
return i;
|
|
}
|
|
|
|
i = COMPARE_RET(s1->ServerPort, s2->ServerPort);
|
|
if (i != 0)
|
|
{
|
|
return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Create a new OpenVPN server
|
|
OPENVPN_SERVER *NewOpenVpnServer(const LIST *options, CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOCK_EVENT *sock_event)
|
|
{
|
|
UINT i;
|
|
OPENVPN_SERVER *s;
|
|
|
|
if (options == NULL || cedar == NULL || interrupt == NULL || sock_event == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
s = ZeroMalloc(sizeof(OPENVPN_SERVER));
|
|
|
|
for (i = 0; i < LIST_NUM(options); ++i)
|
|
{
|
|
const PROTO_OPTION *option = LIST_DATA(options, i);
|
|
if (StrCmp(option->Name, "DefaultClientOption") == 0)
|
|
{
|
|
s->DefaultClientOption = CopyStr(option->String);
|
|
}
|
|
else if (StrCmp(option->Name, "Obfuscation") == 0)
|
|
{
|
|
s->Obfuscation = option->Bool;
|
|
}
|
|
else if (StrCmp(option->Name, "ObfuscationMask") == 0)
|
|
{
|
|
s->ObfuscationMask = CopyStr(option->String);
|
|
}
|
|
else if (StrCmp(option->Name, "PingSendInterval") == 0)
|
|
{
|
|
s->PingSendInterval = option->UInt32;
|
|
}
|
|
else if (StrCmp(option->Name, "PushDummyIPv4AddressOnL2Mode") == 0)
|
|
{
|
|
s->PushDummyIPv4AddressOnL2Mode = option->Bool;
|
|
}
|
|
else if (StrCmp(option->Name, "Timeout") == 0)
|
|
{
|
|
s->Timeout = option->UInt32;
|
|
}
|
|
}
|
|
|
|
s->Cedar = cedar;
|
|
s->Interrupt = interrupt;
|
|
s->SockEvent = sock_event;
|
|
|
|
s->SessionList = NewList(OvsCompareSessionList);
|
|
s->RecvPacketList = NewListFast(NULL);
|
|
s->SendPacketList = NewListFast(NULL);
|
|
|
|
s->Now = Tick64();
|
|
s->Giveup = s->Now + OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT;
|
|
|
|
s->NextSessionId = 1;
|
|
|
|
s->Dh = DhNewFromBits(cedar->DhParamBits);
|
|
|
|
return s;
|
|
}
|
|
|
|
// Release the OpenVPN server
|
|
void FreeOpenVpnServer(OPENVPN_SERVER *s)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (s == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Release the sessions list
|
|
for (i = 0; i < LIST_NUM(s->SessionList); ++i)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
|
|
OvsFreeSession(se);
|
|
}
|
|
|
|
ReleaseList(s->SessionList);
|
|
|
|
// Release the incoming packets list
|
|
for (i = 0; i < LIST_NUM(s->RecvPacketList); ++i)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(s->RecvPacketList, i);
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
ReleaseList(s->RecvPacketList);
|
|
|
|
// Release the outgoing packets list
|
|
for (i = 0; i < LIST_NUM(s->SendPacketList); ++i)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(s->SendPacketList, i);
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
ReleaseList(s->SendPacketList);
|
|
|
|
DhFree(s->Dh);
|
|
|
|
Free(s->DefaultClientOption);
|
|
Free(s->ObfuscationMask);
|
|
|
|
Free(s);
|
|
}
|