mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-07 18:20:41 +03:00
7de986dcca
Buffer overread in ParseL2TPPacket() Memory corruption in IcmpParseResult Missing bounds check in ParseUDP() can lead to invalid memory access Out-of-bounds read in IPsec_PPP.c (unterminated string buffer) Overlapping parameters to memcpy() via StrToIp6() PACK ReadValue() crash vulnerability Potential use of uninitialized memory via IPToInAddr6() 4 memory leaks. While the amount of leakage is very small per time, these bugs can finally cause process crash by out of memory. So these bugs must be fixed. Memory leak in NnReadDnsRecord Memory leak in RadiusLogin() Memory leak via ParsePacketIPv4WithDummyMacHeader Remote memory leak in OpenVPN server code 1 coding improvement. This is not a bug, however, I fixed the code to avoid furture misunderstanding. RecvAll can return success on failure (leading to use of uninitialized memory) Contributors for this bugfix: - Max Planck Institute for Molecular Genetics - Guido Vranken
3024 lines
68 KiB
C
3024 lines
68 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Cedar Communication Module
|
|
//
|
|
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
|
|
//
|
|
// Copyright (c) Daiyuu Nobori.
|
|
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
|
|
// Copyright (c) SoftEther Corporation.
|
|
//
|
|
// All Rights Reserved.
|
|
//
|
|
// http://www.softether.org/
|
|
//
|
|
// Author: Daiyuu Nobori, Ph.D.
|
|
// Comments: Tetsuo Sugiyama, Ph.D.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// version 2 as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License version 2
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
|
|
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
|
|
//
|
|
//
|
|
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
|
|
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
|
|
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
|
|
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
|
|
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
|
|
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
|
|
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
|
|
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
|
|
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
|
|
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
|
|
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
|
|
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
|
|
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
|
|
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
|
|
//
|
|
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
|
|
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
|
|
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
|
|
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
|
|
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
|
|
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
|
|
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
|
|
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
|
|
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
|
|
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
|
|
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
|
|
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
|
|
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
|
|
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
|
|
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
|
|
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
|
|
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
|
|
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
|
|
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
|
|
//
|
|
//
|
|
// SOURCE CODE CONTRIBUTION
|
|
// ------------------------
|
|
//
|
|
// Your contribution to SoftEther VPN Project is much appreciated.
|
|
// Please send patches to us through GitHub.
|
|
// Read the SoftEther VPN Patch Acceptance Policy in advance:
|
|
// http://www.softether.org/5-download/src/9.patch
|
|
//
|
|
//
|
|
// DEAR SECURITY EXPERTS
|
|
// ---------------------
|
|
//
|
|
// If you find a bug or a security vulnerability please kindly inform us
|
|
// about the problem immediately so that we can fix the security problem
|
|
// to protect a lot of users around the world as soon as possible.
|
|
//
|
|
// Our e-mail address for security reports is:
|
|
// softether-vpn-security [at] softether.org
|
|
//
|
|
// Please note that the above e-mail address is not a technical support
|
|
// inquiry address. If you need technical assistance, please visit
|
|
// http://www.softether.org/ and ask your question on the users forum.
|
|
//
|
|
// Thank you for your cooperation.
|
|
//
|
|
//
|
|
// NO MEMORY OR RESOURCE LEAKS
|
|
// ---------------------------
|
|
//
|
|
// The memory-leaks and resource-leaks verification under the stress
|
|
// test has been passed before release this source code.
|
|
|
|
|
|
// Interop_OpenVPN.c
|
|
// OpenVPN protocol stack
|
|
|
|
#include "CedarPch.h"
|
|
|
|
|
|
static bool g_no_openvpn_tcp = false;
|
|
static bool g_no_openvpn_udp = false;
|
|
|
|
// 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
|
|
};
|
|
|
|
// Set the OpenVPN over TCP disabling flag
|
|
void OvsSetNoOpenVpnTcp(bool b)
|
|
{
|
|
g_no_openvpn_tcp = b;
|
|
}
|
|
|
|
// Get the OpenVPN over TCP disabling flag
|
|
bool OvsGetNoOpenVpnTcp()
|
|
{
|
|
return g_no_openvpn_tcp;
|
|
}
|
|
|
|
// Set the OpenVPN over UDP disabling flag
|
|
void OvsSetNoOpenVpnUdp(bool b)
|
|
{
|
|
g_no_openvpn_udp = b;
|
|
}
|
|
|
|
// Get the OpenVPN over UDP disabling flag
|
|
bool OvsGetNoOpenVpnUdp()
|
|
{
|
|
return g_no_openvpn_udp;
|
|
}
|
|
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Process the received packet
|
|
void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// Parse the packet
|
|
recv_packet = OvsParsePacket(p->Data, p->Size);
|
|
|
|
if (recv_packet != NULL)
|
|
{
|
|
OPENVPN_CHANNEL *c = NULL;
|
|
if (recv_packet->OpCode != OPENVPN_P_DATA_V1 && recv_packet->MySessionId != 0)
|
|
{
|
|
Debug("RECV PACKET: %u %I64u\n", recv_packet->KeyId, recv_packet->MySessionId);
|
|
}
|
|
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
|
|
{
|
|
Debug(" PKT %u %u\n", recv_packet->OpCode, recv_packet->KeyId);
|
|
}
|
|
|
|
if (recv_packet->OpCode != OPENVPN_P_DATA_V1)
|
|
{
|
|
// Control packet
|
|
if (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2 ||
|
|
recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
|
|
{
|
|
// Connection request packet
|
|
if (se->Channels[recv_packet->KeyId] != NULL)
|
|
{
|
|
// Release when there is a channel data already
|
|
OvsFreeChannel(se->Channels[recv_packet->KeyId]);
|
|
se->Channels[recv_packet->KeyId] = NULL;
|
|
}
|
|
|
|
// Create a new channel
|
|
c = OvsNewChannel(se, recv_packet->KeyId);
|
|
if (se->ClientSessionId == 0)
|
|
{
|
|
se->ClientSessionId = recv_packet->MySessionId;
|
|
}
|
|
se->Channels[recv_packet->KeyId] = c;
|
|
Debug("OpenVPN New Channel :%u\n", recv_packet->KeyId);
|
|
OvsLog(s, se, c, "LO_NEW_CHANNEL");
|
|
}
|
|
/* else if (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1)
|
|
{
|
|
// Response to soft reset request packet
|
|
OPENVPN_PACKET *p;
|
|
|
|
p = OvsNewControlPacket(OPENVPN_P_CONTROL_SOFT_RESET_V1, recv_packet->KeyId, se->ServerSessionId,
|
|
0, NULL, 0, 0, 0, NULL);
|
|
|
|
OvsSendPacketNow(s, se, p);
|
|
|
|
OvsFreePacket(p);
|
|
}*/
|
|
else
|
|
{
|
|
// Packet other than the connection request
|
|
if (se->Channels[recv_packet->KeyId] != NULL)
|
|
{
|
|
c = se->Channels[recv_packet->KeyId];
|
|
}
|
|
}
|
|
|
|
if (c != NULL)
|
|
{
|
|
// Delete the send packet list by looking the packet ID in the ACK list of arrived packet
|
|
OvsDeleteFromSendingControlPacketList(c, recv_packet->NumAck, recv_packet->AckPacketId);
|
|
|
|
if (recv_packet->OpCode != OPENVPN_P_ACK_V1)
|
|
{
|
|
// Add the Packet ID of arrived packet to the list
|
|
InsertIntDistinct(c->AckReplyList, recv_packet->PacketId);
|
|
Debug("Recv Packet ID (c=%u): %u\n", c->KeyId, recv_packet->PacketId);
|
|
|
|
if ((recv_packet->PacketId > c->MaxRecvPacketId)
|
|
|| (recv_packet->OpCode == OPENVPN_P_CONTROL_HARD_RESET_CLIENT_V2)
|
|
|| (recv_packet->OpCode == OPENVPN_P_CONTROL_SOFT_RESET_V1))
|
|
{
|
|
c->MaxRecvPacketId = recv_packet->PacketId;
|
|
|
|
// Process the received control packet
|
|
OvsProcessRecvControlPacket(s, se, c, recv_packet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Data packet
|
|
if (se->Channels[recv_packet->KeyId] != NULL)
|
|
{
|
|
OPENVPN_CHANNEL *c = se->Channels[recv_packet->KeyId];
|
|
if (c->Status == OPENVPN_CHANNEL_STATUS_ESTABLISHED)
|
|
{
|
|
UCHAR *data;
|
|
UINT size;
|
|
|
|
data = recv_packet->Data;
|
|
size = recv_packet->DataSize;
|
|
|
|
if (size >= (c->MdRecv->Size + c->CipherDecrypt->IvSize + sizeof(UINT)))
|
|
{
|
|
UCHAR *hmac;
|
|
UCHAR *iv;
|
|
UCHAR hmac_test[128];
|
|
|
|
// HMAC
|
|
hmac = data;
|
|
data += c->MdRecv->Size;
|
|
size -= c->MdRecv->Size;
|
|
|
|
// Confirmation of HMAC
|
|
MdProcess(c->MdRecv, hmac_test, data, size);
|
|
if (Cmp(hmac_test, hmac, c->MdRecv->Size) == 0)
|
|
{
|
|
// Update of last communication time
|
|
se->LastCommTick = s->Now;
|
|
|
|
// IV
|
|
iv = data;
|
|
data += c->CipherDecrypt->IvSize;
|
|
size -= c->CipherDecrypt->IvSize;
|
|
|
|
// Payload
|
|
if (size >= 1 && (c->CipherDecrypt->BlockSize == 0 || (size % c->CipherDecrypt->BlockSize) == 0))
|
|
{
|
|
UINT data_packet_id;
|
|
|
|
// Decryption
|
|
size = CipherProcess(c->CipherDecrypt, iv, s->TmpBuf, data, size);
|
|
|
|
data = s->TmpBuf;
|
|
|
|
if (size >= sizeof(UINT))
|
|
{
|
|
data_packet_id = READ_UINT(data);
|
|
|
|
data += sizeof(UINT);
|
|
size -= sizeof(UINT);
|
|
|
|
if (size >= sizeof(ping_signature) &&
|
|
Cmp(data, ping_signature, sizeof(ping_signature)) == 0)
|
|
{
|
|
// Ignore since a ping packet has been received
|
|
DoNothing();
|
|
}
|
|
else
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Debug("HMAC Failed (c=%u)\n", c->KeyId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
{
|
|
c->SslPipe = NewSslPipe(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh);
|
|
}
|
|
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)
|
|
OvsSendControlPacket(c, OPENVPN_P_CONTROL_HARD_RESET_SERVER_V2, NULL, 0);
|
|
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
if (se->Mode == OPENVPN_MODE_L3)
|
|
{
|
|
// L3 Mode
|
|
p.IsL3Mode = true;
|
|
}
|
|
else
|
|
{
|
|
// L2 Mode
|
|
p.BridgeMode = true;
|
|
}
|
|
|
|
p.IsOpenVPN = true;
|
|
|
|
// 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];
|
|
// 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);
|
|
|
|
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->Cedar->OpenVPNDefaultClientOption);
|
|
}
|
|
|
|
o = OvsParseOptions(opt_str);
|
|
|
|
if (se->Mode == OPENVPN_MODE_UNKNOWN)
|
|
{
|
|
UINT mtu;
|
|
// Layer
|
|
if (StrCmpi(IniStrValue(o, "dev-type"), "tun") == 0)
|
|
{
|
|
// L3
|
|
se->Mode = OPENVPN_MODE_L3;
|
|
}
|
|
else
|
|
{
|
|
// L2
|
|
se->Mode = OPENVPN_MODE_L2;
|
|
}
|
|
|
|
// Link MTU
|
|
mtu = IniIntValue(o, "link-mtu");
|
|
if (mtu == 0)
|
|
{
|
|
mtu = OPENVPN_MTU_LINK;
|
|
}
|
|
se->LinkMtu = mtu;
|
|
|
|
// Tun MTU
|
|
mtu = IniIntValue(o, "tun-mtu");
|
|
if (mtu == 0)
|
|
{
|
|
mtu = OPENVPN_MTU_TUN;
|
|
}
|
|
se->TunMtu = mtu;
|
|
}
|
|
|
|
// Protocol
|
|
if (se->Protocol == OPENVPN_PROTOCOL_TCP)
|
|
{
|
|
// TCP
|
|
// UDP
|
|
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
|
|
c->CipherEncrypt = OvsGetCipher(IniStrValue(o, "cipher"));
|
|
c->CipherDecrypt = NewCipher(c->CipherEncrypt->Name);
|
|
|
|
// Hash algorithm
|
|
c->MdSend = OvsGetMd(IniStrValue(o, "auth"));
|
|
c->MdRecv = NewMd(c->MdSend->Name);
|
|
|
|
// 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 the key
|
|
SetCipherKey(c->CipherDecrypt, c->ExpansionKey + 0, false);
|
|
SetCipherKey(c->CipherEncrypt, c->ExpansionKey + 128, true);
|
|
SetMdKey(c->MdRecv, c->ExpansionKey + 64, c->MdRecv->Size);
|
|
SetMdKey(c->MdSend, c->ExpansionKey + 192, c->MdSend->Size);
|
|
|
|
OvsFreeOptions(o);
|
|
|
|
// 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,
|
|
c->CipherEncrypt->Name, c->MdSend->Name, c->CipherEncrypt->KeySize * 8);
|
|
Debug("Building OptionStr: %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;
|
|
|
|
if (IsEmptyStr(name) == false && IsStrInStrTokenList(OPENVPN_CIPHER_LIST, name, NULL, false))
|
|
{
|
|
c = NewCipher(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 && IsStrInStrTokenList(OPENVPN_MD_LIST, name, NULL, false))
|
|
{
|
|
m = NewMd(name);
|
|
}
|
|
|
|
if (m == NULL)
|
|
{
|
|
m = NewMd(OPENVPN_DEFAULT_MD);
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
// Parse the option string
|
|
LIST *OvsParseOptions(char *str)
|
|
{
|
|
LIST *o = NewListFast(NULL);
|
|
TOKEN_LIST *t;
|
|
|
|
t = ParseTokenWithoutNullStr(str, ",");
|
|
if (t != NULL)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0;i < t->NumTokens;i++)
|
|
{
|
|
char key[MAX_SIZE];
|
|
char value[MAX_SIZE];
|
|
char *line = t->Token[i];
|
|
Trim(line);
|
|
|
|
if (GetKeyAndValue(line, key, sizeof(key), value, sizeof(value), " \t"))
|
|
{
|
|
INI_ENTRY *e = ZeroMalloc(sizeof(INI_ENTRY));
|
|
|
|
e->Key = CopyStr(key);
|
|
e->Value = CopyStr(value);
|
|
|
|
Add(o, e);
|
|
}
|
|
}
|
|
|
|
FreeToken(t);
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
// Release the option list
|
|
void OvsFreeOptions(LIST *o)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreeIni(o);
|
|
}
|
|
|
|
// Create an Option List
|
|
LIST *OvsNewOptions()
|
|
{
|
|
return NewListFast(NULL);
|
|
}
|
|
|
|
// Add a value to the option list
|
|
void OvsAddOption(LIST *o, char *key, char *value)
|
|
{
|
|
INI_ENTRY *e;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
e = GetIniEntry(o, key);
|
|
if (e != NULL)
|
|
{
|
|
// Overwrite existing keys
|
|
Free(e->Key);
|
|
e->Key = CopyStr(key);
|
|
|
|
Free(e->Value);
|
|
e->Value = CopyStr(value);
|
|
}
|
|
else
|
|
{
|
|
// Create a new key
|
|
e = ZeroMalloc(sizeof(INI_ENTRY));
|
|
|
|
e->Key = CopyStr(key);
|
|
e->Value = CopyStr(value);
|
|
|
|
Add(o, e);
|
|
}
|
|
}
|
|
|
|
// Confirm whether there is specified option key string
|
|
bool OvsHasOption(LIST *o, char *key)
|
|
{
|
|
// Validate arguments
|
|
if (o == NULL || key == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (GetIniEntry(o, key) != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// 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)) &&
|
|
OvsReadStringFromBuf(b, 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)
|
|
{
|
|
OPENVPN_CONTROL_PACKET *p;
|
|
// Validate arguments
|
|
if (c == NULL || (data_size != 0 && data == NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
p = ZeroMalloc(sizeof(OPENVPN_CONTROL_PACKET));
|
|
|
|
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);
|
|
|
|
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->NextIv, sizeof(c->NextIv));
|
|
|
|
//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)
|
|
{
|
|
UCHAR uc;
|
|
UCHAR *encrypted_data;
|
|
UINT encrypted_size;
|
|
UCHAR *dest_data;
|
|
UINT dest_size;
|
|
UINT r;
|
|
|
|
// Validate arguments
|
|
if (c == NULL || data == NULL || data_size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uc = ((OPENVPN_P_DATA_V1 << 3) & 0xF8) | (key_id & 0x07);
|
|
|
|
// Generate the data to be encrypted
|
|
|
|
encrypted_size = sizeof(UINT) + data_size;
|
|
encrypted_data = ZeroMalloc(encrypted_size);
|
|
|
|
WRITE_UINT(encrypted_data, data_packet_id);
|
|
Copy(encrypted_data + sizeof(UINT), data, data_size);
|
|
|
|
// Prepare a buffer to store the results
|
|
dest_data = Malloc(sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + encrypted_size + 256);
|
|
|
|
// Encrypt
|
|
r = CipherProcess(c->CipherEncrypt, c->NextIv, dest_data + sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize,
|
|
encrypted_data, encrypted_size);
|
|
dest_size = sizeof(UCHAR) + c->MdSend->Size + c->CipherEncrypt->IvSize + r;
|
|
|
|
// Copy the IV
|
|
Copy(dest_data + sizeof(UCHAR) + c->MdSend->Size, c->NextIv, c->CipherEncrypt->IvSize);
|
|
|
|
// Calculate the HMAC
|
|
MdProcess(c->MdSend, dest_data + sizeof(UCHAR), dest_data + sizeof(UCHAR) + c->MdSend->Size,
|
|
dest_size - sizeof(UCHAR) - c->MdSend->Size);
|
|
|
|
// Update the NextIV
|
|
Copy(c->NextIv, dest_data + dest_size - c->CipherEncrypt->IvSize, c->CipherEncrypt->IvSize);
|
|
|
|
// Op-code
|
|
dest_data[0] = uc;
|
|
|
|
OvsSendPacketRawNow(c->Server, c->Session, dest_data, dest_size);
|
|
|
|
Free(encrypted_data);
|
|
}
|
|
|
|
// 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));
|
|
|
|
// OpCode + KeyID
|
|
if (size < 1)
|
|
{
|
|
goto LABEL_ERROR;
|
|
}
|
|
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:
|
|
Debug("OvsParsePacket Error.\n");
|
|
if (ret != NULL)
|
|
{
|
|
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->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);
|
|
|
|
OvsLog(s, se, NULL, "LO_NEW_SESSION", (protocol == OPENVPN_PROTOCOL_UDP ? "UDP" : "TCP"));
|
|
|
|
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);
|
|
IPCProcessL3Events(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)
|
|
{
|
|
IPCProcessL3Events(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",
|
|
(OPENVPN_PING_SEND_INTERVAL / 1000),
|
|
(OPENVPN_RECV_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_tunnel_endpoint[64];
|
|
UINT ip_tunnel_endpoint_32;
|
|
char ip_network[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);
|
|
|
|
// Generate a virtual gateway address to be passed to the OpenVPN
|
|
ip_tunnel_endpoint_32 = Endian32(cao->ClientAddress);
|
|
ip_tunnel_endpoint_32++;
|
|
ip_tunnel_endpoint_32 = Endian32(ip_tunnel_endpoint_32);
|
|
IPToStr32(ip_tunnel_endpoint, sizeof(ip_tunnel_endpoint), ip_tunnel_endpoint_32);
|
|
|
|
// Create a subnet information for the LAN
|
|
IPToStr32(ip_network, sizeof(ip_network),
|
|
GetNetworkAddress(cao->ClientAddress,
|
|
cao->SubnetMask));
|
|
|
|
IPToStr32(ip_subnet_mask, sizeof(ip_subnet_mask),
|
|
cao->SubnetMask);
|
|
|
|
Format(l3_options, sizeof(l3_options),
|
|
",ifconfig %s %s",
|
|
// ",ifconfig %s %s,route %s %s %s 1",
|
|
ip_client, ip_tunnel_endpoint, ip_network, ip_subnet_mask,
|
|
ip_tunnel_endpoint);
|
|
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)
|
|
{
|
|
Format(l3_options, sizeof(l3_options),
|
|
",route-gateway %s,redirect-gateway def1", ip_tunnel_endpoint);
|
|
StrCat(option_str, sizeof(option_str), l3_options);
|
|
|
|
IPToStr32(ip_defgw, sizeof(ip_defgw), cao->Gateway);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
OPENVPN_PACKET *p;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
IPCProcessL3Events(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 + (UINT64)(OPENVPN_PING_SEND_INTERVAL);
|
|
|
|
OvsSendDataPacket(latest_channel, latest_channel->KeyId, ++latest_channel->LastDataPacketId,
|
|
ping_signature, sizeof(ping_signature));
|
|
//Debug(".");
|
|
|
|
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 + (UINT64)OPENVPN_RECV_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);
|
|
OvsLog(s, se, NULL, "LO_DELETE_SESSION");
|
|
|
|
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;
|
|
}
|
|
|
|
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 = CmpIpAddr(&s1->Protocol, &s2->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;
|
|
}
|
|
|
|
// Identify whether the IP address is compatible to the tun device of OpenVPN
|
|
bool OvsIsCompatibleL3IP(UINT ip)
|
|
{
|
|
IP p;
|
|
|
|
UINTToIP(&p, ip);
|
|
if ((p.addr[3] % 4) == 1)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Get an IP address that is compatible to tun device of the OpenVPN after the specified IP address
|
|
UINT OvsGetCompatibleL3IPNext(UINT ip)
|
|
{
|
|
ip = Endian32(ip);
|
|
|
|
while (true)
|
|
{
|
|
if (OvsIsCompatibleL3IP(Endian32(ip)))
|
|
{
|
|
return Endian32(ip);
|
|
}
|
|
|
|
ip++;
|
|
}
|
|
}
|
|
|
|
// Create a new OpenVPN server
|
|
OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOCK_EVENT *sock_event)
|
|
{
|
|
OPENVPN_SERVER *s;
|
|
// Validate arguments
|
|
if (cedar == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
s = ZeroMalloc(sizeof(OPENVPN_SERVER));
|
|
|
|
s->Cedar = cedar;
|
|
|
|
AddRef(s->Cedar->ref);
|
|
|
|
s->Interrupt = interrupt;
|
|
|
|
s->SessionList = NewList(OvsCompareSessionList);
|
|
s->SendPacketList = NewListFast(NULL);
|
|
|
|
s->Now = Tick64();
|
|
|
|
s->NextSessionId = 1;
|
|
|
|
if (sock_event != NULL)
|
|
{
|
|
s->SockEvent = sock_event;
|
|
AddRef(s->SockEvent->ref);
|
|
}
|
|
|
|
OvsLog(s, NULL, NULL, "LO_START");
|
|
|
|
s->Dh = DhNewGroup2();
|
|
|
|
return s;
|
|
}
|
|
|
|
// Release the OpenVPN server
|
|
void FreeOpenVpnServer(OPENVPN_SERVER *s)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (s == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OvsLog(s, NULL, NULL, "LO_STOP");
|
|
|
|
// Release the session 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 packet which is attempting to send
|
|
for (i = 0;i < LIST_NUM(s->SendPacketList);i++)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(s->SendPacketList, i);
|
|
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
ReleaseList(s->SendPacketList);
|
|
|
|
ReleaseCedar(s->Cedar);
|
|
|
|
if (s->SockEvent != NULL)
|
|
{
|
|
ReleaseSockEvent(s->SockEvent);
|
|
}
|
|
|
|
DhFree(s->Dh);
|
|
|
|
Free(s);
|
|
}
|
|
|
|
// UDP reception procedure
|
|
void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list)
|
|
{
|
|
OPENVPN_SERVER_UDP *us;
|
|
UINT64 now = Tick64();
|
|
// Validate arguments
|
|
if (u == NULL || packet_list == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
us = (OPENVPN_SERVER_UDP *)u->Param;
|
|
|
|
if (OvsGetNoOpenVpnUdp())
|
|
{
|
|
// OpenVPN over UDP is disabled
|
|
return;
|
|
}
|
|
|
|
if (us->OpenVpnServer != NULL)
|
|
{
|
|
{
|
|
u->PollMyIpAndPort = false;
|
|
|
|
ClearStr(us->Cedar->OpenVPNPublicPorts, sizeof(us->Cedar->OpenVPNPublicPorts));
|
|
}
|
|
|
|
OvsRecvPacket(us->OpenVpnServer, packet_list, OPENVPN_PROTOCOL_UDP);
|
|
|
|
UdpListenerSendPackets(u, us->OpenVpnServer->SendPacketList);
|
|
DeleteAll(us->OpenVpnServer->SendPacketList);
|
|
}
|
|
}
|
|
|
|
// Create an OpenVPN server (UDP mode)
|
|
OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar)
|
|
{
|
|
OPENVPN_SERVER_UDP *u;
|
|
// Validate arguments
|
|
if (cedar == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
u = ZeroMalloc(sizeof(OPENVPN_SERVER_UDP));
|
|
|
|
u->Cedar = cedar;
|
|
|
|
AddRef(u->Cedar->ref);
|
|
|
|
// Create a UDP listener
|
|
u->UdpListener = NewUdpListener(OpenVpnServerUdpListenerProc, u, &cedar->Server->ListenIP);
|
|
|
|
// Create an OpenVPN server
|
|
u->OpenVpnServer = NewOpenVpnServer(cedar, u->UdpListener->Interrupts, u->UdpListener->Event);
|
|
|
|
return u;
|
|
}
|
|
|
|
// Apply the port list to the OpenVPN server
|
|
void OvsApplyUdpPortList(OPENVPN_SERVER_UDP *u, char *port_list, IP *listen_ip)
|
|
{
|
|
LIST *o;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (u == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DeleteAllPortFromUdpListener(u->UdpListener);
|
|
|
|
if (u->UdpListener != NULL && listen_ip != NULL)
|
|
{
|
|
Copy(&u->UdpListener->ListenIP, listen_ip, sizeof(IP));
|
|
}
|
|
|
|
o = StrToIntList(port_list, true);
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
UINT port = *((UINT *)LIST_DATA(o, i));
|
|
|
|
if (port >= 1 && port <= 65535)
|
|
{
|
|
AddPortToUdpListener(u->UdpListener, port);
|
|
}
|
|
}
|
|
|
|
ReleaseIntList(o);
|
|
}
|
|
|
|
// Release the OpenVPN server (UDP mode)
|
|
void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u)
|
|
{
|
|
// Validate arguments
|
|
if (u == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Stop the UDP listener
|
|
FreeUdpListener(u->UdpListener);
|
|
|
|
// Release the OpenVPN server
|
|
FreeOpenVpnServer(u->OpenVpnServer);
|
|
|
|
ReleaseCedar(u->Cedar);
|
|
|
|
Free(u);
|
|
}
|
|
|
|
// Check whether it's OpenSSL protocol by looking the first receive buffer of the TCP
|
|
bool OvsCheckTcpRecvBufIfOpenVPNProtocol(UCHAR *buf, UINT size)
|
|
{
|
|
if (buf == NULL || size != 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (buf[0] == 0x00 && buf[1] == 0x0E)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Run the OpenVPN server in TCP mode
|
|
bool OvsPerformTcpServer(CEDAR *cedar, SOCK *sock)
|
|
{
|
|
OPENVPN_SERVER *s;
|
|
INTERRUPT_MANAGER *im;
|
|
SOCK_EVENT *se;
|
|
FIFO *tcp_recv_fifo;
|
|
FIFO *tcp_send_fifo;
|
|
UINT buf_size = (128 * 1024);
|
|
UCHAR *buf;
|
|
UINT64 giveup_time = Tick64() + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT;
|
|
LIST *ovs_recv_packet;
|
|
UINT i;
|
|
bool ret = false;
|
|
// Validate arguments
|
|
if (cedar == NULL || sock == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Initialize
|
|
buf = Malloc(buf_size);
|
|
im = NewInterruptManager();
|
|
se = NewSockEvent();
|
|
SetTimeout(sock, TIMEOUT_INFINITE);
|
|
JoinSockToSockEvent(sock, se);
|
|
|
|
tcp_recv_fifo = NewFifoFast();
|
|
tcp_send_fifo = NewFifoFast();
|
|
|
|
ovs_recv_packet = NewListFast(NULL);
|
|
|
|
// Create an OpenVPN server
|
|
s = NewOpenVpnServer(cedar, im, se);
|
|
|
|
// Main loop
|
|
Debug("Entering OpenVPN TCP Server Main Loop.\n");
|
|
while (true)
|
|
{
|
|
UINT next_interval;
|
|
bool disconnected = false;
|
|
UINT64 now = Tick64();
|
|
|
|
// Receive data from a TCP socket
|
|
while (true)
|
|
{
|
|
UINT r = Recv(sock, buf, buf_size, false);
|
|
if (r == SOCK_LATER)
|
|
{
|
|
// Can not read any more
|
|
break;
|
|
}
|
|
else if (r == 0)
|
|
{
|
|
// Disconnected
|
|
disconnected = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Read
|
|
WriteFifo(tcp_recv_fifo, buf, r);
|
|
}
|
|
}
|
|
|
|
// Separate to a list of datagrams by interpreting the data received from the TCP socket
|
|
while (true)
|
|
{
|
|
UINT r = FifoSize(tcp_recv_fifo);
|
|
if (r >= sizeof(USHORT))
|
|
{
|
|
void *ptr = FifoPtr(tcp_recv_fifo);
|
|
USHORT packet_size = READ_USHORT(ptr);
|
|
if (packet_size != 0 && packet_size <= OPENVPN_TCP_MAX_PACKET_SIZE)
|
|
{
|
|
UINT total_len = (UINT)packet_size + sizeof(USHORT);
|
|
if (r >= total_len)
|
|
{
|
|
if (ReadFifo(tcp_recv_fifo, buf, total_len) != total_len)
|
|
{
|
|
// Mismatch
|
|
disconnected = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Read one packet
|
|
UINT payload_len = packet_size;
|
|
UCHAR *payload_ptr = buf + sizeof(USHORT);
|
|
|
|
// Pass the packet to the OpenVPN server
|
|
Add(ovs_recv_packet, NewUdpPacket(&sock->RemoteIP, sock->RemotePort,
|
|
&sock->LocalIP, sock->LocalPort,
|
|
Clone(payload_ptr, payload_len), payload_len));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Non-arrival
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Invalid packet size
|
|
disconnected = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Non-arrival
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Pass a list of received datagrams to the OpenVPN server
|
|
OvsRecvPacket(s, ovs_recv_packet, OPENVPN_PROTOCOL_TCP);
|
|
|
|
// Release the received packet list
|
|
for (i = 0;i < LIST_NUM(ovs_recv_packet);i++)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(ovs_recv_packet, i);
|
|
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
DeleteAll(ovs_recv_packet);
|
|
|
|
// Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server
|
|
for (i = 0;i < LIST_NUM(s->SendPacketList);i++)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(s->SendPacketList, i);
|
|
// Store the size to the TCP send queue first
|
|
USHORT us = (USHORT)p->Size;
|
|
//Debug(" *** TCP SEND %u\n", us);
|
|
us = Endian16(us);
|
|
WriteFifo(tcp_send_fifo, &us, sizeof(USHORT));
|
|
|
|
// Write the data body
|
|
WriteFifo(tcp_send_fifo, p->Data, p->Size);
|
|
|
|
// Packet release
|
|
FreeUdpPacket(p);
|
|
}
|
|
DeleteAll(s->SendPacketList);
|
|
|
|
// Send data to the TCP socket
|
|
while (FifoSize(tcp_send_fifo) >= 1)
|
|
{
|
|
UINT r = Send(sock, FifoPtr(tcp_send_fifo), FifoSize(tcp_send_fifo), false);
|
|
|
|
if (r == SOCK_LATER)
|
|
{
|
|
// Can not write any more
|
|
break;
|
|
}
|
|
else if (r == 0)
|
|
{
|
|
// Disconnected
|
|
disconnected = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Wrote out
|
|
ReadFifo(tcp_send_fifo, NULL, r);
|
|
}
|
|
}
|
|
|
|
if (FifoSize(tcp_send_fifo) > MAX_BUFFERING_PACKET_SIZE)
|
|
{
|
|
s->SupressSendPacket = true;
|
|
}
|
|
else
|
|
{
|
|
s->SupressSendPacket = false;
|
|
}
|
|
|
|
if (s->DisconnectCount >= 1)
|
|
{
|
|
// Session disconnection has occurred on OpenVPN server-side
|
|
disconnected = true;
|
|
}
|
|
|
|
if (giveup_time <= now)
|
|
{
|
|
UINT i;
|
|
UINT num_established_sessions = 0;
|
|
for (i = 0;i < LIST_NUM(s->SessionList);i++)
|
|
{
|
|
OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i);
|
|
|
|
if (se->Established)
|
|
{
|
|
num_established_sessions++;
|
|
}
|
|
}
|
|
|
|
if (num_established_sessions == 0)
|
|
{
|
|
// If the number of sessions is 0 even if wait a certain period of time after the start of server, abort
|
|
disconnected = true;
|
|
}
|
|
}
|
|
|
|
if (disconnected)
|
|
{
|
|
// Error or disconnect occurs
|
|
Debug("Breaking OpenVPN TCP Server Main Loop.\n");
|
|
break;
|
|
}
|
|
|
|
// Wait until the next event occurs
|
|
next_interval = GetNextIntervalForInterrupt(im);
|
|
next_interval = MIN(next_interval, UDPLISTENER_WAIT_INTERVAL);
|
|
WaitSockEvent(se, next_interval);
|
|
}
|
|
|
|
if (s != NULL && s->SessionEstablishedCount != 0)
|
|
{
|
|
ret = true;
|
|
}
|
|
|
|
// Release the OpenVPN server
|
|
FreeOpenVpnServer(s);
|
|
|
|
// Release object
|
|
FreeInterruptManager(im);
|
|
ReleaseSockEvent(se);
|
|
ReleaseFifo(tcp_recv_fifo);
|
|
ReleaseFifo(tcp_send_fifo);
|
|
Free(buf);
|
|
|
|
// Release the received packet list
|
|
for (i = 0;i < LIST_NUM(ovs_recv_packet);i++)
|
|
{
|
|
UDPPACKET *p = LIST_DATA(ovs_recv_packet, i);
|
|
|
|
FreeUdpPacket(p);
|
|
}
|
|
|
|
ReleaseList(ovs_recv_packet);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|