1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-25 10:59:53 +03:00
SoftEtherVPN/src/Cedar/Proto_OpenVPN.c
Davide Beatrici 2aaf9012a0 Cedar/Proto_OpenVPN: Make timeout and ping transmission interval configurable
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.
2021-04-21 08:29:30 +02:00

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);
}